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::adapter_helpers::node_range;
24use brokk_bifrost_core::analyzer::structural::materialization::{
25    GenerationKind, MaterializationRecord,
26};
27use brokk_bifrost_core::analyzer::tree_walk::{
28    NodeKindIds, ParentIndex, WalkControl, children_iter, named_children_iter,
29    push_children_reversed, push_named_children_reversed, walk_named_tree_preorder,
30};
31use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
32use brokk_bifrost_core::hash::{HashMap, HashSet};
33use regex::Regex;
34use tree_sitter::{Node, Parser, Tree};
35
36/// Intern one qualified-name segment in the process-global interner.
37fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
38    segment_interner().intern(text, kind)
39}
40
41/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
42/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
43/// `::` head is exactly the mixed-separator store issue #1163 is about; the
44/// structured form records each namespace component, and the equivalence check
45/// renders it natively (with `::` between adjacent Package segments) so it
46/// round-trips to the legacy string. Splitting the already-joined string here is
47/// the M1 bridge — the legacy strings stay authoritative until M3.
48fn cpp_push_package(fq: &mut FqName, package_name: &str) {
49    for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
50        fq.push(cpp_segment(component, SegmentKind::Package));
51    }
52}
53
54/// C++ namespace paths are stored `::`-joined in `package_name` (issue #1163).
55const CPP_PACKAGE_SEPARATOR: &str = "::";
56
57/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
58/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
59/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
60/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
61/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
62/// cpp-specific native rendering rule is needed for this chain.
63fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
64    let mut first = true;
65    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
66    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
67    // entry point, not re-inference of an already-structured name.
68    for component in chain.split('$').filter(|c| !c.is_empty()) {
69        let kind = if first {
70            SegmentKind::Type
71        } else {
72            SegmentKind::Nested
73        };
74        fq.push(cpp_segment(component, kind));
75        first = false;
76    }
77}
78
79/// Structured name for a C++ namespace module: every `::`-separated component is
80/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
81/// `short_name` with an empty `package_name`).
82fn cpp_namespace_fq(full_name: &str) -> FqName {
83    let mut fq = FqName::new();
84    cpp_push_package(&mut fq, full_name);
85    fq
86}
87
88/// The per-level namespace components a `namespace_definition`'s `name` field
89/// declares.
90///
91/// A C++17 nested definition (`namespace a::b::c`) parses as a
92/// `nested_namespace_specifier` whose named children are the per-level
93/// `namespace_identifier`s plus, for three or more levels, a further
94/// `nested_namespace_specifier`; the `::` separators, the optional per-level
95/// `inline`, and the leading global `::` are all anonymous tokens the walk
96/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
97/// segment path as the expanded `namespace a { namespace b { } }` form.
98///
99/// A shape outside that grammar is the deliberately ill-formed source the
100/// diagnostic corpora carry. Those keep their historical single-component
101/// reading of the raw name text, which the caller still joins to the lexical
102/// namespace exactly as before.
103fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
104    let mut components = Vec::new();
105    let mut stack = vec![node];
106    while let Some(current) = stack.pop() {
107        match current.kind() {
108            "namespace_identifier" | "identifier" => {
109                components.push(normalize_cpp_whitespace(node_text(current, source)));
110            }
111            "nested_namespace_specifier" => {
112                for index in (0..current.named_child_count()).rev() {
113                    stack.push(
114                        current
115                            .named_child(index)
116                            .expect("index below the node's own named child count"),
117                    );
118                }
119            }
120            _ => return cpp_raw_namespace_name_components(node, source),
121        }
122    }
123    if components.iter().any(String::is_empty) {
124        return cpp_raw_namespace_name_components(node, source);
125    }
126    components
127}
128
129/// The historical reading of a namespace name node: its whole source text as
130/// one component, with a leading global `::` marker dropped so the caller's
131/// global-scope handling stays the AST boundary rather than a text prefix.
132///
133/// This is the recovery path for source outside the C++ grammar, so the text it
134/// returns can carry a separator that names nothing. react-native-windows
135/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
136/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
137/// trailing separator with no tail. The caller stores this component in
138/// `short_name` and derives the fq by splitting it back apart, so an empty tail
139/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
140/// here, at the one place the malformed text enters, rather than leaving each
141/// consumer to guard (#2353, a variant of #1878).
142fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
143    let start = node
144        .child(0)
145        .filter(|child| !child.is_named() && child.kind() == "::")
146        .map_or(node.start_byte(), |marker| marker.end_byte());
147    let text = normalize_cpp_whitespace(
148        source
149            .get(start..node.end_byte())
150            .expect("namespace name node covers one source range"),
151    );
152    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
153    if text.is_empty() {
154        return Vec::new();
155    }
156    vec![text]
157}
158
159/// Return the named namespace path that structurally encloses `node`.
160///
161/// This intentionally follows namespace AST ancestors rather than inspecting
162/// source text. Anonymous namespaces are not representable in the legacy C++
163/// package field, so a path containing one fails closed.
164fn cpp_lexical_namespace_name<'tree>(
165    node: Node<'tree>,
166    source: &str,
167    ancestry: &ParentIndex<'tree>,
168) -> Option<String> {
169    let mut components = Vec::new();
170    let mut ancestor = ancestry.parent(node);
171    while let Some(current) = ancestor {
172        if current.kind() == "namespace_definition" {
173            let name_node = current.child_by_field_name("name")?;
174            let name = normalize_cpp_whitespace(node_text(name_node, source));
175            if name.is_empty() {
176                return None;
177            }
178            components.push(name);
179        }
180        ancestor = ancestry.parent(current);
181    }
182    if components.is_empty() {
183        return None;
184    }
185    components.reverse();
186    // Same shared empty-component decision as `cpp_push_package`, which splits
187    // this string back apart: an enclosing namespace whose own name node is
188    // malformed contributes a component carrying its own separator (#2353).
189    Some(
190        normalize_joined(
191            &components.join(CPP_PACKAGE_SEPARATOR),
192            CPP_PACKAGE_SEPARATOR,
193        )
194        .into_owned(),
195    )
196}
197
198/// Nested-class `$` join for short names. An anonymous parent class (empty
199/// short_name) contributes no segment: the FqName bridge drops empty
200/// components, so a bare `parent$child` join would desync `short_name` from
201/// the fq and trip the package/short boundary assert in
202/// `CodeUnit::with_signature_and_fq` (#2140).
203fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
204    if parent_short.is_empty() {
205        name.to_string()
206    } else {
207        format!("{parent_short}${name}")
208    }
209}
210
211/// Member `.` join for short names; same anonymous-parent guard as
212/// [`cpp_join_nested_short`] (#2140).
213fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
214    if parent_short.is_empty() {
215        name.to_string()
216    } else {
217        format!("{parent_short}.{name}")
218    }
219}
220
221/// Structural fq for a leaf declaration: the parent unit's fq plus this
222/// declaration's own name as one segment (or the package segments plus the
223/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
224/// chain, so a literal `$` inside a source identifier (Cython template
225/// substitution points, gcc `$`-identifiers) survives instead of corrupting
226/// the chain and tripping the package/short boundary assert (#2140).
227fn cpp_leaf_fq(
228    package_name: &str,
229    parent: Option<&CodeUnit>,
230    name: &str,
231    kind_if_nested: SegmentKind,
232    kind_if_top: SegmentKind,
233) -> FqName {
234    if let Some(parent) = parent {
235        parent
236            .fq()
237            .clone()
238            .with_pushed(cpp_segment(name, kind_if_nested))
239    } else {
240        let mut fq = FqName::new();
241        cpp_push_package(&mut fq, package_name);
242        fq.push(cpp_segment(name, kind_if_top));
243        fq
244    }
245}
246
247/// Structured name for a member unit (function, field, enumerator). The
248/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
249/// the member has an owner, by `.member`; free functions and globals have no
250/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
251/// C++ member names never contain a literal `.`, so the single `.` (if any)
252/// separates the owner chain from the member.
253pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
254    let mut fq = FqName::new();
255    cpp_push_package(&mut fq, package_name);
256    match short_name.rsplit_once('.') {
257        Some((owner_chain, member)) => {
258            cpp_push_type_chain(&mut fq, owner_chain);
259            fq.push(cpp_segment(member, SegmentKind::Member));
260        }
261        None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
262    }
263    fq
264}
265
266#[derive(Clone)]
267pub struct ScopeInfo {
268    package_name: String,
269    module: Option<CodeUnit>,
270    class_unit: Option<CodeUnit>,
271    template_signature: Option<String>,
272    template_metadata: Option<CppTemplateMetadata>,
273    declarations_are_fields: bool,
274    recovered_specialization_member_scope: bool,
275    /// Namespace targets of every `using namespace X;` directive lexically
276    /// visible at this point in the file (declaration order), threaded
277    /// forward sibling-by-sibling by the sequential container walk (see
278    /// `CppWork::Siblings`). An out-of-line member definition written as a
279    /// bare `Class::method` at file/namespace scope with no enclosing
280    /// `namespace {}` block (issue #1093, e.g. log4cxx's
281    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
282    /// const { ... }`) has no other structural signal for which namespace
283    /// actually owns `Class`; this is the best-effort candidate list used to
284    /// recover it so the definition's indexed identity matches its header
285    /// declaration's.
286    visible_using_namespaces: Vec<String>,
287}
288
289struct CppContainer<'tree> {
290    node: Node<'tree>,
291    scope: ScopeInfo,
292}
293
294struct CppNodeWork<'tree> {
295    node: Node<'tree>,
296    scope: ScopeInfo,
297}
298
299/// Cursor over one container's remaining named children, processed one at a
300/// time (rather than all at once) so a `using namespace X;` sibling can
301/// update `scope.visible_using_namespaces` for the siblings that follow it,
302/// matching real C++ using-directive semantics. Nested container work is
303/// still pushed and fully drained before the cursor resumes (stack LIFO
304/// order), preserving the original left-to-right visitation order.
305struct CppSiblingsWork<'tree> {
306    children: std::vec::IntoIter<Node<'tree>>,
307    scope: ScopeInfo,
308}
309
310enum CppWork<'tree> {
311    Container(CppContainer<'tree>),
312    Node(CppNodeWork<'tree>),
313    Siblings(CppSiblingsWork<'tree>),
314}
315
316fn class_like_name<'tree>(
317    node: Node<'tree>,
318    source: &str,
319    ancestry: &ParentIndex<'tree>,
320) -> Option<String> {
321    let best = class_like_name_from_children(node, source);
322    if let Some(parent) = ancestry.parent(node)
323        && matches!(
324            parent.kind(),
325            "declaration" | "field_declaration" | "function_definition"
326        )
327        // A class_specifier carrying its own body proves the grammar name is
328        // the real class name: a sibling declarator then declares an object
329        // (`class X {} x;`), never a displaced class name. The gate matters
330        // when the class name is itself an all-caps token (`X`, `API`) --
331        // without it the export-macro re-read below steals the object
332        // declarator's name for the class (#2283). The genuine export-macro
333        // shapes leave the class_specifier bodyless, the same invariant
334        // recover_malformed_exported_multiple_base_class already gates on.
335        && cpp_body_node(node).is_none()
336        && node
337            .child_by_field_name("name")
338            .map(|name_node| {
339                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
340            })
341            .unwrap_or(false)
342        && let Some(recovered) = exported_class_name_from_node(parent, source)
343        && best.as_deref() != Some(recovered.as_str())
344    {
345        return Some(recovered);
346    }
347    best.or_else(|| {
348        node.child_by_field_name("name")
349            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
350            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
351    })
352}
353
354fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
355    let mut grammar_name = None;
356    if let Some(name_node) = node.child_by_field_name("name") {
357        let name = normalize_cpp_whitespace(node_text(name_node, source));
358        if name.is_empty() {
359            return None;
360        }
361        if !cpp_export_macro_token(&name) {
362            return Some(name);
363        }
364        grammar_name = Some(name);
365    }
366
367    let mut best = None;
368    let mut cursor = node.walk();
369    let mut stack = Vec::new();
370    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
371        if matches!(
372            child.kind(),
373            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
374        ) {
375            break;
376        }
377        stack.push(child);
378    }
379
380    while let Some(current) = stack.pop() {
381        if matches!(current.kind(), "type_identifier" | "identifier") {
382            let name = normalize_cpp_whitespace(node_text(current, source));
383            if !name.is_empty() && !cpp_export_macro_token(&name) {
384                best = Some(name);
385            }
386            continue;
387        }
388
389        push_named_children_reversed(current, &mut stack);
390    }
391    best.or(grammar_name)
392}
393
394pub fn cpp_export_macro_token(token: &str) -> bool {
395    token
396        .chars()
397        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
398}
399
400struct RecoveredExportedClass<'tree> {
401    declaration_node: Node<'tree>,
402    name: String,
403    body: Option<Node<'tree>>,
404    raw_supertypes: Option<Vec<String>>,
405    uses_initializer_body: bool,
406    /// Present only for the fragmented multiple-base export shape (issue #938).
407    /// Carries the true class-body byte region -- the members tree-sitter scattered
408    /// out of the recovered node -- so they can be reparsed and re-owned as members
409    /// rather than lost inside the truncated `initializer_list` stand-in.
410    fragmented_body: Option<FragmentedExportBody>,
411}
412
413struct RecoveredFunctionLikeExportClassPair {
414    name: String,
415    range: Range,
416    raw_supertypes: Option<Vec<String>>,
417    fragmented_body: FragmentedExportBody,
418}
419
420struct RecoveredEmbeddedFunctionLikeExportClass {
421    name: String,
422    range: Range,
423    raw_supertypes: Vec<String>,
424    fragmented_body: FragmentedExportBody,
425}
426
427/// The recovered class-body geometry for a fragmented multiple-base export class.
428/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
429/// verbatim for a region reparse (issue #941 machinery) so every recovered member
430/// keeps its exact original byte/line position. `class_range` is the full class
431/// navigation range spanning to the displaced closing brace.
432struct FragmentedExportBody {
433    reparse_start: usize,
434    reparse_end: usize,
435    class_range: Range,
436}
437
438fn recovered_fragmented_export_body(
439    body: Node<'_>,
440    class_range: Range,
441) -> Option<FragmentedExportBody> {
442    let open = body.child(0).filter(|child| child.kind() == "{")?;
443    let close = body
444        .child(body.child_count().saturating_sub(1))
445        .filter(|child| child.kind() == "}" && !child.is_missing());
446    Some(FragmentedExportBody {
447        reparse_start: open.end_byte(),
448        // A zero-width missing `}` contributes no source byte. Keep the whole
449        // body range in that case; subtracting one byte would discard the last
450        // member's semicolon and make the otherwise valid region unsafe to
451        // index. A real close token is excluded by its structured start.
452        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
453        class_range,
454    })
455}
456
457struct DisplacedFragmentNamespaceBoundary<'tree> {
458    class_close: Node<'tree>,
459    class_semicolon: Node<'tree>,
460    namespace_items: Vec<Node<'tree>>,
461}
462
463/// Result of validating a reparsed fragmented class body.  A complete tree can
464/// safely consume the whole region.  A partial tree may contain only the exact
465/// class-named constructor that tree-sitter merged into an access label; its
466/// remaining siblings must stay on the ordinary outer walk.
467enum FragmentedExportMembers {
468    Complete(Tree),
469    ConditionalConstructor(Tree),
470}
471
472#[derive(Clone, Copy)]
473struct DisplacedMacroClassTail {
474    split_index: usize,
475    class_range: Range,
476}
477
478fn recover_exported_class_declaration<'tree>(
479    node: Node<'tree>,
480    source: &str,
481) -> Option<RecoveredExportedClass<'tree>> {
482    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
483        return Some(recovered);
484    }
485
486    let class_node = first_class_like_child(node)?;
487    if let Some(name_node) = class_node.child_by_field_name("name") {
488        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
489        if cpp_export_macro_token(&class_name) {
490            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
491            // Name declarator. Only a bare declarator can be the displaced class name;
492            // wrappers describe an object whose type merely happens to look macro-like.
493            let mut cursor = node.walk();
494            if node
495                .children_by_field_name("declarator", &mut cursor)
496                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
497            {
498                return None;
499            }
500        } else if has_direct_cpp_declarator(node) {
501            return None;
502        }
503    }
504    let name = exported_class_name_from_node(class_node, source)?;
505    Some(RecoveredExportedClass {
506        declaration_node: class_node,
507        name,
508        body: cpp_body_node(class_node),
509        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
510            .then(|| extract_cpp_supertypes(class_node, source)),
511        uses_initializer_body: false,
512        fragmented_body: None,
513    })
514}
515
516fn recover_malformed_exported_base_class<'tree>(
517    node: Node<'tree>,
518    source: &str,
519) -> Option<RecoveredExportedClass<'tree>> {
520    if node.kind() != "declaration" {
521        return None;
522    }
523    let class_node = node.child_by_field_name("type")?;
524    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
525        return None;
526    }
527    let macro_name = class_node
528        .child_by_field_name("name")
529        .and_then(|name| direct_identifier_name(name, source))?;
530    if !cpp_export_macro_token(&macro_name) {
531        return None;
532    }
533
534    let mut named_cursor = node.walk();
535    let mut named = node.named_children(&mut named_cursor);
536    if named
537        .next()
538        .is_none_or(|child| !same_node(child, class_node))
539    {
540        return None;
541    }
542    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
543    if displaced.kind() != "ERROR" {
544        return None;
545    }
546    let name = displaced_exported_class_name(displaced, source)?;
547
548    let remaining = named.collect::<Vec<_>>();
549    let init = *remaining.last()?;
550    if init.kind() != "init_declarator" {
551        return None;
552    }
553    let final_base = init
554        .child_by_field_name("declarator")
555        .and_then(|base| recovered_malformed_base_name(base, source))?;
556    let body = init.child_by_field_name("value")?;
557    // A complete reduction has a real closing brace here. In Chromium's Widget
558    // declaration, tree-sitter instead emits the same direct `}` slot as a
559    // zero-width missing node where the first body macro truncates the prefix.
560    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
561        return None;
562    }
563
564    if remaining[..remaining.len() - 1]
565        .iter()
566        .any(|child| match child.kind() {
567            "qualified_identifier"
568            | "scoped_type_identifier"
569            | "type_identifier"
570            | "identifier" => false,
571            "ERROR" => !is_malformed_inheritance_access(*child, source),
572            _ => true,
573        })
574    {
575        return None;
576    }
577
578    let mut raw_supertypes = Vec::new();
579    for base in &remaining[..remaining.len() - 1] {
580        if base.kind() == "ERROR" {
581            continue;
582        }
583        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
584    }
585    raw_supertypes.push(final_base);
586
587    Some(RecoveredExportedClass {
588        declaration_node: node,
589        name,
590        body: Some(body),
591        raw_supertypes: Some(raw_supertypes),
592        uses_initializer_body: true,
593        fragmented_body: fragmented_export_body_region(node, body, source),
594    })
595}
596
597/// Locate the true class-body region for a fragmented multiple-base export class.
598///
599/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
600/// emits in place of the real class body. Tree-sitter reduces that body in one of
601/// two shapes, both of which lose the members from the recovered node:
602///
603/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
604///   a real closing brace and holds the whole body text inline. The interior between
605///   the braces reparses to the members directly.
606/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
607///   first member with a zero-width MISSING `}`; every later member -- and the real
608///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
609///   siblings. The interior runs from the opening brace to that displaced `}`.
610///
611/// Returns the interior byte range to reparse plus the full class navigation range.
612fn fragmented_export_body_region(
613    node: Node<'_>,
614    body: Node<'_>,
615    source: &str,
616) -> Option<FragmentedExportBody> {
617    let reparse_start = body.start_byte() + 1;
618    let close = direct_close_brace(body)?;
619    if close.end_byte() > close.start_byte() {
620        return Some(FragmentedExportBody {
621            reparse_start,
622            reparse_end: close.start_byte(),
623            class_range: cpp_declaration_range(node),
624        });
625    }
626    // The closing brace was displaced past the recovered node. A balanced nested
627    // class keeps its own braces, so the first lone-`}` sibling is this class's.
628    let mut sibling = node.next_named_sibling();
629    let displaced_close = loop {
630        let Some(current) = sibling else {
631            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
632        };
633        if cpp_is_stray_close_brace(current, source) {
634            break current;
635        }
636        sibling = current.next_named_sibling();
637    };
638    Some(FragmentedExportBody {
639        reparse_start,
640        reparse_end: displaced_close.start_byte(),
641        class_range: Range {
642            start_byte: node.start_byte(),
643            end_byte: displaced_close.end_byte(),
644            start_line: node.start_position().row + 1,
645            end_line: displaced_close.end_position().row + 1,
646        },
647    })
648}
649
650/// Locate the true class-body region for the export-macro class shape that
651/// tree-sitter promotes to a `function_definition`.
652///
653/// In this shape the synthetic function body closes at the first inline
654/// method, while the class's real members continue as root-level siblings until
655/// a stray `}` followed by the displaced class `;`. Reparse the complete
656/// interior so those siblings are visited with the recovered class scope.
657fn fragmented_export_function_body_region(
658    node: Node<'_>,
659    body: Node<'_>,
660    source: &str,
661    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
662) -> Option<FragmentedExportBody> {
663    let reparse_start = body.start_byte().checked_add(1)?;
664    if let Some(boundary) = displaced_namespace {
665        return Some(FragmentedExportBody {
666            reparse_start,
667            reparse_end: boundary.class_close.start_byte(),
668            class_range: Range {
669                start_byte: node.start_byte(),
670                end_byte: boundary.class_semicolon.end_byte(),
671                start_line: node.start_position().row + 1,
672                end_line: boundary.class_semicolon.end_position().row + 1,
673            },
674        });
675    }
676    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
677    let boundary =
678        fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
679    let boundary_index = boundary.and_then(|boundary| {
680        siblings
681            .iter()
682            .position(|candidate| same_node(*candidate, boundary))
683    });
684    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
685    let mut sibling_index = 0;
686    // A complete recovered class's synthetic wrapper is immediately followed
687    // by its displaced semicolon (comments and a trailing attribute macro --
688    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
689    // may sit between the body and that semicolon). Only scan for a later
690    // stray close when real member siblings intervene; otherwise every earlier
691    // complete class would borrow the next malformed class's close and claim
692    // its members. The trailing-attribute case is the gtest shape: the scan
693    // borrowed a close ~1900 lines later and re-owned a following
694    // `namespace testing { namespace internal {` block as class members,
695    // doubling the package path ("testing::internal::testing::internal") and
696    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
697    // boundary assert (#2297).
698    while let Some(current) = siblings.get(sibling_index).copied() {
699        if current.kind() == "comment" {
700            sibling_index += 1;
701            continue;
702        }
703        if is_trailing_attribute_macro_sibling(current) {
704            sibling_index += 1;
705            continue;
706        }
707        if cpp_is_stray_semicolon(current, source) {
708            return None;
709        }
710        break;
711    }
712    while let Some(current) = siblings.get(sibling_index).copied() {
713        let next = siblings.get(sibling_index + 1).copied();
714        if cpp_is_stray_close_brace(current, source)
715            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
716        {
717            let semicolon = next.expect("checked above");
718            return Some(FragmentedExportBody {
719                reparse_start,
720                reparse_end: current.start_byte(),
721                class_range: Range {
722                    start_byte: node.start_byte(),
723                    end_byte: semicolon.end_byte(),
724                    start_line: node.start_position().row + 1,
725                    end_line: semicolon.end_position().row + 1,
726                },
727            });
728        }
729        // When the final access label keeps the class close in its malformed
730        // declaration body, tree-sitter nests the lone `}` ERROR below the
731        // label instead of exposing it as a direct sibling. Search only the
732        // scattered siblings after the synthetic wrapper. The first such
733        // close is the class terminator because nested class bodies retain
734        // their own balanced class_specifier nodes.
735        if current.start_byte() >= body.end_byte()
736            && let Some(close) = cpp_nested_stray_close_brace(current, source)
737        {
738            return Some(FragmentedExportBody {
739                reparse_start,
740                reparse_end: close.start_byte(),
741                class_range: Range {
742                    start_byte: node.start_byte(),
743                    end_byte: current.end_byte(),
744                    start_line: node.start_position().row + 1,
745                    end_line: current.end_position().row + 1,
746                },
747            });
748        }
749        sibling_index += 1;
750    }
751    boundary.map(|boundary| FragmentedExportBody {
752        reparse_start,
753        reparse_end: boundary.start_byte(),
754        class_range: Range {
755            start_byte: node.start_byte(),
756            end_byte: boundary.start_byte(),
757            start_line: node.start_position().row + 1,
758            end_line: boundary.start_position().row + 1,
759        },
760    })
761}
762
763/// Find a later macro-export class that tree-sitter lifted through an enclosing
764/// preprocessor container. A class that is still a direct sibling can be a
765/// nested member of the current fragmented class, so only a changed parent is
766/// a proven boundary between the two recovered class envelopes.
767fn fragmented_export_sibling_class_boundary<'tree>(
768    node: Node<'tree>,
769    source: &str,
770    ancestry: &ParentIndex<'tree>,
771) -> Option<Node<'tree>> {
772    let node_parent = ancestry.parent(node)?;
773    cpp_following_named_siblings(node, source, ancestry)
774        .into_iter()
775        .find(|candidate| {
776            recover_exported_class_function_definition(*candidate, source).is_some()
777                && ancestry
778                    .parent(*candidate)
779                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
780        })
781}
782
783/// A trailing attribute macro after a recovered class's closing brace, spelled
784/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
785/// bare identifier is never a class member (members need a type), so this
786/// sibling can only be the class's own tail (#2297).
787fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
788    if node.kind() != "expression_statement" {
789        return false;
790    }
791    let mut cursor = node.walk();
792    let mut children = node.named_children(&mut cursor);
793    children
794        .next()
795        .is_some_and(|child| child.kind() == "identifier")
796        && children.next().is_none()
797}
798
799/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
800/// export-class wrapper can place the class close inside an access-label node,
801/// so direct-sibling checks alone miss the boundary.  Walk named CST children
802/// only; the helper does not inspect source text beyond the existing structured
803/// stray-brace predicate.
804fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
805    let mut stack = vec![node];
806    while let Some(current) = stack.pop() {
807        if cpp_is_stray_close_brace(current, source) {
808            return Some(current);
809        }
810        let mut cursor = current.walk();
811        stack.extend(current.named_children(&mut cursor));
812    }
813    None
814}
815
816/// Return named siblings that follow `node`, including siblings that tree-sitter
817/// attached to an enclosing container after malformed recovery split the local
818/// declaration list. Stop at the first structurally visible class close so a
819/// later namespace or exported class cannot supply the recovery boundary.
820///
821/// The climb takes a [`ParentIndex`] because it runs once per visited node and
822/// walks every level to the root: with `Node::parent`, which re-descends from
823/// the root on each call, a translation unit holding thousands of siblings --
824/// generated instruction tables, for instance -- made this the whole cost of
825/// indexing the file. Callers inside the declaration walk pass the walk's
826/// index; the four that ask from outside a traversal pass
827/// [`ParentIndex::unindexed`], which is `Node::parent` itself, so their
828/// behaviour is unchanged.
829fn cpp_following_named_siblings<'tree>(
830    node: Node<'tree>,
831    source: &str,
832    ancestry: &ParentIndex<'tree>,
833) -> Vec<Node<'tree>> {
834    let mut siblings = Vec::new();
835    let mut anchor = node;
836    while let Some(parent) = ancestry.parent(anchor) {
837        let at_translation_unit = parent.kind() == "translation_unit";
838        let mut cursor = parent.walk();
839        assert!(
840            cursor.goto_first_child(),
841            "ParentIndex returned a parent without children"
842        );
843        while !same_node(cursor.node(), anchor) {
844            assert!(
845                cursor.goto_next_sibling(),
846                "ParentIndex parent does not contain its child anchor"
847            );
848        }
849        while cursor.goto_next_sibling() {
850            let current = cursor.node();
851            if !current.is_named() {
852                continue;
853            }
854            if at_translation_unit
855                && (current.kind() == "namespace_definition"
856                    || (current.kind() == "function_definition"
857                        && first_class_like_child(current).is_some()))
858            {
859                return siblings;
860            }
861            siblings.push(current);
862            if cpp_is_stray_close_brace(current, source) {
863                if let Some(semicolon) = current
864                    .next_named_sibling()
865                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
866                {
867                    siblings.push(semicolon);
868                }
869                return siblings;
870            }
871            if current.start_byte() >= node.end_byte()
872                && matches!(current.kind(), "ERROR" | "labeled_statement")
873                && cpp_nested_stray_close_brace(current, source).is_some()
874            {
875                return siblings;
876            }
877        }
878        anchor = parent;
879    }
880    siblings
881}
882
883fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
884    if node.start_byte() >= class_end {
885        return false;
886    }
887    node.end_byte() <= class_end
888        || cpp_nested_stray_close_brace(node, source)
889            .is_some_and(|close| close.start_byte() == class_end)
890}
891
892/// A class whose head tree-sitter kept in one malformed container while the
893/// members after the first and the closing `};` scattered across that
894/// container's following siblings.
895struct FragmentedClassRecovery<'tree> {
896    declaration_node: Node<'tree>,
897    name: String,
898    raw_supertypes: Vec<String>,
899    body: FragmentedExportBody,
900}
901
902/// Recover a class whose opening prefix is retained in one ERROR node while one
903/// or more nested class closes and the outer close are displaced to sibling
904/// `}`/`;` nodes. All boundaries come from tree-sitter nodes: the direct class
905/// tokens establish nesting depth and the displaced close nodes terminate it.
906///
907/// Two head spellings reach the ERROR arm. A plain `class Name ... {` keeps the
908/// `class` keyword as the container's first token. A function-like export macro
909/// (`class BOTAN_PUBLIC_API(2, 0) Name : public virtual Base {`) is reduced to a
910/// body-less `class_specifier` named after the macro followed by the
911/// invocation's `(`, arguments and `)`, with the real class name and the base
912/// clause left as bare tokens after it (#2924).
913fn fragmented_class_body<'tree>(
914    node: Node<'tree>,
915    source: &str,
916) -> Option<FragmentedClassRecovery<'tree>> {
917    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
918        return Some(recovered);
919    }
920    let supported_container = node.kind() == "ERROR"
921        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
922    if !supported_container {
923        return None;
924    }
925    let mut cursor = node.walk();
926    let children = node.children(&mut cursor).collect::<Vec<_>>();
927    if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
928        return Some(recovered);
929    }
930    let keyword = children.first()?;
931    if !matches!(keyword.kind(), "class" | "struct" | "union") {
932        return None;
933    }
934    let name_node = children
935        .iter()
936        .copied()
937        .skip(1)
938        .find(|child| child.is_named())?;
939    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
940        return None;
941    }
942    let name = normalize_cpp_whitespace(node_text(name_node, source));
943    if name.is_empty() || cpp_export_macro_token(&name) {
944        return None;
945    }
946    let open_index = children.iter().position(|child| child.kind() == "{")?;
947    Some(FragmentedClassRecovery {
948        declaration_node: node,
949        name,
950        raw_supertypes: extract_cpp_supertypes(node, source),
951        body: fragmented_displaced_class_body(node, &children, open_index, source)?,
952    })
953}
954
955/// The fragmented body of a function-like export-macro class head that
956/// tree-sitter kept in one declaration-scope `ERROR` together with the body's
957/// opening `{` (Botan's `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey :
958/// public virtual EC_PublicKey {`, #2924).
959///
960/// The class name is read positionally by [`recovered_export_head_name`] -- the
961/// last identifier before the head ends at `final`, at the base clause `:`, or
962/// at the body `{` -- so no macro spelling is interpreted, and a base clause
963/// spelled with `virtual`, with `final`, or with qualified base names needs no
964/// arm of its own.
965fn fragmented_export_macro_class_body<'tree>(
966    node: Node<'tree>,
967    children: &[Node<'tree>],
968    source: &str,
969) -> Option<FragmentedClassRecovery<'tree>> {
970    let class_node = *children.first()?;
971    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
972        return None;
973    }
974    // The identifier tree-sitter took for the class name is the export macro, a
975    // function-like invocation when the `(` of its argument list follows it
976    // directly. Spelling plays no part (#2557).
977    class_node
978        .child_by_field_name("name")
979        .and_then(|name| direct_identifier_name(name, source))?;
980    let invocation = *children.get(1)?;
981    if invocation.is_named() || invocation.kind() != "(" {
982        return None;
983    }
984    let open_index = children
985        .iter()
986        .position(|child| !child.is_named() && child.kind() == "{")?;
987    let open = children[open_index];
988    let name_node = recovered_export_head_name(node, open, source)?;
989    let name = normalize_cpp_whitespace(node_text(name_node, source));
990    if name.is_empty() || cpp_export_macro_token(&name) {
991        return None;
992    }
993    Some(FragmentedClassRecovery {
994        declaration_node: node,
995        name,
996        raw_supertypes: recovered_export_head_bases(
997            node,
998            name_node.end_byte(),
999            open.start_byte(),
1000            source,
1001        ),
1002        body: fragmented_displaced_class_body(node, children, open_index, source)
1003            .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1004    })
1005}
1006
1007/// The class body a fragmented head opened at `children[open_index]` but could
1008/// not close: tree-sitter scattered the remaining members and the closing `};`
1009/// across the container's following siblings. The direct class tokens after the
1010/// opening brace say how many closes the nested classes consume before this
1011/// class's own, and the displaced `}`/`;` sibling pair terminates it.
1012fn fragmented_displaced_class_body(
1013    node: Node<'_>,
1014    children: &[Node<'_>],
1015    open_index: usize,
1016    source: &str,
1017) -> Option<FragmentedExportBody> {
1018    let open = children[open_index];
1019    let nested_class_opens = children[open_index + 1..]
1020        .iter()
1021        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1022        .count();
1023    let mut closes_remaining = 1 + nested_class_opens;
1024    let mut sibling = node.next_named_sibling();
1025    while let Some(candidate) = sibling {
1026        let next = candidate.next_named_sibling();
1027        if cpp_is_stray_close_brace(candidate, source) {
1028            closes_remaining -= 1;
1029            if closes_remaining == 0 {
1030                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
1031                if open.end_byte() >= candidate.start_byte() {
1032                    return None;
1033                }
1034                return Some(FragmentedExportBody {
1035                    reparse_start: open.end_byte(),
1036                    reparse_end: candidate.start_byte(),
1037                    class_range: Range {
1038                        start_byte: node.start_byte(),
1039                        end_byte: semicolon.end_byte(),
1040                        start_line: node.start_position().row + 1,
1041                        end_line: semicolon.end_position().row + 1,
1042                    },
1043                });
1044            }
1045        }
1046        sibling = next;
1047    }
1048    None
1049}
1050
1051/// The body of a fragmented export-macro class head whose close tree-sitter
1052/// spent on the enclosing container instead: Botan's
1053/// `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey : public virtual
1054/// EC_PublicKey {` leaves every member as a `declaration_list` sibling and ends
1055/// `namespace Botan` on the class's own brace, so nothing is displaced to a
1056/// sibling `}` and [`fragmented_displaced_class_body`] finds no terminator
1057/// (#2924).
1058fn fragmented_container_close_class_body(
1059    node: Node<'_>,
1060    open: Node<'_>,
1061    source: &str,
1062) -> Option<FragmentedExportBody> {
1063    let parent = node.parent()?;
1064    if !matches!(
1065        parent.kind(),
1066        "declaration_list" | "field_declaration_list" | "compound_statement"
1067    ) {
1068        return None;
1069    }
1070    let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1071    // The container's close is this class's close only when the body's own
1072    // brace balance says so. Without that cross-check -- the one issue #1524
1073    // already uses on a mis-closed wrapper body -- a class that really does end
1074    // earlier would claim every later container-level declaration as a member.
1075    if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1076        || open.end_byte() >= close.start_byte()
1077    {
1078        return None;
1079    }
1080    Some(FragmentedExportBody {
1081        reparse_start: open.end_byte(),
1082        reparse_end: close.start_byte(),
1083        class_range: Range {
1084            start_byte: node.start_byte(),
1085            end_byte: close.end_byte(),
1086            start_line: node.start_position().row + 1,
1087            end_line: close.end_position().row + 1,
1088        },
1089    })
1090}
1091
1092pub(crate) fn recovered_fragmented_class_has_body(
1093    node: Node<'_>,
1094    source: &str,
1095    expected_name: &str,
1096    expected_range: &Range,
1097) -> bool {
1098    fragmented_class_body(node, source).is_some_and(|recovered| {
1099        recovered.name == expected_name
1100            && recovered.body.class_range.start_byte == expected_range.start_byte
1101            && recovered.body.class_range.end_byte == expected_range.end_byte
1102    })
1103}
1104
1105/// Recover a plain class whose parser-visible body ends inside a malformed
1106/// inline member. Tree-sitter then attaches either the next real member
1107/// declarator or the unfinished `else` branch directly to the outer function
1108/// definition and leaves the class's actual `};` among later siblings. Those
1109/// structured continuations and the close/semicolon siblings establish the
1110/// complete body envelope without interpreting source text.
1111fn fragmented_plain_class_declaration_body<'tree>(
1112    node: Node<'tree>,
1113    source: &str,
1114) -> Option<FragmentedClassRecovery<'tree>> {
1115    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1116        return None;
1117    }
1118    let class_node = node.child_by_field_name("type")?;
1119    if !matches!(
1120        class_node.kind(),
1121        "class_specifier" | "struct_specifier" | "union_specifier"
1122    ) {
1123        return None;
1124    }
1125    let name_node = class_node.child_by_field_name("name")?;
1126    let name = normalize_cpp_whitespace(node_text(name_node, source));
1127    if name.is_empty() || cpp_export_macro_token(&name) {
1128        return None;
1129    }
1130    let body = cpp_body_node(class_node)?;
1131    if body.kind() != "field_declaration_list" {
1132        return None;
1133    }
1134    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1135        if declarator.start_byte() < class_node.end_byte() {
1136            return None;
1137        }
1138        let mut cursor = node.walk();
1139        node.named_children(&mut cursor).any(|child| {
1140            if child.kind() != "ERROR"
1141                || child.start_byte() < class_node.end_byte()
1142                || child.end_byte() > declarator.start_byte()
1143            {
1144                return false;
1145            }
1146            let mut cursor = child.walk();
1147            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1148            let Some((return_type, attributes)) = components.split_last() else {
1149                return false;
1150            };
1151            matches!(
1152                return_type.kind(),
1153                "identifier"
1154                    | "type_identifier"
1155                    | "primitive_type"
1156                    | "decltype"
1157                    | "placeholder_type_specifier"
1158            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1159                && attributes.iter().all(|attribute| {
1160                    matches!(attribute.kind(), "identifier" | "type_identifier")
1161                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1162                            *attribute, source,
1163                        )))
1164                })
1165        })
1166    } else {
1167        let mut cursor = node.walk();
1168        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1169        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1170            if same_node(*candidate_class, class_node)
1171                && continuation.kind() == "identifier"
1172                && node_text(*continuation, source) == "else"
1173                && continuation_body.kind() == "compound_statement"
1174                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1175                && continuation_body
1176                    .child(continuation_body.child_count().saturating_sub(1))
1177                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1178    };
1179    if !displaced_member {
1180        return None;
1181    }
1182    let open = body
1183        .children(&mut body.walk())
1184        .find(|child| child.kind() == "{")?;
1185    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1186    let ordinary_boundary =
1187        siblings
1188            .iter()
1189            .copied()
1190            .enumerate()
1191            .find_map(|(close_index, close)| {
1192                cpp_is_stray_close_brace(close, source)
1193                    .then(|| {
1194                        siblings
1195                            .get(close_index + 1)
1196                            .copied()
1197                            .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1198                            .map(|semicolon| (close, semicolon))
1199                    })
1200                    .flatten()
1201            });
1202    let (close, semicolon) =
1203        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1204            (boundary.class_close, boundary.class_semicolon)
1205        } else {
1206            ordinary_boundary?
1207        };
1208    if open.end_byte() >= close.start_byte() {
1209        return None;
1210    }
1211    Some(FragmentedClassRecovery {
1212        declaration_node: class_node,
1213        name,
1214        raw_supertypes: extract_cpp_supertypes(class_node, source),
1215        body: FragmentedExportBody {
1216            reparse_start: open.end_byte(),
1217            reparse_end: close.start_byte(),
1218            class_range: Range {
1219                start_byte: class_node.start_byte(),
1220                end_byte: semicolon.end_byte(),
1221                start_line: class_node.start_position().row + 1,
1222                end_line: semicolon.end_position().row + 1,
1223            },
1224        },
1225    })
1226}
1227
1228fn displaced_export_function_namespace_shape<'tree>(
1229    declaration: Node<'tree>,
1230    source: &str,
1231) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1232    let mut nested = Vec::new();
1233    for index in (0..declaration.named_child_count()).rev() {
1234        nested.push(declaration.named_child(index)?);
1235    }
1236    while let Some(current) = nested.pop() {
1237        // A recovered export class nested in this class can consume the first
1238        // parser-visible namespace close itself. In that shape the existing
1239        // later-class boundary logic already distinguishes the nested and
1240        // namespace-sibling owners; do not mistake the nested close for this
1241        // class's terminator.
1242        if recover_exported_class_function_definition(current, source).is_some() {
1243            return None;
1244        }
1245        for index in (0..current.named_child_count()).rev() {
1246            nested.push(current.named_child(index)?);
1247        }
1248    }
1249    let mut same_envelope_sibling = declaration.next_named_sibling();
1250    while let Some(current) = same_envelope_sibling {
1251        if recover_exported_class_function_definition(current, source).is_some() {
1252            return None;
1253        }
1254        same_envelope_sibling = current.next_named_sibling();
1255    }
1256    let declaration_list = declaration.parent()?;
1257    if declaration_list.kind() != "declaration_list" {
1258        return None;
1259    }
1260    let namespace = declaration_list.parent()?;
1261    if namespace.kind() != "namespace_definition"
1262        || namespace.child_by_field_name("body") != Some(declaration_list)
1263    {
1264        return None;
1265    }
1266    let class_close = direct_close_brace(declaration_list)?;
1267    let trailing_semicolon = namespace.next_named_sibling()?;
1268    if trailing_semicolon.kind() != "expression_statement"
1269        || trailing_semicolon.named_child_count() != 0
1270    {
1271        return None;
1272    }
1273    // A chain of malformed export classes can consume one parser-visible
1274    // namespace close per class. Walk through the enclosing sibling levels so
1275    // the later real namespace close remains the structural boundary; a
1276    // direct next-sibling walk stops at the first collapsed namespace and
1277    // incorrectly makes its intervening items members of this class.
1278    let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1279    let trailing_index = siblings
1280        .iter()
1281        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1282    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1283        recover_exported_class_function_definition(*candidate, source).is_some()
1284    }) {
1285        // Consecutive recovered classes already have an exact sibling-class
1286        // boundary. Preserve that established path, including nested export
1287        // classes, instead of interpreting the first class close as a
1288        // collapsed namespace boundary.
1289        return None;
1290    }
1291    let mut namespace_items = Vec::new();
1292    let mut nested_fragment_end = 0;
1293    for current in siblings.into_iter().skip(trailing_index + 1) {
1294        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1295        {
1296            return Some(DisplacedFragmentNamespaceBoundary {
1297                class_close,
1298                class_semicolon: trailing_semicolon,
1299                namespace_items,
1300            });
1301        }
1302        if current.start_byte() >= nested_fragment_end
1303            && let Some(recovered) = fragmented_class_body(current, source)
1304        {
1305            nested_fragment_end = recovered.body.class_range.end_byte;
1306        } else if current.start_byte() >= nested_fragment_end
1307            && recover_exported_class_function_definition(current, source).is_some()
1308            && let Some(body) = cpp_body_node(current)
1309            && let Some(fragmented) =
1310                fragmented_export_function_body_region(current, body, source, None)
1311        {
1312            nested_fragment_end = fragmented.class_range.end_byte;
1313        }
1314        namespace_items.push(current);
1315    }
1316    None
1317}
1318
1319fn displaced_fragment_namespace_boundary<'tree>(
1320    declaration: Node<'tree>,
1321    body: Node<'tree>,
1322    source: &str,
1323) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1324    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1325    let reparse_start = body.start_byte() + 1;
1326    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1327    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1328}
1329
1330/// Recover the class/namespace brace geometry for a declaration whose class
1331/// close tree-sitter consumed as the enclosing namespace close. This proof is
1332/// independent of whether every member in the class body can be reparsed: the
1333/// ordinary-tree fallback can still re-own bounded sibling declarations when
1334/// an unknown macro makes the complete body reparse unsafe.
1335fn displaced_fragment_namespace_geometry<'tree>(
1336    declaration: Node<'tree>,
1337    source: &str,
1338) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1339    // A templated class's malformed function wrapper remains beneath the
1340    // template node even though its later members have escaped to the
1341    // enclosing declaration list. Lift only that exact declaration child.
1342    let envelope = declaration
1343        .parent()
1344        .filter(|parent| {
1345            parent.kind() == "template_declaration"
1346                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1347        })
1348        .unwrap_or(declaration);
1349    let declaration_list = envelope.parent()?;
1350    if declaration_list.kind() != "declaration_list" {
1351        return None;
1352    }
1353    let namespace = declaration_list.parent()?;
1354    if namespace.kind() != "namespace_definition"
1355        || namespace.child_by_field_name("body") != Some(declaration_list)
1356    {
1357        return None;
1358    }
1359    let class_close = direct_close_brace(declaration_list)?;
1360    let trailing_semicolon = namespace.next_named_sibling()?;
1361    if trailing_semicolon.kind() != "expression_statement"
1362        || trailing_semicolon.named_child_count() != 0
1363    {
1364        return None;
1365    }
1366    let mut namespace_items = Vec::new();
1367    let mut sibling = trailing_semicolon.next_named_sibling();
1368    let mut nested_fragment_end = 0;
1369    loop {
1370        let current = sibling?;
1371        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1372        {
1373            break;
1374        }
1375        if current.start_byte() >= nested_fragment_end
1376            && let Some(recovered) = fragmented_class_body(current, source)
1377        {
1378            nested_fragment_end = recovered.body.class_range.end_byte;
1379        }
1380        namespace_items.push(current);
1381        sibling = current.next_named_sibling();
1382    }
1383    Some(DisplacedFragmentNamespaceBoundary {
1384        class_close,
1385        class_semicolon: trailing_semicolon,
1386        namespace_items,
1387    })
1388}
1389
1390/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1391fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1392    (0..node.child_count())
1393        .filter_map(|index| node.child(index))
1394        .find(|child| !child.is_named() && child.kind() == "}")
1395}
1396
1397/// A displaced lone closing brace: the class close that the fragmented multiple-base
1398/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1399fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1400    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1401}
1402
1403/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1404/// text while skipping line/block comments and string/char literals. The
1405/// exported-class recovery needs this when tree-sitter's bogus
1406/// `function_definition` body runs past the class's true closing brace and
1407/// swallows following siblings (issue #1524): the grammar tree carries no
1408/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1409/// close is located textually. Returns `None` when the text is unbalanced or
1410/// contains a construct the scanner deliberately does not interpret (raw
1411/// strings) -- callers treat that as "cannot partition" and keep the
1412/// un-split recovery.
1413fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1414    let bytes = source.as_bytes();
1415    if bytes.get(open_byte) != Some(&b'{') {
1416        return None;
1417    }
1418    let mut depth = 0usize;
1419    let mut i = open_byte;
1420    while i < bytes.len() {
1421        match bytes[i] {
1422            b'{' => depth += 1,
1423            b'}' => {
1424                depth = depth.checked_sub(1)?;
1425                if depth == 0 {
1426                    return Some(i);
1427                }
1428            }
1429            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1430                while i < bytes.len() && bytes[i] != b'\n' {
1431                    i += 1;
1432                }
1433                continue;
1434            }
1435            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1436                i += 2;
1437                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1438                    i += 1;
1439                }
1440                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1441                continue;
1442            }
1443            quote @ (b'"' | b'\'') => {
1444                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1445                // bail out rather than mis-count.
1446                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1447                    return None;
1448                }
1449                i += 1;
1450                while i < bytes.len() && bytes[i] != quote {
1451                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1452                }
1453                if i >= bytes.len() {
1454                    return None;
1455                }
1456            }
1457            _ => {}
1458        }
1459        i += 1;
1460    }
1461    None
1462}
1463
1464fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1465    let mut name = None;
1466    let mut colon_count = 0;
1467    let mut access_count = 0;
1468    for index in 0..node.child_count() {
1469        let child = node.child(index)?;
1470        match child.kind() {
1471            "identifier" | "type_identifier" if child.is_named() => {
1472                if name.is_some() {
1473                    return None;
1474                }
1475                let candidate = normalize_cpp_whitespace(node_text(child, source));
1476                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1477                    return None;
1478                }
1479                name = Some(candidate);
1480            }
1481            "template_function" | "template_type" if child.is_named() => {
1482                if name.is_some() {
1483                    return None;
1484                }
1485                let candidate = child
1486                    .child_by_field_name("name")
1487                    .and_then(|name| direct_identifier_name(name, source))?;
1488                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1489                    return None;
1490                }
1491                name = Some(candidate);
1492            }
1493            ":" if !child.is_named() => colon_count += 1,
1494            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1495            _ => return None,
1496        }
1497    }
1498    (colon_count == 1 && access_count == 1)
1499        .then_some(name)
1500        .flatten()
1501}
1502
1503fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1504    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1505        return false;
1506    }
1507    node.named_child(0)
1508        .and_then(|child| direct_identifier_name(child, source))
1509        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1510}
1511
1512fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1513    (0..node.child_count()).any(|index| {
1514        node.child(index)
1515            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1516    })
1517}
1518
1519fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1520    match node.kind() {
1521        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1522            recovered_base_atom(node, source)
1523        }
1524        "template_type" | "template_function" => node
1525            .child_by_field_name("name")
1526            .and_then(|name| recovered_malformed_base_name(name, source)),
1527        "ERROR" => None,
1528        "qualified_identifier" | "scoped_type_identifier" => {
1529            let suffix = node
1530                .child_by_field_name("name")
1531                .and_then(|name| recovered_malformed_base_name(name, source))?;
1532            let scope = node
1533                .child_by_field_name("scope")
1534                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1535            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1536                malformed_qualified_prefix(node, source)?
1537            } else {
1538                if malformed_qualified_prefix(node, source).is_some() {
1539                    return None;
1540                }
1541                scope
1542            };
1543            Some(format!("{prefix}::{suffix}"))
1544        }
1545        _ => None,
1546    }
1547}
1548
1549fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1550    if !matches!(
1551        node.kind(),
1552        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1553    ) {
1554        return None;
1555    }
1556    let name = normalize_cpp_whitespace(node_text(node, source));
1557    (!name.is_empty()).then_some(name)
1558}
1559
1560fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1561    let mut prefix = None;
1562    let mut cursor = node.walk();
1563    for error in node
1564        .named_children(&mut cursor)
1565        .filter(|child| child.kind() == "ERROR")
1566    {
1567        if prefix.is_some() {
1568            return None;
1569        }
1570        // `public virtual Botan::EC_PublicKey` reduces the access specifier to
1571        // the qualified name's scope and leaves `virtual` beside the real
1572        // qualifier inside the `ERROR`. `virtual` is a base specifier, never a
1573        // component of the base's name (#2924).
1574        let mut error_cursor = error.walk();
1575        let atoms = error
1576            .named_children(&mut error_cursor)
1577            .map(|child| recovered_base_atom(child, source))
1578            .collect::<Option<Vec<_>>>()?;
1579        let [atom] = atoms
1580            .iter()
1581            .filter(|atom| atom.as_str() != "virtual")
1582            .collect::<Vec<_>>()[..]
1583        else {
1584            return None;
1585        };
1586        prefix = Some(atom.clone());
1587    }
1588    prefix
1589}
1590
1591/// One declaration an attribute-like macro invocation swallowed into a
1592/// declaration-scope `ERROR`, with the byte range that spells it.
1593/// What [`stranded_declaration_run`] read out of one node.
1594struct StrandedRun<'tree> {
1595    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1596    /// Whether every part of the node read as part of a declaration. False when
1597    /// a part the reader does not understand ended it early, or when the last
1598    /// parts were types with no declarator after them. Callers that index what
1599    /// was found keep the declarations either way; a caller deciding whether a
1600    /// whole region is safe to index requires this.
1601    complete: bool,
1602}
1603
1604struct MacroWrappedDeclaration<'tree> {
1605    declarator: Node<'tree>,
1606    range: Range,
1607    /// Whether the recovered declaration spells `static`. The envelope hides
1608    /// that keyword from the ordinary linkage reader, which looks for it among
1609    /// a declaration node's own children, and internal linkage is what decides
1610    /// whether a header declaration and a body in another file are one symbol.
1611    is_static: bool,
1612}
1613
1614/// Whether `node` stands where declarations live: directly in the translation
1615/// unit, in a `namespace` or `extern "C"` body, or in an `ERROR` that itself
1616/// stands in one of those.
1617///
1618/// An `ERROR` is a grouping failure, never a scope, so what it holds stands
1619/// where it stands. The case that matters is a whole translation unit the
1620/// parser could not close: whisper.cpp's `include/whisper.h` opens
1621/// `extern "C" {` inside `#ifdef __cplusplus` and closes it inside another one,
1622/// so the braces never balance in the token stream tree-sitter sees, the file's
1623/// own root node is an `ERROR`, and every top-level declaration in the file is
1624/// a child of it (#3094). Without this the macro recoveries below read nothing
1625/// in such a file, because none of their envelopes is in a declaration scope
1626/// the parser managed to build.
1627fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1628    declaration_scope_container(node, ancestry).is_some()
1629}
1630
1631/// The container `node` declares in, for [`is_declaration_scope_position`]. Its
1632/// end is where a parse failure that starts at `node` can still be doing
1633/// damage: a declaration scope has no synchronization point of its own, so the
1634/// parser carries such a failure to the scope's close.
1635fn declaration_scope_container<'tree>(
1636    node: Node<'tree>,
1637    ancestry: &ParentIndex<'tree>,
1638) -> Option<Node<'tree>> {
1639    let mut parent = ancestry.parent(node)?;
1640    loop {
1641        match parent.kind() {
1642            "translation_unit" => return Some(parent),
1643            "declaration_list" => {
1644                return ancestry
1645                    .parent(parent)
1646                    .is_some_and(|grandparent| {
1647                        matches!(
1648                            grandparent.kind(),
1649                            "namespace_definition" | "linkage_specification"
1650                        )
1651                    })
1652                    .then_some(parent);
1653            }
1654            "ERROR" => match ancestry.parent(parent) {
1655                Some(grandparent) => parent = grandparent,
1656                // A root `ERROR` is the translation unit the parser could not
1657                // build, so its children stand at file scope.
1658                None => return Some(parent),
1659            },
1660            _ => return None,
1661        }
1662    }
1663}
1664
1665/// Whether `node` is an `ERROR` the parser produced where declarations live.
1666fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1667    node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
1668}
1669
1670/// Whether `node` can only be part of what precedes a declarator -- a type, a
1671/// specifier, or a word of the attribute macro's own text that the lexer left
1672/// as a bare identifier -- so a run of these followed by a declarator is one
1673/// declaration the parser failed to group.
1674fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1675    matches!(
1676        node.kind(),
1677        "identifier"
1678            | "type_identifier"
1679            | "primitive_type"
1680            | "sized_type_specifier"
1681            | "struct_specifier"
1682            | "union_specifier"
1683            | "enum_specifier"
1684            | "type_qualifier"
1685            | "storage_class_specifier"
1686            | "explicit_function_specifier"
1687            | "virtual_function_specifier"
1688            | "qualified_identifier"
1689            | "template_type"
1690            | "dependent_type"
1691            | "placeholder_type_specifier"
1692    )
1693}
1694
1695/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
1696/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
1697/// words the lexer reports as bare identifiers. Anything else in such an
1698/// `ERROR` stops the recovery, so a macro argument that carries structure this
1699/// recovery does not understand is never guessed at.
1700fn is_macro_argument_error(node: Node<'_>) -> bool {
1701    if node.kind() != "ERROR" {
1702        return false;
1703    }
1704    let mut cursor = node.walk();
1705    node.named_children(&mut cursor).all(|child| {
1706        matches!(
1707            child.kind(),
1708            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1709        )
1710    })
1711}
1712
1713/// The end of a recovered declaration: the declarator's end, extended across
1714/// the `;` the grammar left beside it inside the envelope.
1715///
1716/// The envelope's own end is the hard boundary. The parser hands the last
1717/// declaration's `;` to the sibling statement it recovered with, outside the
1718/// envelope, and a range that reached it would no longer lie inside one node --
1719/// which is how every reader (including the resolver's climb from a range to
1720/// the node that declares it) finds a recovered declaration again.
1721fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1722    declarator
1723        .next_sibling()
1724        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1725        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1726}
1727
1728/// The declarations `node` holds after an attribute-like macro cost the parser
1729/// their grouping, in source order.
1730///
1731/// The parser packs the parts into whichever slots it has left. In whisper's
1732/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
1733/// field takes the export macro, the real return type and the *next*
1734/// declaration's declarator both end up inside one sibling `ERROR`, and the
1735/// `declarator` field takes whatever declaration the recovery reached last. In
1736/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
1737/// arrive as bare identifiers and the attributed member and the member after it
1738/// share one declarator node.
1739///
1740/// Both are the same failure, so both get the same reading: flatten the node's
1741/// parts -- splicing each nested `ERROR`'s own children in place, since an
1742/// `ERROR` here is only a grouping failure -- and read the flat run as what it
1743/// spells, a run of type-and-specifier tokens followed by a declarator, over
1744/// and over.
1745///
1746/// Fails closed. A part that is neither a declarator nor something that can
1747/// only precede one ends the recovery there, and the declarations before it are
1748/// kept.
1749fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1750    let mut parts = Vec::new();
1751    let mut cursor = node.walk();
1752    for child in node.named_children(&mut cursor) {
1753        if child.kind() == "ERROR" {
1754            let mut error_cursor = child.walk();
1755            parts.extend(child.named_children(&mut error_cursor));
1756        } else {
1757            parts.push(child);
1758        }
1759    }
1760
1761    let mut declarations = Vec::new();
1762    let mut start = None;
1763    let mut is_static = false;
1764    let mut complete = true;
1765    for part in parts {
1766        if part.kind() == "comment" {
1767            continue;
1768        }
1769        if let Some(declarator) = extract_function_declarator(part) {
1770            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1771            declarations.push(MacroWrappedDeclaration {
1772                declarator,
1773                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1774                is_static,
1775            });
1776            is_static = false;
1777            continue;
1778        }
1779        if !is_recovered_declaration_type_part(part) {
1780            complete = false;
1781            break;
1782        }
1783        is_static |= part.kind() == "storage_class_specifier"
1784            && normalize_cpp_whitespace(node_text(part, source)) == "static";
1785        start.get_or_insert(part.start_byte());
1786    }
1787    StrandedRun {
1788        declarations,
1789        complete: complete && start.is_none(),
1790    }
1791}
1792
1793/// The declarations an attribute-like macro invocation swallowed into a
1794/// declaration-scope `ERROR`, in source order.
1795///
1796/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
1797/// declaration in a macro call:
1798///
1799/// ```text
1800/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
1801///                  struct llama_model * model,
1802///           struct llama_context_params   params),
1803///         "use llama_init_from_model instead");
1804/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
1805/// ```
1806///
1807/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
1808/// holding the macro name, the wrapped declaration as a
1809/// `parameter_declaration`, the hint string as another `ERROR`, and then every
1810/// following declaration as a further `parameter_declaration` until it
1811/// recovers. That is what removed `llama_tokenize` from the index and left the
1812/// seven-argument call in `talk-llama.cpp` with only that file's own
1813/// three-parameter `static` overload to choose from (#2552, and the
1814/// `LLAMA_API` half of #2551).
1815///
1816/// Every part of every swallowed declaration is still a real node; only their
1817/// grouping is lost. This rebuilds the grouping and reads the nodes.
1818fn macro_wrapped_declarations<'tree>(
1819    envelope: Node<'tree>,
1820    source: &str,
1821    ancestry: &ParentIndex<'tree>,
1822) -> Vec<MacroWrappedDeclaration<'tree>> {
1823    let mut declarations = Vec::new();
1824    if !is_declaration_scope_error(envelope, ancestry) {
1825        return declarations;
1826    }
1827    let mut cursor = envelope.walk();
1828    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1829    let [macro_name, arguments @ ..] = children.as_slice() else {
1830        return declarations;
1831    };
1832    if macro_name.kind() != "identifier" {
1833        return declarations;
1834    }
1835    let mut wrapped_declaration_seen = false;
1836    for argument in arguments {
1837        match argument.kind() {
1838            "comment" => {}
1839            "parameter_declaration" => {
1840                let recovered = stranded_declaration_run(*argument, source).declarations;
1841                if recovered.is_empty() {
1842                    break;
1843                }
1844                wrapped_declaration_seen = true;
1845                declarations.extend(recovered);
1846            }
1847            // The hint string, and only that: an argument the recovery cannot
1848            // read as a declaration is admitted before the wrapped declaration
1849            // is found, so a macro whose first argument is not a declaration
1850            // recovers nothing.
1851            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1852            _ => break,
1853        }
1854    }
1855    declarations
1856}
1857
1858/// What one macro invocation swallowed when it collapsed a whole run of
1859/// declarations.
1860struct CollapsedMacroDeclarationRun {
1861    /// The byte just past the `;` that closes the invocation, which is where
1862    /// the declarations it swallowed begin.
1863    invocation_end: usize,
1864    /// The byte this recovery owns to: the close of the enclosing declaration
1865    /// scope when the invocation swallowed the declarations written after it,
1866    /// and the invocation's own end when the parser instead spread the
1867    /// invocation itself across the container's children and swallowed nothing.
1868    region_end: usize,
1869}
1870
1871/// The tokens one collapsed macro invocation spans, in source order: the leaves
1872/// of `node`, then the leaves of the siblings written after it. Comments and
1873/// the parser's own MISSING tokens are skipped; neither is in the source the
1874/// invocation is written in.
1875///
1876/// The parser puts the parts of a failed invocation wherever it has room. It
1877/// keeps them in one `ERROR`; it splits them across an `ERROR` and the
1878/// `expression_statement` it recovered with; it spreads them flat over the
1879/// container's children; it packs the tail of the file into one of the
1880/// arguments. The grouping therefore says nothing, but the token order still
1881/// spells the invocation, which is what this reads.
1882struct MacroInvocationTokens<'tree> {
1883    stack: Vec<Node<'tree>>,
1884    /// The last sibling handed to `stack`, and the point the next one is asked
1885    /// for. Held as the node rather than as its already-computed successor so
1886    /// the successor is asked for only when the walk actually runs off the end
1887    /// of what it has: `Node::next_sibling` recovers the parent first, which
1888    /// re-descends from the root, so it costs the node's position in the tree.
1889    /// Nearly every node this iterator is built for is rejected on its first
1890    /// token, before any sibling is needed, and computing one eagerly made that
1891    /// rejection cost a walk of the whole container (capstone's generated
1892    /// instruction tables, t19).
1893    frontier: Option<Node<'tree>>,
1894}
1895
1896impl<'tree> MacroInvocationTokens<'tree> {
1897    fn new(node: Node<'tree>) -> Self {
1898        Self {
1899            stack: vec![node],
1900            frontier: Some(node),
1901        }
1902    }
1903}
1904
1905impl<'tree> Iterator for MacroInvocationTokens<'tree> {
1906    type Item = Node<'tree>;
1907
1908    fn next(&mut self) -> Option<Node<'tree>> {
1909        loop {
1910            let Some(node) = self.stack.pop() else {
1911                let sibling = self.frontier?.next_sibling()?;
1912                self.frontier = Some(sibling);
1913                self.stack.push(sibling);
1914                continue;
1915            };
1916            if node.child_count() == 0 {
1917                if node.kind() == "comment" || node.is_missing() {
1918                    continue;
1919                }
1920                return Some(node);
1921            }
1922            let mut cursor = node.walk();
1923            let children = node.children(&mut cursor).collect::<Vec<_>>();
1924            self.stack.extend(children.into_iter().rev());
1925        }
1926    }
1927}
1928
1929/// The macro invocation that begins at `node` when the parser could not group
1930/// it, or `None` when `node` does not begin one.
1931///
1932/// whisper.cpp's bundled `llama.h` writes
1933///
1934/// ```text
1935/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
1936///                          const char * path_model,
1937///           struct llama_model_params   params),
1938///         "use llama_model_load_from_file instead");
1939/// ```
1940///
1941/// and its own `include/whisper.h` writes
1942///
1943/// ```text
1944/// WHISPER_DEPRECATED(
1945///     WHISPER_API struct whisper_context * whisper_init_from_file(const char * path_model),
1946///     "use whisper_init_from_file_with_params instead"
1947/// );
1948/// ```
1949///
1950/// tree-sitter cannot know either name is a macro, so it reads the name and its
1951/// `(` as a function declarator whose close it never finds, and carries that
1952/// failure forward over the declarations written after it. In `llama.h` that
1953/// puts 57 KB, from line 481 to line 1535, into one `function_definition`
1954/// (#2551). In `whisper.h` it instead flattens the invocations over the
1955/// container's children -- a bare `identifier`, `(`, `parameter_declaration`,
1956/// `,` and an `ERROR` holding the hint, over and over -- and then packs lines
1957/// 232 to 456 into one `parameter_declaration` (#3094). The envelope is a
1958/// `function_definition` when a brace block falls in a swallowed tail, because
1959/// the parser borrows it for the body the bogus definition needs, and an
1960/// `ERROR` or a bare token when none does. None of that says anything about the
1961/// construct, so the node's kind is not the criterion; the token order is.
1962///
1963/// The invocation ends at the `)` that closes its own `(`, immediately followed
1964/// by `;`. That is unambiguous: a `)` the lexer left inside the hint text is
1965/// balanced by the `(` beside it, and a macro argument list carries no `;` of
1966/// its own, so a `;` reached before the close means this is some other
1967/// construct -- a macro-defined function body, say -- and the ordinary readers
1968/// keep the node.
1969///
1970/// Fails closed everywhere else too. The head must be an export-macro-shaped
1971/// token followed by `(`, the node must stand where declarations live, and an
1972/// invocation the ordinary [`macro_wrapped_declarations`] reader already has --
1973/// one the parser left whole in a declaration-scope `ERROR` and that swallowed
1974/// nothing past it -- is left to that reader.
1975fn collapsed_macro_declaration_run<'tree>(
1976    node: Node<'tree>,
1977    source: &str,
1978    ancestry: &ParentIndex<'tree>,
1979) -> Option<CollapsedMacroDeclarationRun> {
1980    let container_end = declaration_scope_container(node, ancestry)?.end_byte();
1981    let mut tokens = MacroInvocationTokens::new(node);
1982    let name = tokens.next()?;
1983    if !matches!(name.kind(), "identifier" | "type_identifier")
1984        || !cpp_export_macro_token(node_text(name, source))
1985    {
1986        return None;
1987    }
1988    if tokens.next()?.kind() != "(" {
1989        return None;
1990    }
1991    let mut depth = 1usize;
1992    let invocation_end = loop {
1993        let token = tokens.next()?;
1994        match token.kind() {
1995            "(" => depth += 1,
1996            ";" => return None,
1997            ")" => {
1998                depth -= 1;
1999                if depth == 0 {
2000                    let semicolon = tokens.next()?;
2001                    if semicolon.kind() != ";" {
2002                        return None;
2003                    }
2004                    break semicolon.end_byte();
2005                }
2006            }
2007            _ => {}
2008        }
2009    };
2010    // An invocation that reaches the end of its own declaration scope swallowed
2011    // nothing: there is nothing after it left to recover, and the ordinary
2012    // readers have the invocation itself. This is also what stops the scan
2013    // below from re-entering itself, since the region it reparses for one
2014    // invocation ends exactly at that invocation's `;`.
2015    if invocation_end >= container_end {
2016        return None;
2017    }
2018    // Where the damage ends. A node that reaches past its own invocation packed
2019    // real declarations into the invocation's argument list, so the parser was
2020    // still failing when it got there and everything to the close of the scope
2021    // is suspect -- in `whisper.h` the collapse takes the `{` of the enum on
2022    // line 455 with it, and every declaration from there to the end of the file
2023    // is shredded. A node that stops at or before the invocation's `;` holds
2024    // nothing but the invocation, whose own parts the parser spread over the
2025    // siblings after it.
2026    let region_end = if node.end_byte() > invocation_end {
2027        container_end
2028    } else {
2029        invocation_end
2030    };
2031    Some(CollapsedMacroDeclarationRun {
2032        invocation_end,
2033        region_end,
2034    })
2035}
2036
2037/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
2038/// ordinary class body, and the members it swallowed.
2039///
2040/// Botan deprecates members that way:
2041///
2042/// ```text
2043/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
2044/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
2045/// ```
2046///
2047/// The parser reads the macro name as the member's type and its argument list
2048/// as a parenthesized declarator, which then swallows the attributed member
2049/// *and* the member written after it: a `field_declaration` whose `type` is a
2050/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
2051/// opening with an `ERROR` whose first part is a bare identifier -- the first
2052/// word of the string, which the lexer could not keep together.
2053///
2054/// The macro's spelling is not the criterion; that structural shape is. The
2055/// returned declarations are in source order, the first being the attributed
2056/// member and the rest the members the declarator swallowed after it.
2057fn string_attribute_macro_member_declarators<'tree>(
2058    field: Node<'tree>,
2059    source: &str,
2060) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2061    if field.kind() != "field_declaration"
2062        || field
2063            .child_by_field_name("type")
2064            .is_none_or(|type_node| type_node.kind() != "type_identifier")
2065    {
2066        return None;
2067    }
2068    let declarator = field.child_by_field_name("declarator")?;
2069    if declarator.kind() != "parenthesized_declarator" {
2070        return None;
2071    }
2072    let opening = declarator.named_child(0)?;
2073    if opening.kind() != "ERROR"
2074        || opening
2075            .named_child(0)
2076            .is_none_or(|word| word.kind() != "identifier")
2077    {
2078        return None;
2079    }
2080    let declarations = stranded_declaration_run(declarator, source).declarations;
2081    (!declarations.is_empty()).then_some(declarations)
2082}
2083
2084/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
2085/// export-macro class body leaves in front of the members that macro decorated.
2086///
2087/// The reparse reads the class body as statements, so the attribute becomes a
2088/// call statement of its own -- with the `;` the grammar had to invent -- and
2089/// the members after it are stranded in the `ERROR` that follows.
2090fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2091    let Some(call) = (node.kind() == "expression_statement")
2092        .then(|| node.named_child(0))
2093        .flatten()
2094        .filter(|child| child.kind() == "call_expression")
2095    else {
2096        return false;
2097    };
2098    call.child_by_field_name("function")
2099        .is_some_and(|function| function.kind() == "identifier")
2100        && call
2101            .child_by_field_name("arguments")
2102            .is_some_and(|arguments| {
2103                let mut cursor = arguments.walk();
2104                arguments.named_child_count() > 0
2105                    && arguments
2106                        .named_children(&mut cursor)
2107                        .all(|argument| argument.kind() == "string_literal")
2108            })
2109}
2110
2111/// The start byte of the call the region reparse left where an access-labeled
2112/// constructor was written.
2113///
2114/// A constructor is a member only inside a class body. The export-macro class
2115/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
2116/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
2117/// comma-expression under `private:`, with no declarator left in the tree. It
2118/// is still spelled in the source, though, starting at the one call under the
2119/// label whose callee is the class's own name. Botan's private six-parameter
2120/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
2121/// call is an ambiguity this declines, and so is a reparse from that byte that
2122/// does not yield exactly one constructor declarator there.
2123fn cpp_access_label_constructor_call_start(
2124    node: Node<'_>,
2125    class_name: &str,
2126    source: &str,
2127) -> Option<usize> {
2128    if node.kind() != "labeled_statement" {
2129        return None;
2130    }
2131    let label = node.named_child(0)?;
2132    if label.kind() != "statement_identifier"
2133        || !matches!(
2134            node_text(label, source).trim(),
2135            "public" | "private" | "protected"
2136        )
2137    {
2138        return None;
2139    }
2140    let mut starts = Vec::new();
2141    let mut stack = vec![node];
2142    while let Some(current) = stack.pop() {
2143        if current.kind() == "call_expression"
2144            && current
2145                .child_by_field_name("function")
2146                .is_some_and(|function| {
2147                    function.kind() == "identifier"
2148                        && node_text(function, source).trim() == class_name
2149                })
2150        {
2151            starts.push(current.start_byte());
2152        }
2153        let mut cursor = current.walk();
2154        stack.extend(current.named_children(&mut cursor));
2155    }
2156    let [start] = starts.as_slice() else {
2157        return None;
2158    };
2159    Some(*start)
2160}
2161
2162/// The `function_definition` that gives `declarator` a body, following only the
2163/// declarator chain, so a recovered callable knows whether it is a declaration
2164/// or a definition without anyone having to say.
2165fn cpp_declarator_function_definition<'tree>(
2166    declarator: Node<'tree>,
2167    ancestry: &ParentIndex<'tree>,
2168) -> Option<Node<'tree>> {
2169    let mut current = declarator;
2170    while let Some(parent) = ancestry.parent(current) {
2171        match parent.kind() {
2172            "function_definition" if parent.child_by_field_name("body").is_some() => {
2173                return Some(parent);
2174            }
2175            "pointer_declarator"
2176            | "reference_declarator"
2177            | "parenthesized_declarator"
2178            | "array_declarator" => current = parent,
2179            _ => return None,
2180        }
2181    }
2182    None
2183}
2184
2185/// Whether `node` lies in the body of a `namespace_definition` the ordinary
2186/// declaration walk still reaches, so a recovery that runs ahead of that walk
2187/// would name what it finds without the namespace.
2188fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2189    let mut current = node;
2190    while let Some(parent) = ancestry.parent(current) {
2191        if parent.kind() == "namespace_definition"
2192            && parent.child_by_field_name("body") == Some(current)
2193        {
2194            return true;
2195        }
2196        current = parent;
2197    }
2198    false
2199}
2200
2201/// Whether the source a recovered callable's byte range spells is a definition
2202/// (a declarator with a body) rather than a declaration.
2203///
2204/// A callable recovered from a mangled region owns no node of its own in the
2205/// file's tree, so the occurrence-role scan that climbs from the range to the
2206/// node containing it lands on whatever container the parser left and answers
2207/// for that instead -- which called Botan's inline `XMSS_Parameters` private
2208/// constructor a declaration and left its call site with nothing to navigate to
2209/// (#2552). Reparse the range on its own, the same offset-preserving reparse
2210/// extraction used, and read the answer from the one declaration it spells.
2211///
2212/// `None` when the range is not one recovered declaration on its own, which is
2213/// the case for every ordinary declaration, so callers keep their own answer.
2214pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2215    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2216    let root = tree.root_node();
2217    let mut cursor = root.walk();
2218    let items = root
2219        .named_children(&mut cursor)
2220        .filter(|child| child.kind() != "comment")
2221        .collect::<Vec<_>>();
2222    let [item] = items.as_slice() else {
2223        return None;
2224    };
2225    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2226        return None;
2227    }
2228    match item.kind() {
2229        "function_definition" => Some(item.child_by_field_name("body").is_some()),
2230        "declaration" | "field_declaration" => Some(false),
2231        _ => None,
2232    }
2233}
2234
2235/// Whether `node` is an envelope an attribute-like macro invocation left where
2236/// declarations were written: the declaration-scope `ERROR`
2237/// [`macro_wrapped_declarations`] reads, or the collapsed run
2238/// [`collapsed_macro_declaration_run`] reads.
2239///
2240/// Extraction and resolution must read one definition of this shape. The
2241/// resolver climbs from a declaration's recorded byte range to the node that
2242/// declares it, and a recovered declaration's range lies inside one of these
2243/// envelopes rather than inside a `declaration` node, so the climb stops here
2244/// (the same role `is_recovered_exported_class_container` plays for a recovered
2245/// class).
2246pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2247    let ancestry = ParentIndex::unindexed();
2248    !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2249        || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2250}
2251
2252fn recover_exported_class_function_definition<'tree>(
2253    node: Node<'tree>,
2254    source: &str,
2255) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
2256    if node.kind() != "function_definition" {
2257        return None;
2258    }
2259    if let Some(prefix) = node.prev_named_sibling()
2260        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2261        && recovered.range.end_byte == node.end_byte()
2262    {
2263        return Some((node, recovered.name, recovered.raw_supertypes));
2264    }
2265    let type_node = node.child_by_field_name("type")?;
2266    let declarator = node.child_by_field_name("declarator")?;
2267
2268    if matches!(
2269        type_node.kind(),
2270        "class_specifier" | "struct_specifier" | "union_specifier"
2271    ) {
2272        let type_name = type_node
2273            .child_by_field_name("name")
2274            .and_then(|name| direct_identifier_name(name, source));
2275        let exported_macro_type = type_name
2276            .as_ref()
2277            .is_some_and(|name| cpp_export_macro_token(name));
2278        if exported_macro_type {
2279            let mut cursor = node.walk();
2280            let errors_before_declarator = node
2281                .named_children(&mut cursor)
2282                .filter(|child| {
2283                    child.kind() == "ERROR"
2284                        && child.start_byte() >= type_node.end_byte()
2285                        && child.end_byte() <= declarator.start_byte()
2286                })
2287                .collect::<Vec<_>>();
2288            if let Some(name) = errors_before_declarator
2289                .iter()
2290                .find_map(|error| displaced_exported_class_name(*error, source))
2291            {
2292                let raw_supertypes = errors_before_declarator
2293                    .iter()
2294                    .any(|error| malformed_inheritance_syntax(*error))
2295                    .then(|| recovered_malformed_base_name(declarator, source))
2296                    .flatten()
2297                    .map(|base| vec![base]);
2298                return Some((node, name, raw_supertypes));
2299            }
2300            if errors_before_declarator
2301                .iter()
2302                .any(|error| malformed_inheritance_syntax(*error))
2303            {
2304                return None;
2305            }
2306        }
2307        if !exported_macro_type
2308            && let Some(name) = type_name
2309            && !cpp_export_macro_token(&name)
2310            && let Some(base) =
2311                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2312        {
2313            return Some((node, name, Some(vec![base])));
2314        }
2315        if let Some(name) = direct_identifier_name(declarator, source)
2316            && exported_macro_type
2317            && !cpp_export_macro_token(&name)
2318        {
2319            let raw_supertypes = exported_macro_type
2320                .then(|| recovered_single_base_after_declarator(node, declarator, source))
2321                .flatten()
2322                .map(|base| vec![base]);
2323            return Some((node, name, raw_supertypes));
2324        }
2325        if declarator.kind() == "parenthesized_declarator"
2326            && type_node
2327                .child_by_field_name("name")
2328                .and_then(|name| direct_identifier_name(name, source))
2329                .is_some_and(|name| cpp_export_macro_token(&name))
2330        {
2331            if let Some((name, base)) =
2332                recovered_function_like_export_class_owner(declarator, source)
2333            {
2334                return Some((node, name, Some(vec![base])));
2335            }
2336            let body_start = node
2337                .child_by_field_name("body")
2338                .map(|body| body.start_byte())
2339                .unwrap_or(node.end_byte());
2340            let mut cursor = node.walk();
2341            if let Some(name) = node
2342                .named_children(&mut cursor)
2343                .filter(|child| {
2344                    child.kind() == "ERROR"
2345                        && child.start_byte() >= declarator.end_byte()
2346                        && child.end_byte() <= body_start
2347                })
2348                .find_map(|error| declarator_name_from_node(error, source))
2349            {
2350                return Some((node, name, None));
2351            }
2352        }
2353    }
2354
2355    let declarator_text = direct_identifier_name(declarator, source)?;
2356    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2357        return None;
2358    }
2359    class_identifier_before_body(node, source).map(|name| (node, name, None))
2360}
2361
2362fn recovered_function_like_export_class_owner(
2363    declarator: Node<'_>,
2364    source: &str,
2365) -> Option<(String, String)> {
2366    if declarator.kind() != "parenthesized_declarator" {
2367        return None;
2368    }
2369    let mut cursor = declarator.walk();
2370    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2371    let [prefix, base] = children.as_slice() else {
2372        return None;
2373    };
2374    if prefix.kind() != "ERROR"
2375        || !matches!(
2376            base.kind(),
2377            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2378        )
2379    {
2380        return None;
2381    }
2382    let mut identifiers = Vec::new();
2383    let mut prefix_cursor = prefix.walk();
2384    for child in prefix.named_children(&mut prefix_cursor) {
2385        match child.kind() {
2386            "number_literal" | "string_literal" | "char_literal" => {}
2387            "identifier" | "type_identifier" => {
2388                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2389            }
2390            _ => return None,
2391        }
2392    }
2393    let name = match identifiers.as_slice() {
2394        [name] => name.clone(),
2395        [name, final_token] if final_token == "final" => name.clone(),
2396        _ => return None,
2397    };
2398    if name.is_empty() || cpp_export_macro_token(&name) {
2399        return None;
2400    }
2401    let base = recovered_malformed_base_name(*base, source)?;
2402    Some((name, base))
2403}
2404
2405/// Collect the base names tree-sitter scattered across the recovered head of a
2406/// function-like export-macro class. `skip` names the structural children that
2407/// are not bases, such as the class name and the body. A base arrives either as
2408/// a direct sibling identifier or inside the `ERROR` node the grammar produced
2409/// for a `: public Base` fragment. The grammar leaves the `final` specifier and
2410/// the access specifiers in the same position as the bases, so drop them.
2411/// The bases a recovered function-like export-macro class head names between
2412/// `after` (the end of the class name, or of the access specifier that stands
2413/// in for it) and `before` (the start of the body, or of the `init_declarator`
2414/// that carries it). Bases arrive as bare declarator fields and inside `ERROR`
2415/// fragments, and one fragment can also hold the class name and `final`
2416/// (`M1 M2 Name final : public A`), so each part is bounded by position rather
2417/// than by the fragment that holds it.
2418fn recovered_export_head_bases(
2419    node: Node<'_>,
2420    after: usize,
2421    before: usize,
2422    source: &str,
2423) -> Vec<String> {
2424    let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2425    let mut bases = Vec::new();
2426    let mut cursor = node.walk();
2427    for child in node.named_children(&mut cursor) {
2428        if child.kind() == "ERROR" {
2429            let mut error_cursor = child.walk();
2430            bases.extend(
2431                child
2432                    .named_children(&mut error_cursor)
2433                    .filter(within)
2434                    .filter_map(|part| recovered_malformed_base_name(part, source)),
2435            );
2436        } else if within(&child)
2437            && let Some(base) = recovered_malformed_base_name(child, source)
2438        {
2439            bases.push(base);
2440        }
2441    }
2442    bases.retain(|base| {
2443        !matches!(
2444            base.as_str(),
2445            "final" | "public" | "protected" | "private" | "virtual"
2446        )
2447    });
2448    bases
2449}
2450
2451/// Whether `token` is the `final` that closes a recovered class head. The
2452/// grammar keeps it as the `final` keyword when it recognised the head
2453/// (`M1 M2 Name final : public A`) and as an `identifier` spelled `final` when
2454/// it did not (`Name final {`, `OTHER_MACRO Name final : public A`).
2455fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2456    if token.is_named() {
2457        token.kind() == "identifier" && node_text(token, source) == "final"
2458    } else {
2459        token.kind() == "final"
2460    }
2461}
2462
2463/// The class name of a recovered function-like export-macro class head, read by
2464/// position rather than spelling (#2557). `node` is the sibling tree-sitter
2465/// built from the head after `class MACRO(2, 0)`, and `tail` the child that
2466/// carries the body. The head's tokens, in source order with `ERROR` fragments
2467/// flattened, read `macros... name [final] [: bases...]`: the name is the last
2468/// identifier before the head ends, and the head ends at `final`, at the base
2469/// clause `:`, or at `tail`. Every identifier before the name is decoration
2470/// (`class MACRO(2, 0) OTHER_MACRO Name`), and a class named in capitals
2471/// (`X509_CA`) is a class name like any other.
2472///
2473/// A `MISSING` identifier is a zero-width node tree-sitter invented to close a
2474/// rule, not a token of the source, so it is never the name.
2475fn recovered_export_head_name<'tree>(
2476    node: Node<'tree>,
2477    tail: Node<'tree>,
2478    source: &str,
2479) -> Option<Node<'tree>> {
2480    export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2481}
2482
2483/// The class name of a head whose tokens straddle the recovered pair: the
2484/// grammar can keep `class MACRO(3, 6) Name final :` in the prefix fragment and
2485/// only the base list and the body in the sibling (Botan's TPM2 keys, #2924).
2486/// The positional rule is the same, read over the two token runs in source
2487/// order, so a name in either node wins by position rather than by which node
2488/// holds it.
2489fn recovered_export_pair_head_name<'tree>(
2490    prefix: Node<'tree>,
2491    sibling: Node<'tree>,
2492    tail: Node<'tree>,
2493    source: &str,
2494) -> Option<Node<'tree>> {
2495    let mut tokens = export_head_tokens(prefix, None);
2496    tokens.extend(export_head_tokens(sibling, Some(tail)));
2497    export_head_name_from_tokens(&tokens, source)
2498}
2499
2500/// One node's head tokens in source order, with `ERROR` fragments spliced in
2501/// place (an `ERROR` here is a grouping failure, not a construct) and stopping
2502/// before `tail` when the caller names one.
2503fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2504    let mut tokens = Vec::new();
2505    let mut cursor = node.walk();
2506    for child in node.children(&mut cursor) {
2507        if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2508            break;
2509        }
2510        if child.kind() == "ERROR" {
2511            let mut fragment_cursor = child.walk();
2512            tokens.extend(child.children(&mut fragment_cursor));
2513        } else {
2514            tokens.push(child);
2515        }
2516    }
2517    tokens
2518}
2519
2520/// The last identifier before the head ends, per the positional rule.
2521fn export_head_name_from_tokens<'tree>(
2522    tokens: &[Node<'tree>],
2523    source: &str,
2524) -> Option<Node<'tree>> {
2525    let mut name = None;
2526    for token in tokens.iter().copied() {
2527        if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2528        {
2529            break;
2530        }
2531        if token.is_named()
2532            && !token.is_missing()
2533            && matches!(
2534                token.kind(),
2535                "identifier" | "type_identifier" | "field_identifier"
2536            )
2537        {
2538            name = Some(token);
2539        }
2540    }
2541    name
2542}
2543
2544/// The `init_declarator` whose `value` carries the class body when the grammar
2545/// keeps a function-like export-macro class head as one `declaration`.
2546fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2547    let mut cursor = declaration.walk();
2548    declaration
2549        .named_children(&mut cursor)
2550        .find(|child| child.kind() == "init_declarator")
2551}
2552
2553/// Read the bases and the initializer-list body of a `declaration`-shaped tail
2554/// of a function-like export-macro class head. The grammar splits a base list
2555/// across bare declarator fields, `ERROR` fragments, and one trailing
2556/// `init_declarator` whose `value` is the class body. `head_end` is where the
2557/// class identity ends and the bases can begin: the end of the access specifier
2558/// when the class name went to a statement label, and the end of the class name
2559/// itself otherwise.
2560fn recovered_export_declaration_tail<'tree>(
2561    declaration: Node<'tree>,
2562    head_end: usize,
2563    source: &str,
2564) -> Option<(Vec<String>, Node<'tree>)> {
2565    let init = recovered_export_init_declarator(declaration)?;
2566    let body = init.child_by_field_name("value")?;
2567    if body.kind() != "initializer_list" {
2568        return None;
2569    }
2570    let mut bases = recovered_export_head_bases(declaration, head_end, init.start_byte(), source);
2571    bases.extend(recovered_export_head_bases(
2572        init,
2573        init.start_byte(),
2574        body.start_byte(),
2575        source,
2576    ));
2577    Some((bases, body))
2578}
2579
2580/// Whether `node` is the head fragment tree-sitter leaves where
2581/// `class MACRO(args)` was written: the class keyword and the export macro's
2582/// function-like invocation in that order, with nothing else between them.
2583/// Spelling plays no part -- the invocation's `(` following the macro
2584/// identifier directly is what makes it function-like (#2557).
2585///
2586/// Two reductions produce the head. On its own the grammar keeps a body-less
2587/// `class_specifier` named after the macro inside a declaration-scope `ERROR`,
2588/// with the invocation's `(`, arguments and `)` beside it. After object-like
2589/// macro lines -- Botan's `BOTAN_DIAGNOSTIC_PUSH` /
2590/// `BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE` -- it instead keeps a
2591/// `declaration` whose class keyword is demoted to a bare `identifier` and
2592/// whose macro invocation becomes the trailing `init_declarator` (#2924).
2593fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2594    match node.kind() {
2595        "ERROR" => {
2596            let Some(class_node) = first_class_like_child(node) else {
2597                return false;
2598            };
2599            if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2600                return false;
2601            }
2602            if class_node
2603                .child_by_field_name("name")
2604                .and_then(|name| direct_identifier_name(name, source))
2605                .is_none()
2606            {
2607                return false;
2608            }
2609            class_node
2610                .next_sibling()
2611                .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2612        }
2613        "declaration" => {
2614            let mut cursor = node.walk();
2615            let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2616            // The class keyword lost its own node: it survives as a bare
2617            // identifier spelled `class`, `struct` or `union`, which is never a
2618            // declarator or a type in well-formed code. It can sit inside an
2619            // `ERROR` beside the object-like macro names, and an `ERROR` here is
2620            // a grouping failure rather than a construct, so read through it.
2621            let Some(keyword) = children
2622                .iter()
2623                .copied()
2624                .flat_map(|child| {
2625                    let mut cursor = child.walk();
2626                    if child.kind() == "ERROR" {
2627                        child.named_children(&mut cursor).collect::<Vec<_>>()
2628                    } else {
2629                        vec![child]
2630                    }
2631                })
2632                .find(|child| {
2633                    child.kind() == "identifier"
2634                        && matches!(node_text(*child, source), "class" | "struct" | "union")
2635                })
2636            else {
2637                return false;
2638            };
2639            children.last().is_some_and(|init| {
2640                init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2641                    let mut cursor = init.walk();
2642                    let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2643                    matches!(parts.as_slice(), [macro_name, arguments]
2644                        if matches!(macro_name.kind(), "identifier" | "type_identifier")
2645                            && arguments.kind() == "argument_list")
2646                }
2647            })
2648        }
2649        _ => false,
2650    }
2651}
2652
2653fn recover_function_like_export_class_pair(
2654    node: Node<'_>,
2655    source: &str,
2656) -> Option<RecoveredFunctionLikeExportClassPair> {
2657    if !is_function_like_export_class_head(node, source) {
2658        return None;
2659    }
2660    let sibling = node.next_named_sibling()?;
2661    let (name, raw_supertypes, body) = match sibling.kind() {
2662        // At translation-unit scope tree-sitter can leave the malformed class
2663        // head in one ERROR node and parse its body as the adjacent compound
2664        // statement. The head still carries the export invocation, displaced
2665        // class name, and optional `final` token in source order, so recover
2666        // the name by the same positional rule as the other shapes.
2667        "compound_statement" => (
2668            recovered_export_pair_head_name(node, sibling, sibling, source)
2669                .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2670            None,
2671            sibling,
2672        ),
2673        "expression_statement" => {
2674            let compound = sibling.named_child(0)?;
2675            if compound.kind() != "compound_literal_expression" {
2676                return None;
2677            }
2678            let body = compound.child_by_field_name("value")?;
2679            if body.kind() != "initializer_list" {
2680                return None;
2681            }
2682            (
2683                compound
2684                    .child_by_field_name("type")
2685                    .and_then(|name| direct_identifier_name(name, source))?,
2686                None,
2687                body,
2688            )
2689        }
2690        "labeled_statement" => {
2691            let label = sibling.child_by_field_name("label")?;
2692            if label.kind() != "statement_identifier" {
2693                return None;
2694            }
2695            let name = normalize_cpp_whitespace(node_text(label, source));
2696            let declaration = sibling
2697                .named_children(&mut sibling.walk())
2698                .find(|child| child.kind() == "declaration")?;
2699            let access = declaration.child_by_field_name("type")?;
2700            if !matches!(
2701                node_text(access, source),
2702                "public" | "protected" | "private"
2703            ) {
2704                return None;
2705            }
2706            let (bases, body) =
2707                recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
2708            (name, (!bases.is_empty()).then_some(bases), body)
2709        }
2710        // `class MACRO(2, 0) Name final { ... };`,
2711        // `class MACRO(2, 0) Name final : public Base { ... };`, and
2712        // `class MACRO(2, 0) OTHER_MACRO Name { ... };`. The head's identifiers
2713        // spread over the `type` field, the declarator field, and `ERROR`
2714        // fragments beside them; the name is the last one before the head ends.
2715        "function_definition" => {
2716            let body = sibling.child_by_field_name("body")?;
2717            if body.kind() != "compound_statement" {
2718                return None;
2719            }
2720            let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
2721            let bases = recovered_export_head_bases(
2722                sibling,
2723                name_node.end_byte(),
2724                body.start_byte(),
2725                source,
2726            );
2727            (
2728                normalize_cpp_whitespace(node_text(name_node, source)),
2729                (!bases.is_empty()).then_some(bases),
2730                body,
2731            )
2732        }
2733        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
2734        // comma-separated base list makes the grammar keep the whole tail as one
2735        // declaration whose trailing `init_declarator` carries the body.
2736        "declaration" => {
2737            let init = recovered_export_init_declarator(sibling)?;
2738            let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
2739            let (bases, body) =
2740                recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
2741            (
2742                normalize_cpp_whitespace(node_text(name_node, source)),
2743                (!bases.is_empty()).then_some(bases),
2744                body,
2745            )
2746        }
2747        _ => return None,
2748    };
2749    debug_assert!(
2750        !name.is_empty(),
2751        "a recovered class head names its class by an identifier token"
2752    );
2753    let range = Range {
2754        start_byte: node.start_byte(),
2755        end_byte: sibling.end_byte(),
2756        start_line: node.start_position().row + 1,
2757        end_line: sibling.end_position().row + 1,
2758    };
2759    Some(RecoveredFunctionLikeExportClassPair {
2760        name,
2761        raw_supertypes,
2762        range,
2763        fragmented_body: recovered_fragmented_export_body(body, range)?,
2764    })
2765}
2766
2767/// Recover a function-like export-macro class that tree-sitter embedded in a
2768/// larger error after an earlier malformed class body. The grammar still
2769/// preserves every part of the class head: the `class` token, export macro
2770/// identifier and argument list, displaced class identifier, access specifier,
2771/// base field, and initializer-list-shaped body. Match only that complete
2772/// structured sequence and keep each recovered class's exact byte envelope.
2773fn recover_embedded_function_like_export_classes(
2774    node: Node<'_>,
2775    source: &str,
2776) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2777    if !node.is_error() {
2778        return Vec::new();
2779    }
2780
2781    let mut nodes = Vec::new();
2782    let mut class_tokens = Vec::new();
2783    let mut stack = vec![node];
2784    while let Some(current) = stack.pop() {
2785        nodes.push(current);
2786        // An error subtree without an anonymous class token cannot contain
2787        // a recoverable class head. Detect that before sorting or resolving
2788        // kind-id tables; the grammar spelling matches all aliased symbol ids.
2789        if !current.is_named() && current.kind() == "class" {
2790            class_tokens.push(current);
2791        }
2792        push_children_reversed(current, &mut stack);
2793    }
2794    if class_tokens.is_empty() {
2795        return Vec::new();
2796    }
2797    class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2798    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2799
2800    // The scans below ask the same handful of kind questions once per node of
2801    // the error subtree, so the kinds are resolved to symbol ids first (#3097).
2802    let language = node.language();
2803    let class_kind = NodeKindIds::new(&language, "class");
2804    let identifier_kinds = [
2805        NodeKindIds::new(&language, "identifier"),
2806        NodeKindIds::new(&language, "type_identifier"),
2807        NodeKindIds::new(&language, "field_identifier"),
2808    ];
2809    let argument_list_kind = NodeKindIds::new(&language, "argument_list");
2810    let colon_kind = NodeKindIds::new(&language, ":");
2811    let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
2812
2813    let mut recovered = Vec::new();
2814    for class_token in class_tokens {
2815        debug_assert!(class_kind.matches(class_token));
2816        let row = class_token.start_position().row;
2817        // The head after the `class` token reads `MACRO(args) macros... name
2818        // [final] : bases`. The macro is the first identifier, a function-like
2819        // invocation when its argument list is the next thing after it, and the
2820        // name is the last identifier before the head ends at `final` or at the
2821        // base clause `:`. Spelling plays no part (#2557).
2822        let is_identifier = |candidate: &Node<'_>| {
2823            !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
2824        };
2825        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2826            candidate.start_byte() >= class_token.end_byte()
2827                && candidate.start_position().row == row
2828                && is_identifier(candidate)
2829        }) else {
2830            continue;
2831        };
2832        let Some(arguments) = nodes.iter().copied().find(|candidate| {
2833            argument_list_kind.matches(*candidate)
2834                && candidate.start_byte() >= macro_name.end_byte()
2835                && candidate.start_position().row == row
2836        }) else {
2837            continue;
2838        };
2839        if nodes.iter().any(|candidate| {
2840            is_identifier(candidate)
2841                && candidate.start_byte() >= macro_name.end_byte()
2842                && candidate.end_byte() <= arguments.start_byte()
2843        }) {
2844            continue;
2845        }
2846        let Some(head_end) = nodes.iter().copied().find(|candidate| {
2847            candidate.start_byte() >= arguments.end_byte()
2848                && (recovered_export_head_final(*candidate, source)
2849                    || (!candidate.is_named() && colon_kind.matches(*candidate)))
2850        }) else {
2851            continue;
2852        };
2853        let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
2854            is_identifier(candidate)
2855                && candidate.start_byte() >= arguments.end_byte()
2856                && candidate.end_byte() <= head_end.start_byte()
2857                && candidate.start_position().row == row
2858        }) else {
2859            continue;
2860        };
2861        let name = normalize_cpp_whitespace(node_text(name_node, source));
2862        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2863            field_initializer_kind.matches(*candidate)
2864                && candidate.start_byte() >= name_node.end_byte()
2865                && candidate
2866                    .child_by_field_name("field")
2867                    .or_else(|| candidate.named_child(0))
2868                    .is_some()
2869                && candidate
2870                    .child_by_field_name("value")
2871                    .or_else(|| {
2872                        let mut cursor = candidate.walk();
2873                        candidate
2874                            .named_children(&mut cursor)
2875                            .find(|child| child.kind() == "initializer_list")
2876                    })
2877                    .is_some_and(|value| value.kind() == "initializer_list")
2878        }) else {
2879            continue;
2880        };
2881        let has_access = nodes.iter().copied().any(|candidate| {
2882            candidate.start_byte() >= name_node.end_byte()
2883                && candidate.end_byte() <= base_initializer.start_byte()
2884                && matches!(
2885                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2886                    "public" | "protected" | "private"
2887                )
2888        });
2889        if !has_access {
2890            continue;
2891        }
2892        let Some(base_node) = base_initializer
2893            .child_by_field_name("field")
2894            .or_else(|| base_initializer.named_child(0))
2895        else {
2896            continue;
2897        };
2898        let Some(base) = recovered_malformed_base_name(base_node, source) else {
2899            continue;
2900        };
2901        let body = base_initializer
2902            .child_by_field_name("value")
2903            .or_else(|| {
2904                let mut cursor = base_initializer.walk();
2905                base_initializer
2906                    .named_children(&mut cursor)
2907                    .find(|child| child.kind() == "initializer_list")
2908            })
2909            .expect("initializer-list value checked above");
2910        let range = Range {
2911            start_byte: class_token.start_byte(),
2912            end_byte: body.end_byte(),
2913            start_line: class_token.start_position().row + 1,
2914            end_line: body.end_position().row + 1,
2915        };
2916        if recovered
2917            .iter()
2918            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
2919                existing.name == name && existing.range == range
2920            })
2921        {
2922            continue;
2923        }
2924        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
2925            name,
2926            range,
2927            raw_supertypes: vec![base],
2928            fragmented_body: match recovered_fragmented_export_body(body, range) {
2929                Some(fragmented) => fragmented,
2930                None => continue,
2931            },
2932        });
2933    }
2934    recovered
2935}
2936
2937fn lifted_function_like_export_class_namespace<'tree>(
2938    node: Node<'tree>,
2939    source: &str,
2940    ancestry: &ParentIndex<'tree>,
2941) -> Option<String> {
2942    // A long malformed body can embed the next exported class several levels
2943    // below a bogus top-level function_definition. Compare namespace evidence
2944    // against that top-level envelope, not only the recovered ERROR's direct
2945    // parent. The source tree still proves the same boundary: one earlier
2946    // malformed namespace and one later standalone closing brace.
2947    let mut anchor = node;
2948    let parent = loop {
2949        let parent = ancestry.parent(anchor)?;
2950        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
2951            break parent;
2952        }
2953        anchor = parent;
2954    };
2955    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
2956        sibling.start_byte() > anchor.end_byte()
2957            && sibling.kind() == "ERROR"
2958            && sibling.named_child_count() == 0
2959            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
2960    });
2961    if !has_later_close {
2962        return None;
2963    }
2964    let candidates = parent
2965        .named_children(&mut parent.walk())
2966        .filter(|sibling| {
2967            sibling.kind() == "namespace_definition"
2968                && sibling.has_error()
2969                && sibling.end_byte() < anchor.start_byte()
2970        })
2971        .filter_map(|namespace| {
2972            namespace
2973                .child_by_field_name("name")
2974                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
2975                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
2976        })
2977        .collect::<Vec<_>>();
2978    let [namespace] = candidates.as_slice() else {
2979        return None;
2980    };
2981    Some(namespace.clone())
2982}
2983
2984pub(crate) fn recovered_function_like_export_class_pair_has_body(
2985    node: Node<'_>,
2986    source: &str,
2987    identifier: &str,
2988    range: &Range,
2989) -> bool {
2990    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
2991        recovered.name == identifier
2992            && recovered.range.start_byte == range.start_byte
2993            && recovered.range.end_byte == range.end_byte
2994    })
2995}
2996
2997/// One file's embedded export-macro class recovery, resolved once and keyed by
2998/// the `ERROR` node that mints each set.
2999///
3000/// [`recover_embedded_function_like_export_classes`] collects and sorts an
3001/// `ERROR` node's whole subtree, and the declaration-strength question asks it
3002/// once per class-like unit in the file. On a translation unit the parser could
3003/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
3004/// node spans most of the file -- that is one full subtree pass per class,
3005/// quadratic in the file's size, and it was 78% of that file's inverse scan
3006/// (#1496).
3007///
3008/// Keyed by byte span rather than node identity, so one analyzer generation's
3009/// re-parses of the same content share the index. A nested `ERROR` that shares
3010/// its parent's span recovers the same classes: the only node the parent adds
3011/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
3012#[derive(Default)]
3013pub struct CppRecoveredExportClassIndex {
3014    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3015}
3016
3017impl CppRecoveredExportClassIndex {
3018    pub fn build(root: Node<'_>, source: &str) -> Self {
3019        let mut by_error_node: HashMap<
3020            (usize, usize),
3021            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3022        > = HashMap::default();
3023        // One cursor for the whole-file walk, and the error question asked by
3024        // symbol rather than by `kind()` string (#3097).
3025        let mut cursor = root.walk();
3026        let mut stack = vec![root];
3027        while let Some(node) = stack.pop() {
3028            if node.is_error() {
3029                let recovered = recover_embedded_function_like_export_classes(node, source);
3030                if !recovered.is_empty() {
3031                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3032                }
3033            }
3034            stack.extend(node.named_children(&mut cursor));
3035        }
3036        Self { by_error_node }
3037    }
3038
3039    /// The bytes this index holds, for the analyzer cache's weight.
3040    pub fn approximate_size(&self) -> usize {
3041        self.by_error_node
3042            .values()
3043            .fold(0usize, |total, recovered| {
3044                recovered.iter().fold(
3045                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3046                    |acc, class| {
3047                        acc.saturating_add(std::mem::size_of::<
3048                            RecoveredEmbeddedFunctionLikeExportClass,
3049                        >())
3050                        .saturating_add(class.name.len())
3051                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3052                    },
3053                )
3054            })
3055    }
3056
3057    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3058        self.by_error_node
3059            .get(&(node.start_byte(), node.end_byte()))
3060            .is_some_and(|recovered| {
3061                recovered.iter().any(|class| {
3062                    class.name == identifier
3063                        && class.range.start_byte == range.start_byte
3064                        && class.range.end_byte == range.end_byte
3065                })
3066            })
3067    }
3068}
3069
3070// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
3071// `recovered_class_body_at` pops while deciding whether a recovered class shape
3072// claims one declaration range. The count is deterministic for a given source,
3073// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
3074// pins it directly instead of timing the walk, the way #2358 pinned the
3075// `remove_code_unit` scan.
3076#[cfg(any(test, feature = "test-support"))]
3077thread_local! {
3078    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3079        const { std::cell::Cell::new(0) };
3080}
3081
3082/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
3083/// the calling thread since the last
3084/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
3085#[cfg(any(test, feature = "test-support"))]
3086#[doc(hidden)]
3087pub fn recovered_class_body_node_visits_for_test() -> usize {
3088    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3089}
3090
3091/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
3092#[cfg(any(test, feature = "test-support"))]
3093#[doc(hidden)]
3094pub fn reset_recovered_class_body_node_visits_for_test() {
3095    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3096}
3097
3098#[cfg(any(test, feature = "test-support"))]
3099fn record_recovered_class_body_visit() {
3100    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3101}
3102
3103#[cfg(not(any(test, feature = "test-support")))]
3104fn record_recovered_class_body_visit() {}
3105
3106/// Whether a recovered class shape named `identifier` owns `range`, and if so
3107/// whether that shape has a body.
3108///
3109/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
3110/// forward declaration, and `None` means no recovered shape claims the range,
3111/// so the caller reads the plain `class_specifier` family instead. This is the
3112/// single definition of "does a recovered class have a body": the resolver's
3113/// declaration-strength answer and the navigation occurrence role both read it,
3114/// so an export-macro class is a definition on both paths.
3115///
3116/// The walk follows only the nodes whose span covers `range.start_byte`, which
3117/// is every node that can answer. Each recovered shape reports a range that
3118/// starts at the node's own start byte (the function-like export pair, whose
3119/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
3120/// class, which the caller gates on an equal start) or at a token inside the
3121/// node (the embedded export class, keyed on its `class` token, and the
3122/// exported class wrapper, gated on containment here), and every one of them is
3123/// accepted only on an exact match with `range`. Descending everywhere instead
3124/// made one declaration-strength question cost a full pass over the file, so
3125/// asking it once per reference was quadratic in file size: 80% of the 385 s
3126/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
3127/// (#1496).
3128pub(crate) fn recovered_class_body_at(
3129    recovered_export_classes: &CppRecoveredExportClassIndex,
3130    root: Node<'_>,
3131    source: &str,
3132    identifier: &str,
3133    range: &Range,
3134) -> Option<bool> {
3135    let covers_range_start = |node: &Node<'_>| {
3136        node.start_byte() <= range.start_byte
3137            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3138    };
3139    let mut stack = vec![root];
3140    let mut saw_forward = false;
3141    while let Some(node) = stack.pop() {
3142        record_recovered_class_body_visit();
3143        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
3144        // so an unequal start settles it before the recovery reads the node's
3145        // children at all.
3146        if (node.start_byte() == range.start_byte
3147            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3148            || recovered_export_classes.claims(node, identifier, range)
3149            || (node.start_byte() == range.start_byte
3150                && recovered_fragmented_class_has_body(node, source, identifier, range))
3151        {
3152            return Some(true);
3153        }
3154        if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3155            return Some(true);
3156        }
3157        // Macro-decorated exported classes are recovered from a malformed
3158        // function_definition/declaration wrapper. Their indexed class range starts at
3159        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
3160        // may also extend the indexed range beyond the wrapper through trailing class
3161        // fragments. Match the structured container that owns the range start by its
3162        // recovered name instead of requiring identical boundaries.
3163        if node.start_byte() <= range.start_byte
3164            && range.start_byte < node.end_byte()
3165            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3166        {
3167            if has_body {
3168                return Some(true);
3169            }
3170            saw_forward = true;
3171            continue;
3172        }
3173        let mut cursor = node.walk();
3174        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3175    }
3176    saw_forward.then_some(false)
3177}
3178
3179/// Whether the collapsed-aggregate recovery reads `node`'s children as the
3180/// head of `identifier`'s member list at `range`.
3181///
3182/// An aggregate whose member list opens with a field-list macro invocation
3183/// never forms a specifier, so the ordinary strength read finds no specifier
3184/// node at the indexed range and answers `Unknown`. A complete definition
3185/// recovered this way then loses the defining-type choice to a forward
3186/// declaration of the same tag in another header, and every member the
3187/// definition declares becomes unreachable through it: libuv's
3188/// `struct uv_loop_s` in `include/uv.h` lost to `struct uv_loop_s;` in
3189/// `include/uv/unix.h`, hiding `uv_loop_s.wq` (#3098). The recovery already
3190/// proves the body, so it answers for its own ranges.
3191fn recovered_collapsed_aggregate_has_body(
3192    node: Node<'_>,
3193    source: &str,
3194    identifier: &str,
3195    range: &Range,
3196) -> bool {
3197    let claims = |head: &CppCollapsedAggregateHead<'_>| {
3198        head.key.start_byte() == range.start_byte
3199            && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3200    };
3201    if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3202        return true;
3203    }
3204    if !node.is_error() {
3205        return false;
3206    }
3207    let mut cursor = node.walk();
3208    let children = node.children(&mut cursor).collect::<Vec<_>>();
3209    children
3210        .iter()
3211        .enumerate()
3212        .filter(|(_, child)| {
3213            child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3214        })
3215        .any(|(index, _)| {
3216            cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3217        })
3218}
3219
3220/// Whether `node` is the base type displaced into the declarator field of an
3221/// export-macro class that tree-sitter represented as a declaration or
3222/// function definition.
3223///
3224/// Declaration extraction already recovers this exact malformed envelope as a
3225/// class and records the declarator as its base. Reference extraction must use
3226/// the same structural fact instead of treating the node as a function name.
3227pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3228    if !matches!(
3229        node.kind(),
3230        "qualified_identifier" | "scoped_type_identifier" | "template_type"
3231    ) {
3232        return false;
3233    }
3234    if let Some(function) = node.parent().filter(|parent| {
3235        parent.kind() == "function_definition"
3236            && parent
3237                .child_by_field_name("declarator")
3238                .is_some_and(|declarator| same_node(declarator, node))
3239    }) {
3240        return recover_exported_class_function_definition(function, source)
3241            .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
3242    }
3243    let Some(initializer) = node.parent().filter(|parent| {
3244        parent.kind() == "init_declarator"
3245            && parent
3246                .child_by_field_name("declarator")
3247                .is_some_and(|declarator| same_node(declarator, node))
3248    }) else {
3249        return false;
3250    };
3251    initializer
3252        .parent()
3253        .filter(|parent| parent.kind() == "declaration")
3254        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3255        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3256}
3257
3258/// Recover the class item from a region reparse that still carries the
3259/// sentinel's synthetic function envelope.  An unknown class attribute can
3260/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
3261/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
3262/// then nested below that function, so direct class-child lookup is not enough.
3263struct CppSentinelReparsedClass<'tree> {
3264    declaration_node: Node<'tree>,
3265    name: String,
3266    body: Node<'tree>,
3267    raw_supertypes: Option<Vec<String>>,
3268}
3269
3270fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3271    let mut cursor = root.walk();
3272    root.named_children(&mut cursor)
3273        .find(|child| child.kind() != "comment")
3274        .filter(|child| child.kind() == "template_declaration")
3275}
3276
3277fn cpp_sentinel_reparsed_class<'tree>(
3278    root: Node<'tree>,
3279    template_node: Option<Node<'tree>>,
3280    source: &str,
3281    ancestry: &ParentIndex<'tree>,
3282) -> Option<CppSentinelReparsedClass<'tree>> {
3283    let container = template_node.unwrap_or(root);
3284    let mut cursor = container.walk();
3285    for child in container.named_children(&mut cursor) {
3286        if matches!(
3287            child.kind(),
3288            "class_specifier" | "struct_specifier" | "union_specifier"
3289        ) {
3290            let name = class_like_name(child, source, ancestry)?;
3291            let body = cpp_body_node(child)?;
3292            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3293                .then(|| extract_cpp_supertypes(child, source));
3294            return Some(CppSentinelReparsedClass {
3295                declaration_node: child,
3296                name,
3297                body,
3298                raw_supertypes,
3299            });
3300        }
3301        if child.kind() == "declaration"
3302            && let Some(class_node) = first_class_like_child(child)
3303        {
3304            let name = class_like_name(class_node, source, ancestry)?;
3305            let body = cpp_body_node(class_node)?;
3306            let raw_supertypes =
3307                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3308                    .then(|| extract_cpp_supertypes(class_node, source));
3309            return Some(CppSentinelReparsedClass {
3310                declaration_node: class_node,
3311                name,
3312                body,
3313                raw_supertypes,
3314            });
3315        }
3316        // Only when the nested class item carries its own body. A bodyless
3317        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
3318        // a function definition -- is the export-macro shape recovered by the
3319        // next arm, and must fall through to it rather than abort the search.
3320        if child.kind() == "function_definition"
3321            && let Some(class_node) = first_class_like_child(child)
3322            && let Some(body) = cpp_body_node(class_node)
3323            && let Some(name) = class_like_name(class_node, source, ancestry)
3324        {
3325            let raw_supertypes =
3326                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3327                    .then(|| extract_cpp_supertypes(class_node, source));
3328            return Some(CppSentinelReparsedClass {
3329                declaration_node: class_node,
3330                name,
3331                body,
3332                raw_supertypes,
3333            });
3334        }
3335        if child.kind() == "function_definition"
3336            && let Some((_, name, raw_supertypes)) =
3337                recover_exported_class_function_definition(child, source)
3338        {
3339            let body = cpp_body_node(child)?;
3340            return Some(CppSentinelReparsedClass {
3341                declaration_node: child,
3342                name,
3343                body,
3344                raw_supertypes,
3345            });
3346        }
3347    }
3348    None
3349}
3350
3351fn recovered_postfix_export_macro_base(
3352    node: Node<'_>,
3353    type_node: Node<'_>,
3354    declarator: Node<'_>,
3355    source: &str,
3356) -> Option<String> {
3357    let mut cursor = node.walk();
3358    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3359        child.kind() == "ERROR"
3360            && child.start_byte() >= type_node.end_byte()
3361            && child.end_byte() <= declarator.start_byte()
3362            && postfix_export_macro_inheritance(*child, source)
3363    });
3364    malformed_clauses.next()?;
3365    if malformed_clauses.next().is_some() {
3366        return None;
3367    }
3368    recovered_malformed_base_name(declarator, source)
3369}
3370
3371fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3372    let mut macro_count = 0;
3373    let mut colon_count = 0;
3374    let mut access_count = 0;
3375    for index in 0..node.child_count() {
3376        let Some(child) = node.child(index) else {
3377            return false;
3378        };
3379        match child.kind() {
3380            "identifier" | "type_identifier" if child.is_named() => {
3381                let candidate = normalize_cpp_whitespace(node_text(child, source));
3382                if !cpp_export_macro_token(&candidate) {
3383                    return false;
3384                }
3385                macro_count += 1;
3386            }
3387            ":" if !child.is_named() => colon_count += 1,
3388            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3389            _ => return false,
3390        }
3391    }
3392    macro_count == 1 && colon_count == 1 && access_count == 1
3393}
3394
3395fn recovered_single_base_after_declarator(
3396    node: Node<'_>,
3397    declarator: Node<'_>,
3398    source: &str,
3399) -> Option<String> {
3400    let body_start = node
3401        .child_by_field_name("body")
3402        .map(|body| body.start_byte())
3403        .unwrap_or(node.end_byte());
3404    let mut cursor = node.walk();
3405    let mut bases = node
3406        .named_children(&mut cursor)
3407        .filter(|child| {
3408            child.kind() == "ERROR"
3409                && child.start_byte() >= declarator.end_byte()
3410                && child.end_byte() <= body_start
3411        })
3412        .filter_map(|error| displaced_exported_class_name(error, source));
3413    let base = bases.next()?;
3414    bases.next().is_none().then_some(base)
3415}
3416
3417fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3418    (0..node.child_count()).any(|index| {
3419        node.child(index)
3420            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
3421    })
3422}
3423
3424pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3425    recover_exported_class_function_definition(node, source).is_some()
3426}
3427
3428fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3429    matches!(
3430        kind,
3431        "ERROR"
3432            | "preproc_if"
3433            | "preproc_ifdef"
3434            | "preproc_ifndef"
3435            | "preproc_else"
3436            | "preproc_elif"
3437    ) || (kind == "labeled_statement" && in_class_scope)
3438}
3439
3440pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3441    if node.kind() != "declaration" {
3442        return false;
3443    }
3444    let mut ancestor = node.parent();
3445    while let Some(container) = ancestor {
3446        match container.kind() {
3447            "compound_statement" => {
3448                return container.parent().is_some_and(|class_container| {
3449                    is_recovered_exported_class_container(class_container, source)
3450                });
3451            }
3452            // These containers preserve ScopeInfo in visit_node. declaration_list is
3453            // the body container selected for a linkage specification.
3454            "template_declaration" | "linkage_specification" | "declaration_list" => {}
3455            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3456            _ => return false,
3457        }
3458        ancestor = container.parent();
3459    }
3460    false
3461}
3462
3463pub fn recovered_exported_class_has_body(
3464    node: Node<'_>,
3465    source: &str,
3466    expected_name: &str,
3467) -> Option<bool> {
3468    match node.kind() {
3469        "function_definition" => {
3470            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
3471            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3472        }
3473        "declaration" | "field_declaration" => {
3474            let recovered = recover_exported_class_declaration(node, source)?;
3475            (recovered.name == expected_name).then(|| recovered.body.is_some())
3476        }
3477        _ => None,
3478    }
3479}
3480
3481fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3482    let body_start = node
3483        .child_by_field_name("body")
3484        .map(|body| body.start_byte())
3485        .unwrap_or(node.end_byte());
3486    let mut stack = Vec::new();
3487    for index in (0..node.named_child_count()).rev() {
3488        let Some(child) = node.named_child(index) else {
3489            continue;
3490        };
3491        if child.start_byte() >= body_start {
3492            continue;
3493        }
3494        stack.push(child);
3495    }
3496
3497    let mut best = None;
3498    while let Some(current) = stack.pop() {
3499        if matches!(current.kind(), "identifier" | "type_identifier") {
3500            let name = normalize_cpp_whitespace(node_text(current, source));
3501            if !name.is_empty()
3502                && !cpp_export_macro_token(&name)
3503                && !matches!(name.as_str(), "class" | "struct" | "union")
3504            {
3505                best = Some(name);
3506            }
3507            continue;
3508        }
3509
3510        for index in (0..current.named_child_count()).rev() {
3511            if let Some(child) = current.named_child(index)
3512                && child.start_byte() < body_start
3513            {
3514                stack.push(child);
3515            }
3516        }
3517    }
3518    best
3519}
3520
3521fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3522    if node.kind() == "declaration"
3523        && node
3524            .child_by_field_name("type")
3525            .or_else(|| first_class_like_child(node))
3526            .is_some_and(|type_node| {
3527                matches!(
3528                    type_node.kind(),
3529                    "class_specifier" | "struct_specifier" | "union_specifier"
3530                )
3531            })
3532        && let Some(name) = node
3533            .child_by_field_name("declarator")
3534            .and_then(|declarator| declarator_name_from_node(declarator, source))
3535        && !cpp_export_macro_token(&name)
3536    {
3537        return Some(name);
3538    }
3539
3540    if node.kind() == "function_definition"
3541        && node.child_by_field_name("type").is_some_and(|type_node| {
3542            matches!(
3543                type_node.kind(),
3544                "class_specifier" | "struct_specifier" | "union_specifier"
3545            )
3546        })
3547        && let Some(name) = node
3548            .child_by_field_name("declarator")
3549            .and_then(|declarator| direct_identifier_name(declarator, source))
3550        && !cpp_export_macro_token(&name)
3551    {
3552        return Some(name);
3553    }
3554
3555    let class_node = if matches!(
3556        node.kind(),
3557        "class_specifier" | "struct_specifier" | "union_specifier"
3558    ) {
3559        node
3560    } else {
3561        first_class_like_child(node)?
3562    };
3563    class_like_name_from_children(class_node, source)
3564}
3565
3566fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3567    if !matches!(
3568        node.kind(),
3569        "identifier" | "field_identifier" | "type_identifier"
3570    ) {
3571        return None;
3572    }
3573    let name = normalize_cpp_whitespace(node_text(node, source));
3574    (!name.is_empty()).then_some(name)
3575}
3576
3577fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3578    match node.kind() {
3579        "identifier" | "field_identifier" | "type_identifier" => {
3580            let name = normalize_cpp_whitespace(node_text(node, source));
3581            (!name.is_empty()).then_some(name)
3582        }
3583        _ => {
3584            let mut cursor = node.walk();
3585            node.named_children(&mut cursor)
3586                .find_map(|child| declarator_name_from_node(child, source))
3587        }
3588    }
3589}
3590
3591fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3592    let mut cursor = node.walk();
3593    node.named_children(&mut cursor).find(|child| {
3594        matches!(
3595            child.kind(),
3596            "class_specifier" | "struct_specifier" | "union_specifier"
3597        )
3598    })
3599}
3600
3601/// Push a container's children as a `Siblings` cursor rather than snapshotting
3602/// them all with one shared scope: children are visited one at a time so a
3603/// `using namespace X;` sibling can affect the scope threaded to the siblings
3604/// that textually follow it (issue #1093).
3605fn push_cpp_container_work<'tree>(
3606    node: Node<'tree>,
3607    scope: ScopeInfo,
3608    stack: &mut Vec<CppWork<'tree>>,
3609) {
3610    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3611}
3612
3613/// Materialize one selected named-child range with a tree-sitter cursor. The
3614/// cursor advances linearly across the parent's concrete children; repeatedly
3615/// asking for `named_child(index)` is quadratic on very wide generated nodes.
3616fn push_cpp_sibling_range<'tree>(
3617    parent: Node<'tree>,
3618    start_index: usize,
3619    end_index: usize,
3620    scope: ScopeInfo,
3621    stack: &mut Vec<CppWork<'tree>>,
3622) {
3623    let mut cursor = parent.walk();
3624    let children = parent
3625        .named_children(&mut cursor)
3626        .skip(start_index)
3627        .take(end_index.saturating_sub(start_index))
3628        .collect::<Vec<_>>()
3629        .into_iter();
3630    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3631}
3632
3633/// Advance a `Siblings` cursor by one child: dispatch the current child under
3634/// the scope accumulated from its *earlier* siblings, then push a
3635/// continuation for the remaining siblings carrying the scope updated for
3636/// *this* child (only `using namespace X;` directives change it). Pushing the
3637/// continuation before the current child's own node work means the current
3638/// child's subtree fully drains (LIFO) before the next sibling is visited,
3639/// preserving left-to-right order.
3640fn advance_cpp_siblings<'tree>(
3641    mut siblings: CppSiblingsWork<'tree>,
3642    source: &str,
3643    stack: &mut Vec<CppWork<'tree>>,
3644) {
3645    let Some(child) = siblings.children.next() else {
3646        return;
3647    };
3648    let current_scope = siblings.scope.clone();
3649    if let Some(namespace) = cpp_using_namespace_target(child, source) {
3650        siblings.scope.visible_using_namespaces.push(namespace);
3651    }
3652    if !siblings.children.as_slice().is_empty() {
3653        stack.push(CppWork::Siblings(siblings));
3654    }
3655    stack.push(CppWork::Node(CppNodeWork {
3656        node: child,
3657        scope: current_scope,
3658    }));
3659}
3660
3661/// The namespace target of a `using namespace X;` directive, or `None` for
3662/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
3663/// kind. Distinguished structurally by the presence of the grammar's literal
3664/// `namespace` keyword token among the node's children -- not by inspecting
3665/// source text -- so it never misreads a member-importing using-declaration
3666/// as a namespace directive.
3667fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3668    if node.kind() != "using_declaration" {
3669        return None;
3670    }
3671    let mut cursor = node.walk();
3672    let is_namespace_directive = node
3673        .children(&mut cursor)
3674        .any(|child| child.kind() == "namespace");
3675    if !is_namespace_directive {
3676        return None;
3677    }
3678    let target = node.named_child(0)?;
3679    // A leading `::` is the explicit-global marker, not part of the namespace
3680    // path (`using namespace ::std::chrono;`). Drop that AST token before
3681    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
3682    // keeps: storing the marker verbatim desynced the legacy package string from
3683    // the FqName bridge, which splits on `::` and drops the empty leading
3684    // component, tripping the package/short boundary assert when a bare-owner
3685    // out-of-line definition borrowed the directive's namespace (#1093 path).
3686    let start = target
3687        .child(0)
3688        .filter(|child| !child.is_named() && child.kind() == "::")
3689        .map_or(target.start_byte(), |marker| marker.end_byte());
3690    let text = normalize_cpp_whitespace(
3691        source
3692            .get(start..target.end_byte())
3693            .expect("using-directive target covers one source range"),
3694    );
3695    (!text.is_empty()).then_some(text)
3696}
3697
3698/// Every `using namespace X;` directive target in a file, in source order, for
3699/// resolution-time consumers that need the file's using-directives without the
3700/// per-position scope threading extraction does. Parses `source` fresh and
3701/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
3702/// keys on the grammar's `namespace` keyword token, not source text), so it
3703/// never misreads a member-importing `using X::Y;` as a namespace directive.
3704///
3705/// This is a whole-file over-approximation of what is in scope at any one point
3706/// (a directive nested inside a `namespace {}` block or a function body is still
3707/// reported), which is exactly what the #1134 identity reconciler wants: extra
3708/// candidate namespaces that no visible class confirms are harmless, and two
3709/// that both confirm are treated as a genuine ambiguity by the reconciler.
3710pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3711    let mut parser = Parser::new();
3712    if parser
3713        .set_language(&tree_sitter_cpp::LANGUAGE.into())
3714        .is_err()
3715    {
3716        return Vec::new();
3717    }
3718    let Some(tree) = parser.parse(source, None) else {
3719        return Vec::new();
3720    };
3721    let mut namespaces = Vec::new();
3722    let mut seen = std::collections::HashSet::new();
3723    let mut stack = vec![tree.root_node()];
3724    while let Some(node) = stack.pop() {
3725        if let Some(namespace) = cpp_using_namespace_target(node, source)
3726            && seen.insert(namespace.clone())
3727        {
3728            namespaces.push(namespace);
3729        }
3730        let mut cursor = node.walk();
3731        stack.extend(node.named_children(&mut cursor));
3732    }
3733    namespaces
3734}
3735
3736pub struct CppVisitor<'a> {
3737    pub file: &'a ProjectFile,
3738    pub source: &'a str,
3739    pub parsed: &'a mut ParsedFile,
3740    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
3741    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
3742    ///
3743    /// C has no nested tag scope: a struct/union/enum tag declared inside
3744    /// another aggregate's member list has the scope of the outer declaration
3745    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
3746    /// therefore declares a file-scope `inner` that a later file-scope
3747    /// `struct inner *p;` legitimately references, where C++ would make the
3748    /// same shape a nested class `outer::inner`. Headers carry no compilation
3749    /// language of their own and keep the conservative C++ interpretation.
3750    pub c_tag_semantics: bool,
3751    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3752    /// Byte regions whose contents were re-owned by a fragmented export-class
3753    /// recovery (#938): the scattered members between the fragmented
3754    /// declaration and its displaced closing brace are indexed as members of
3755    /// the recovered class by the region reparse, so the ordinary sibling walk
3756    /// must not ALSO index them as top-level declarations (that double-indexing
3757    /// made a scattered nested class ambiguous between `Inner` and
3758    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
3759    /// linear scan at visit time is fine.
3760    pub consumed_fragment_regions: Vec<(usize, usize)>,
3761    /// Lexical namespace corrections and brace boundaries for declarations
3762    /// displaced by parse recovery (issues #1537 and #3087). The parsed
3763    /// ancestors can stop short or extend past their true closes; see
3764    /// [`CppVisitor::recovered_namespace_scope`].
3765    pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
3766    /// Owned reparses waiting for the outer work loop. Partitioning another
3767    /// swallowed class in a tail must not grow the Rust call stack.
3768    pub partitioned_regions: Vec<(Tree, std::ops::Range<usize>, ScopeInfo)>,
3769    /// The namespace forward declarations already folded out of each tree this
3770    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
3771    /// Empty until the first question, which the overwhelming majority of files
3772    /// never ask.
3773    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3774    /// Which owners already have field declarations in the parse product, as
3775    /// [`CppVisitor::has_enum_enumerator_units`] needs to know. `None` until
3776    /// the first enum asks, which most files never do (#2786).
3777    pub field_owners: Option<CppFieldOwnerIndex>,
3778    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
3779    /// happen to the declaration set, innermost last. Empty outside a recovery
3780    /// reparse, which is almost always (#2787).
3781    pub recovery_captures: Vec<CppRecoveryCapture>,
3782    /// Object-like field-list macros defined earlier in this source. Their
3783    /// replacements are parsed structurally and materialized under each
3784    /// invoking aggregate; no source-text expansion is used.
3785    pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
3786    /// Names whose active replacement is not a unique structured field list.
3787    /// A later `#undef` resets the ambiguity; another `#define` does not.
3788    pub ambiguous_object_macro_fields: HashSet<String>,
3789}
3790
3791/// One source-order event from an object-like field-list macro environment.
3792///
3793/// The event stream lets an include-closure consumer preserve preprocessor
3794/// invalidation without guessing which parsed header happened to be visited
3795/// first. A conservative consumer may permanently block a name after an
3796/// `undef` when conditional include order is not provable.
3797#[derive(Clone, Debug, PartialEq, Eq)]
3798pub enum ObjectMacroFieldEvent {
3799    Define {
3800        name: String,
3801        replacement: ObjectMacroReplacement,
3802        conditional: bool,
3803    },
3804    Undef {
3805        name: String,
3806        conditional: bool,
3807    },
3808}
3809
3810/// Collect object-like field-list macros from a parsed source in source order.
3811/// Macros with conflicting active replacements are omitted so an include
3812/// closure cannot silently choose one guarded definition over another.
3813pub fn collect_cpp_object_macro_fields<'tree>(
3814    root: Node<'tree>,
3815    source: &str,
3816) -> HashMap<String, ObjectMacroReplacement> {
3817    let mut fields = HashMap::default();
3818    let mut ambiguous = HashSet::default();
3819    for event in collect_cpp_object_macro_field_events(root, source) {
3820        match event {
3821            ObjectMacroFieldEvent::Define {
3822                name,
3823                replacement: value,
3824                conditional,
3825            } => {
3826                if value.is_empty() {
3827                    fields.remove(&name);
3828                    if conditional {
3829                        ambiguous.insert(name);
3830                    } else {
3831                        ambiguous.remove(&name);
3832                    }
3833                } else if ambiguous.contains(&name) {
3834                    // Keep the name blocked until an explicit undef resets it.
3835                } else if let Some(previous) = fields.get(&name) {
3836                    if previous != &value {
3837                        fields.remove(&name);
3838                        ambiguous.insert(name);
3839                    }
3840                } else {
3841                    fields.insert(name, value);
3842                }
3843            }
3844            ObjectMacroFieldEvent::Undef { name, conditional } => {
3845                fields.remove(&name);
3846                if conditional {
3847                    ambiguous.insert(name);
3848                } else {
3849                    ambiguous.remove(&name);
3850                }
3851            }
3852        }
3853    }
3854    fields
3855}
3856
3857/// Collect object-like field-list macro events in source order. Conditional
3858/// branches remain visible in the event stream: callers that cannot prove
3859/// branch selection can invalidate names conservatively instead of inventing
3860/// a replacement from one branch.
3861pub fn collect_cpp_object_macro_field_events<'tree>(
3862    root: Node<'tree>,
3863    source: &str,
3864) -> Vec<ObjectMacroFieldEvent> {
3865    let mut events = Vec::new();
3866    let mut stack = vec![root];
3867    while let Some(node) = stack.pop() {
3868        if node.kind() == "preproc_def"
3869            && let Some(name) = extract_macro_name(node, source)
3870        {
3871            let replacement = object_macro_replacement_of(node, source);
3872            events.push(ObjectMacroFieldEvent::Define {
3873                name,
3874                replacement,
3875                conditional: inside_preprocessor_conditional(node),
3876            });
3877        } else if is_cpp_undef_directive(node, source)
3878            && let Some(argument) = node.child_by_field_name("argument")
3879        {
3880            events.push(ObjectMacroFieldEvent::Undef {
3881                name: node_text(argument, source).trim().to_string(),
3882                conditional: inside_preprocessor_conditional(node),
3883            });
3884        }
3885        let mut cursor = node.walk();
3886        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3887        stack.extend(children.into_iter().rev());
3888    }
3889    events
3890}
3891
3892fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
3893    let mut current = node.parent();
3894    while let Some(parent) = current {
3895        if matches!(
3896            parent.kind(),
3897            "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
3898        ) {
3899            return true;
3900        }
3901        current = parent.parent();
3902    }
3903    false
3904}
3905
3906fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
3907    node.kind() == "preproc_call"
3908        && node
3909            .child_by_field_name("directive")
3910            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
3911}
3912
3913impl<'a> CppVisitor<'a> {
3914    /// Records `code_unit` with the answers the walk carries forward, then adds
3915    /// it to the parse product.
3916    ///
3917    /// Every declaration this walk publishes goes through this family, so the
3918    /// carried-forward answers see each one exactly once: the field ownership
3919    /// index behind [`Self::has_enum_enumerator_units`] (#2786) and the minted
3920    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
3921    fn add_declaration(
3922        &mut self,
3923        code_unit: CodeUnit,
3924        node: Node<'_>,
3925        parent: Option<CodeUnit>,
3926        top_level: Option<CodeUnit>,
3927    ) {
3928        self.note_declaration(&code_unit);
3929        let source = self.source;
3930        self.parsed
3931            .add_code_unit(code_unit, node, source, parent, top_level);
3932    }
3933
3934    /// Range-based form of [`Self::add_declaration`].
3935    fn add_declaration_with_range(
3936        &mut self,
3937        code_unit: CodeUnit,
3938        range: Range,
3939        parent: Option<CodeUnit>,
3940        top_level: Option<CodeUnit>,
3941    ) {
3942        self.note_declaration(&code_unit);
3943        self.parsed
3944            .add_code_unit_with_range(code_unit, range, parent, top_level);
3945    }
3946
3947    /// Deferred-replacement form of [`Self::add_declaration`].
3948    fn replace_declaration_deferred(
3949        &mut self,
3950        code_unit: CodeUnit,
3951        node: Node<'_>,
3952        parent: Option<CodeUnit>,
3953        top_level: Option<CodeUnit>,
3954    ) {
3955        self.note_replaced_declaration(&code_unit);
3956        let source = self.source;
3957        self.parsed
3958            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
3959    }
3960
3961    /// Range-based form of [`Self::replace_declaration_deferred`].
3962    fn replace_declaration_with_range_deferred(
3963        &mut self,
3964        code_unit: CodeUnit,
3965        range: Range,
3966        parent: Option<CodeUnit>,
3967        top_level: Option<CodeUnit>,
3968    ) {
3969        self.note_replaced_declaration(&code_unit);
3970        self.parsed
3971            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
3972    }
3973
3974    /// Notes one declaration about to enter the parse product.
3975    ///
3976    /// A declaration the product already holds is not a creation, so an open
3977    /// recovery capture ignores it -- which is the membership test the set
3978    /// difference it replaces performed. A creation inside a nested recovery
3979    /// belongs to the recoveries around it too, so every open capture takes it.
3980    fn note_declaration(&mut self, code_unit: &CodeUnit) {
3981        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
3982            for capture in &mut self.recovery_captures {
3983                if capture.removed_pre_existing.contains(code_unit) {
3984                    continue;
3985                }
3986                if capture.created_units.insert(code_unit.clone()) {
3987                    capture.created.push(code_unit.clone());
3988                }
3989            }
3990        }
3991        if let Some(field_owners) = self.field_owners.as_mut() {
3992            field_owners.record(code_unit, self.file);
3993        }
3994    }
3995
3996    /// Notes one declaration about to replace an existing one.
3997    ///
3998    /// A deferred replacement of a declaration that already owns children
3999    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
4000    /// a removal is the one thing the field index cannot absorb by addition.
4001    /// Drop it; the next question rebuilds it from the declarations that
4002    /// survive. A replacement of a unit with no children, and a "replacement"
4003    /// of a unit that is not there at all, remove nothing.
4004    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
4005        let removes_children = self.parsed.contains_declaration(code_unit)
4006            && self
4007                .parsed
4008                .children
4009                .get(code_unit)
4010                .is_some_and(|children| !children.is_empty());
4011        if removes_children {
4012            if !self.recovery_captures.is_empty() {
4013                let removed = self.declarations_a_replacement_removes(code_unit);
4014                for capture in &mut self.recovery_captures {
4015                    for unit in &removed {
4016                        // A declaration this capture watched being created is
4017                        // its own; one it did not is a declaration that was
4018                        // already there when the capture opened, so creating it
4019                        // again is a restoration and not a mint.
4020                        if !capture.created_units.contains(unit) {
4021                            capture.removed_pre_existing.insert(unit.clone());
4022                        }
4023                    }
4024                }
4025            }
4026            self.field_owners = None;
4027        }
4028        self.note_declaration(code_unit);
4029    }
4030
4031    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
4032    /// when `code_unit` is replaced: its children, transitively.
4033    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
4034        let mut removed = Vec::new();
4035        let mut seen = HashSet::default();
4036        let mut pending: Vec<CodeUnit> = self
4037            .parsed
4038            .children
4039            .get(code_unit)
4040            .cloned()
4041            .unwrap_or_default();
4042        while let Some(unit) = pending.pop() {
4043            if !seen.insert(unit.clone()) {
4044                continue;
4045            }
4046            if let Some(children) = self.parsed.children.get(&unit) {
4047                pending.extend(children.iter().cloned());
4048            }
4049            removed.push(unit);
4050        }
4051        removed
4052    }
4053
4054    fn visit_function_like_export_class_pair<'tree>(
4055        &mut self,
4056        node: Node<'tree>,
4057        scope: &ScopeInfo,
4058        stack: &mut Vec<CppWork<'tree>>,
4059        ancestry: &ParentIndex<'tree>,
4060    ) -> bool {
4061        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
4062            return false;
4063        };
4064        let member_outcome = self
4065            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
4066        // A malformed class body can escape into several following siblings
4067        // before the next export-macro class head appears. Inspect siblings in
4068        // order and stop at the first envelope that contains such a head. One
4069        // envelope can contain several following classes, all recovered in a
4070        // single bounded traversal.
4071        let mut displaced = node.next_named_sibling();
4072        while let Some(candidate) = displaced {
4073            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
4074                break;
4075            }
4076            displaced = candidate.next_named_sibling();
4077        }
4078        let class_unit = self.visit_named_class_like_shape(
4079            node,
4080            recovered.name,
4081            // The adjacent initializer_list proves the class body envelope,
4082            // but its children are expression-shaped rather than declaration-
4083            // preserving. Index the class identity here; callable definitions
4084            // remain available from their ordinary out-of-line declarations.
4085            None,
4086            true,
4087            Some(recovered.range),
4088            recovered.raw_supertypes,
4089            scope,
4090            stack,
4091            ancestry,
4092        );
4093        self.parsed
4094            .record_materialization(MaterializationRecord::RecoveredDeclaration {
4095                recovery: recovered.range,
4096                unit: class_unit.clone(),
4097            });
4098        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4099            && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4100                tree.root_node(),
4101                class_unit.identifier(),
4102                self.source,
4103            )
4104        {
4105            self.visit_recovered_fragment_constructor(
4106                range,
4107                body,
4108                node,
4109                &class_unit,
4110                scope,
4111                ancestry,
4112            );
4113        }
4114        if let Some(outcome) = member_outcome {
4115            self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4116        }
4117        self.consumed_fragment_regions
4118            .push((node.start_byte(), recovered.range.end_byte));
4119        true
4120    }
4121
4122    fn visit_embedded_function_like_export_classes<'tree>(
4123        &mut self,
4124        node: Node<'tree>,
4125        scope: &ScopeInfo,
4126        stack: &mut Vec<CppWork<'tree>>,
4127        ancestry: &ParentIndex<'tree>,
4128    ) -> bool {
4129        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
4130        let found = !recovered_classes.is_empty();
4131        for recovered in recovered_classes {
4132            let member_outcome = self.reparse_fragmented_export_class_members(
4133                &recovered.fragmented_body,
4134                &recovered.name,
4135            );
4136            let class_unit = self.visit_named_class_like_shape(
4137                node,
4138                recovered.name,
4139                None,
4140                true,
4141                Some(recovered.range),
4142                Some(recovered.raw_supertypes),
4143                scope,
4144                stack,
4145                ancestry,
4146            );
4147            self.parsed
4148                .record_materialization(MaterializationRecord::RecoveredDeclaration {
4149                    recovery: recovered.range,
4150                    unit: class_unit.clone(),
4151                });
4152            if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4153                && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4154                    tree.root_node(),
4155                    class_unit.identifier(),
4156                    self.source,
4157                )
4158            {
4159                self.visit_recovered_fragment_constructor(
4160                    range,
4161                    body,
4162                    node,
4163                    &class_unit,
4164                    scope,
4165                    ancestry,
4166                );
4167            }
4168            if let Some(outcome) = member_outcome {
4169                self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4170            }
4171        }
4172        found
4173    }
4174
4175    /// Walk `node`'s container, answering every ancestor question from
4176    /// `ancestry`.
4177    ///
4178    /// `ancestry` must index the tree `node` belongs to. The caller owns it
4179    /// because one tree can be walked more than once -- a header's C and C++
4180    /// readings are the same tree under different tag semantics -- and the
4181    /// parent relation is a property of the tree, not of the reading.
4182    #[allow(clippy::too_many_arguments)]
4183    pub fn visit_container<'tree>(
4184        &mut self,
4185        node: Node<'tree>,
4186        ancestry: &ParentIndex<'tree>,
4187        package_name: &str,
4188        module: Option<CodeUnit>,
4189        class_unit: Option<CodeUnit>,
4190        template_signature: Option<String>,
4191        visible_using_namespaces: Vec<String>,
4192    ) {
4193        let scope = ScopeInfo {
4194            package_name: package_name.to_string(),
4195            module,
4196            class_unit,
4197            template_signature,
4198            template_metadata: None,
4199            declarations_are_fields: false,
4200            recovered_specialization_member_scope: false,
4201            visible_using_namespaces,
4202        };
4203        // The work loop dispatches a container's children, never the container
4204        // itself, so an `ERROR` container that is the seed does not reach the
4205        // ERROR arm of `visit_node`. A header whose first collapsed aggregate
4206        // breaks the parse leaves the whole translation unit as that seed
4207        // (libuv's `include/uv.h`, issue #2985), which is exactly the shape the
4208        // macro-error-class recovery reads.
4209        if node.is_error() {
4210            self.visit_object_macro_error_classes(node, &scope);
4211        }
4212        self.run_container_work(node, scope, ancestry);
4213        while let Some((tree, range, scope)) = self.partitioned_regions.pop() {
4214            let root = tree.root_node();
4215            let container = root
4216                .descendant_for_byte_range(range.start, range.end)
4217                .expect("the queued container belongs to this tree");
4218            assert_eq!(container.byte_range(), range);
4219            self.run_container_work(container, scope, &ParentIndex::new(root));
4220        }
4221    }
4222
4223    /// Whether a work node lies entirely inside a byte region consumed by a
4224    /// fragmented export-class recovery (#938); such nodes were already indexed
4225    /// as members of the recovered class by the region reparse.
4226    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
4227        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
4228    }
4229
4230    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
4231    /// candidate a recovery is about to reparse but has not yet turned into a
4232    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
4233    /// prior structured recovery.
4234    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
4235        self.consumed_fragment_regions
4236            .iter()
4237            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
4238    }
4239
4240    /// Drive the container work loop from an explicit seed scope to completion. The
4241    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
4242    /// stays alive for the whole traversal.
4243    ///
4244    /// Every ancestor question this walk asks is answered from `ancestry`, which
4245    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
4246    /// node's position in the tree, which made a generated header with thousands
4247    /// of top-level declarations quadratic (#2361). The index is the caller's
4248    /// because it outlives any one walk: the file's tree is walked twice when a
4249    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
4250    /// builds its own index for its own tree.
4251    fn run_container_work<'tree>(
4252        &mut self,
4253        node: Node<'tree>,
4254        scope: ScopeInfo,
4255        ancestry: &ParentIndex<'tree>,
4256    ) {
4257        self.drain_cpp_work(
4258            vec![CppWork::Container(CppContainer { node, scope })],
4259            ancestry,
4260        );
4261    }
4262
4263    /// The work loop itself, from whatever seed the caller built.
4264    ///
4265    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
4266    /// that must walk only part of a reparsed tree seeds it with the sibling
4267    /// range it may walk instead, which keeps the `using namespace X;` scope
4268    /// accumulation `advance_cpp_siblings` performs.
4269    fn drain_cpp_work<'tree>(
4270        &mut self,
4271        mut stack: Vec<CppWork<'tree>>,
4272        ancestry: &ParentIndex<'tree>,
4273    ) {
4274        while let Some(work) = stack.pop() {
4275            match work {
4276                CppWork::Container(container) => {
4277                    push_cpp_container_work(container.node, container.scope, &mut stack);
4278                }
4279                CppWork::Siblings(siblings) => {
4280                    advance_cpp_siblings(siblings, self.source, &mut stack);
4281                }
4282                CppWork::Node(work) => {
4283                    if self.node_is_inside_consumed_fragment(work.node) {
4284                        continue;
4285                    }
4286                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
4287                }
4288            }
4289        }
4290    }
4291
4292    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
4293    /// it only when the entire region is member-shaped. This validation must happen
4294    /// before registering the recovered class because a rejected speculative range
4295    /// must not leak into the ordinary recovery path.
4296    fn reparse_fragmented_export_class_members(
4297        &self,
4298        fragmented: &FragmentedExportBody,
4299        class_name: &str,
4300    ) -> Option<FragmentedExportMembers> {
4301        if fragmented.reparse_start >= fragmented.reparse_end {
4302            return None;
4303        }
4304        let tree = cpp_reparse_fragmented_class_body(
4305            self.source,
4306            fragmented.reparse_start,
4307            fragmented.reparse_end,
4308        )?;
4309        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
4310            return Some(FragmentedExportMembers::Complete(tree));
4311        }
4312        let has_conditional_constructor = {
4313            let root = tree.root_node();
4314            let mut cursor = root.walk();
4315            root.named_children(&mut cursor).any(|child| {
4316                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
4317            })
4318        };
4319        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
4320    }
4321
4322    /// Index an already validated fragmented body as members of `class_unit`. The
4323    /// region reparse keeps each member's exact original byte and line positions.
4324    fn visit_fragmented_export_class_members(
4325        &mut self,
4326        outcome: FragmentedExportMembers,
4327        class_unit: CodeUnit,
4328        scope: &ScopeInfo,
4329    ) -> bool {
4330        let (tree, complete) = match outcome {
4331            FragmentedExportMembers::Complete(tree) => (tree, true),
4332            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
4333        };
4334        let root = tree.root_node();
4335        let class_name = class_unit.identifier().to_string();
4336        let member_scope = ScopeInfo {
4337            // A recovered export-macro class may borrow its namespace from an
4338            // earlier forward declaration even when the malformed node itself
4339            // sits at file scope. Use the recovered class identity as the
4340            // authoritative package for reparsed members as well.
4341            package_name: class_unit.package_name().to_string(),
4342            module: scope.module.clone(),
4343            class_unit: Some(class_unit),
4344            template_signature: scope.template_signature.clone(),
4345            template_metadata: None,
4346            declarations_are_fields: true,
4347            recovered_specialization_member_scope: false,
4348            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4349        };
4350        if !complete {
4351            // A conditional beginning immediately after an access label can
4352            // fragment one constructor declaration while leaving the rest of
4353            // the class body as unsafe statement soup. Recover only that
4354            // structurally proven constructor and leave the outer-tree
4355            // siblings unconsumed for their ordinary walk.
4356            let mut cursor = root.walk();
4357            let constructors = root
4358                .named_children(&mut cursor)
4359                .filter_map(|child| {
4360                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
4361                })
4362                .collect::<Vec<_>>();
4363            // The reparsed region is its own tree, so this drain walks it with
4364            // its own parent index.
4365            let reparsed_ancestry = ParentIndex::new(root);
4366            for constructor in constructors {
4367                let mut stack = Vec::new();
4368                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
4369                while let Some(work) = stack.pop() {
4370                    match work {
4371                        CppWork::Container(container) => {
4372                            push_cpp_container_work(container.node, container.scope, &mut stack);
4373                        }
4374                        CppWork::Siblings(siblings) => {
4375                            advance_cpp_siblings(siblings, self.source, &mut stack);
4376                        }
4377                        CppWork::Node(work) => {
4378                            self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
4379                        }
4380                    }
4381                }
4382            }
4383            return false;
4384        }
4385        // The reparsed region is its own tree, so this walk indexes it itself.
4386        self.run_container_work(root, member_scope, &ParentIndex::new(root));
4387        true
4388    }
4389
4390    fn visit_recovered_fragment_constructor<'tree>(
4391        &mut self,
4392        range: std::ops::Range<usize>,
4393        constructor_body: Node<'tree>,
4394        class_declaration: Node<'tree>,
4395        class_unit: &CodeUnit,
4396        scope: &ScopeInfo,
4397        ancestry: &ParentIndex<'tree>,
4398    ) {
4399        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
4400            return;
4401        };
4402        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
4403            tree.root_node(),
4404            range.start,
4405            class_unit.identifier(),
4406            self.source,
4407        ) else {
4408            return;
4409        };
4410        let member_scope = ScopeInfo {
4411            package_name: class_unit.package_name().to_string(),
4412            module: scope.module.clone(),
4413            class_unit: Some(class_unit.clone()),
4414            template_signature: scope.template_signature.clone(),
4415            template_metadata: None,
4416            declarations_are_fields: true,
4417            recovered_specialization_member_scope: false,
4418            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4419        };
4420        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
4421        else {
4422            return;
4423        };
4424        debug_assert_eq!(function.name, class_unit.identifier());
4425        let code_unit = function.code_unit(self.file.clone());
4426        self.add_declaration_with_range(
4427            code_unit.clone(),
4428            Range {
4429                start_byte: function_declarator.start_byte(),
4430                end_byte: constructor_body.end_byte(),
4431                start_line: function_declarator.start_position().row + 1,
4432                end_line: constructor_body.end_position().row + 1,
4433            },
4434            None,
4435            None,
4436        );
4437        self.parsed.add_signature_with_metadata(
4438            code_unit.clone(),
4439            cpp_signature_metadata(
4440                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
4441                function_declarator,
4442                self.source,
4443                ancestry,
4444            )
4445            .with_declaration_only(false)
4446            .with_callable_linkage(cpp_callable_linkage(
4447                class_declaration,
4448                self.source,
4449                ancestry,
4450            )),
4451        );
4452        self.parsed.add_child(class_unit.clone(), code_unit);
4453    }
4454
4455    fn visit_recovered_fragment_prefix_members<'tree>(
4456        &mut self,
4457        root: Node<'tree>,
4458        constructor_start: usize,
4459        class_unit: &CodeUnit,
4460        scope: &ScopeInfo,
4461        ancestry: &ParentIndex<'tree>,
4462    ) {
4463        let member_scope = ScopeInfo {
4464            package_name: class_unit.package_name().to_string(),
4465            module: scope.module.clone(),
4466            class_unit: Some(class_unit.clone()),
4467            template_signature: scope.template_signature.clone(),
4468            template_metadata: None,
4469            declarations_are_fields: true,
4470            recovered_specialization_member_scope: false,
4471            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4472        };
4473        let mut stack = vec![root];
4474        while let Some(current) = stack.pop() {
4475            if current.kind() == "comment" || current.start_byte() >= constructor_start {
4476                continue;
4477            }
4478            if current.end_byte() <= constructor_start
4479                && current.kind() != "translation_unit"
4480                && current.kind() != "labeled_statement"
4481                && current.kind() != "ERROR"
4482            {
4483                let mut work_stack = Vec::new();
4484                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
4485                while let Some(work) = work_stack.pop() {
4486                    match work {
4487                        CppWork::Container(container) => {
4488                            push_cpp_container_work(
4489                                container.node,
4490                                container.scope,
4491                                &mut work_stack,
4492                            );
4493                        }
4494                        CppWork::Siblings(siblings) => {
4495                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
4496                        }
4497                        CppWork::Node(work) => {
4498                            self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
4499                        }
4500                    }
4501                }
4502                continue;
4503            }
4504            if matches!(
4505                current.kind(),
4506                "translation_unit" | "labeled_statement" | "ERROR"
4507            ) {
4508                let mut cursor = current.walk();
4509                stack.extend(current.named_children(&mut cursor));
4510            }
4511        }
4512    }
4513
4514    fn visit_node<'tree>(
4515        &mut self,
4516        node: Node<'tree>,
4517        scope: &ScopeInfo,
4518        stack: &mut Vec<CppWork<'tree>>,
4519        ancestry: &ParentIndex<'tree>,
4520    ) {
4521        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
4522            self.visit_node(node, &recovered_scope, stack, ancestry);
4523            return;
4524        }
4525        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
4526            self.visit_node(node, &recovered_scope, stack, ancestry);
4527            return;
4528        }
4529        // Fragmented-class recovery below may consume a malformed function
4530        // envelope before the ordinary kind dispatch runs. Recover any
4531        // export-macro class embedded in that envelope first; the strict class
4532        // head/base/body predicate is independent of which later recovery owns
4533        // the surrounding parser fragment.
4534        if node.kind() == "function_definition" && node.has_error() {
4535            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4536        }
4537        if let Some(FragmentedClassRecovery {
4538            declaration_node: class_node,
4539            name,
4540            raw_supertypes,
4541            body: fragmented,
4542        }) = fragmented_class_body(node, self.source)
4543        {
4544            let displaced_namespace_items =
4545                displaced_fragment_namespace_geometry(node, self.source)
4546                    .map(|boundary| boundary.namespace_items)
4547                    .unwrap_or_default();
4548            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
4549            let mut class_stack = Vec::new();
4550            // When the full body cannot be safely reparsed, the original class
4551            // node still proves ownership for its parser-visible prefix.
4552            let parser_visible_body =
4553                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
4554                    .then(|| cpp_body_node(class_node))
4555                    .flatten();
4556            let class_unit = self.visit_named_class_like_shape(
4557                class_node,
4558                name,
4559                parser_visible_body,
4560                true,
4561                Some(fragmented.class_range),
4562                Some(raw_supertypes),
4563                scope,
4564                &mut class_stack,
4565                ancestry,
4566            );
4567            let member_scope = ScopeInfo {
4568                package_name: class_unit.package_name().to_string(),
4569                module: scope.module.clone(),
4570                class_unit: Some(class_unit.clone()),
4571                template_signature: scope.template_signature.clone(),
4572                template_metadata: None,
4573                declarations_are_fields: true,
4574                recovered_specialization_member_scope: false,
4575                visible_using_namespaces: scope.visible_using_namespaces.clone(),
4576            };
4577            let complete = outcome.is_some_and(|outcome| {
4578                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
4579            });
4580            if complete {
4581                self.consumed_fragment_regions
4582                    .push((node.start_byte(), fragmented.class_range.end_byte));
4583            } else {
4584                // A macro-constrained member can make the full body reparse
4585                // unsafe while tree-sitter still exposes later class members
4586                // as bounded siblings up to the displaced `}`/`;`. Keep the
4587                // structurally proven class/base declaration and re-own those
4588                // sibling nodes under it. They retain their original parser
4589                // nodes and exact ranges; the close boundary comes solely from
4590                // `fragmented_class_body`.
4591                // Template wrappers put the escaped members beside the
4592                // template rather than beside its malformed declaration.
4593                for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
4594                    if candidate.start_byte() >= fragmented.reparse_end {
4595                        break;
4596                    }
4597                    if cpp_fragment_sibling_is_class_member(
4598                        candidate,
4599                        fragmented.reparse_end,
4600                        self.source,
4601                    ) {
4602                        self.recovered_class_sibling_scopes
4603                            .insert(candidate.id(), member_scope.clone());
4604                    }
4605                }
4606            }
4607            for item in displaced_namespace_items {
4608                self.recovered_class_sibling_scopes
4609                    .insert(item.id(), scope.clone());
4610            }
4611            stack.extend(class_stack);
4612            return;
4613        }
4614        if self.visit_folded_aggregate(node, scope) {
4615            return;
4616        }
4617        match node.kind() {
4618            "template_declaration" => {
4619                if let Some(recovered) =
4620                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
4621                {
4622                    let mut template_scope = scope.clone();
4623                    template_scope.template_signature =
4624                        cpp_template_signature(node, recovered.declaration_node, self.source);
4625                    template_scope.template_metadata =
4626                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
4627                    let raw_supertypes =
4628                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
4629                    let mut class_stack = Vec::new();
4630                    let class_unit = self.visit_named_class_like_shape(
4631                        recovered.class_node,
4632                        recovered.name,
4633                        Some(recovered.body),
4634                        true,
4635                        Some(recovered.range),
4636                        raw_supertypes,
4637                        &template_scope,
4638                        &mut class_stack,
4639                        ancestry,
4640                    );
4641                    self.parsed.record_materialization(
4642                        MaterializationRecord::RecoveredDeclaration {
4643                            recovery: recovered.range,
4644                            unit: class_unit.clone(),
4645                        },
4646                    );
4647                    let member_scope = ScopeInfo {
4648                        package_name: template_scope.package_name.clone(),
4649                        module: template_scope.module.clone(),
4650                        class_unit: Some(class_unit.clone()),
4651                        template_signature: template_scope.template_signature.clone(),
4652                        template_metadata: None,
4653                        declarations_are_fields: true,
4654                        recovered_specialization_member_scope: recovered
4655                            .class_node
4656                            .child_by_field_name("name")
4657                            .is_some_and(|name| name.kind() == "template_type"),
4658                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
4659                    };
4660                    for tail_member in recovered.tail_members.into_iter().rev() {
4661                        stack.push(CppWork::Node(CppNodeWork {
4662                            node: tail_member,
4663                            scope: member_scope.clone(),
4664                        }));
4665                    }
4666                    stack.extend(class_stack);
4667                    for sibling in recovered.member_siblings {
4668                        self.recovered_class_sibling_scopes
4669                            .insert(sibling.id(), member_scope.clone());
4670                    }
4671                    return;
4672                }
4673                for index in (0..node.named_child_count()).rev() {
4674                    let Some(child) = node.named_child(index) else {
4675                        continue;
4676                    };
4677                    if matches!(
4678                        child.kind(),
4679                        "class_specifier"
4680                            | "struct_specifier"
4681                            | "union_specifier"
4682                            | "enum_specifier"
4683                            | "function_definition"
4684                            | "declaration"
4685                            | "field_declaration"
4686                            | "alias_declaration"
4687                            | "namespace_definition"
4688                    ) {
4689                        let mut template_scope = scope.clone();
4690                        template_scope.template_signature =
4691                            cpp_template_signature(node, child, self.source);
4692                        template_scope.template_metadata =
4693                            cpp_template_metadata(node, child, self.source, ancestry);
4694                        if let Some(recovered) = recover_fragmented_partial_specialization(
4695                            node,
4696                            child,
4697                            self.source,
4698                            ancestry,
4699                        ) {
4700                            let code_unit = self.visit_named_class_like_shape(
4701                                recovered.declaration_node,
4702                                recovered.name,
4703                                None,
4704                                true,
4705                                Some(recovered.range),
4706                                None,
4707                                &template_scope,
4708                                stack,
4709                                ancestry,
4710                            );
4711                            self.parsed.record_materialization(
4712                                MaterializationRecord::RecoveredDeclaration {
4713                                    recovery: recovered.range,
4714                                    unit: code_unit.clone(),
4715                                },
4716                            );
4717                            let mut member_scope = template_scope.clone();
4718                            member_scope.class_unit = Some(code_unit);
4719                            member_scope.declarations_are_fields = true;
4720                            member_scope.recovered_specialization_member_scope = true;
4721                            for prefix_member in recovered.prefix_members.into_iter().rev() {
4722                                stack.push(CppWork::Node(CppNodeWork {
4723                                    node: prefix_member,
4724                                    scope: member_scope.clone(),
4725                                }));
4726                            }
4727                            for sibling in recovered.member_siblings {
4728                                self.recovered_class_sibling_scopes
4729                                    .insert(sibling.id(), member_scope.clone());
4730                            }
4731                            for following in recovered.following_declarations.into_iter().rev() {
4732                                stack.push(CppWork::Node(CppNodeWork {
4733                                    node: following,
4734                                    scope: scope.clone(),
4735                                }));
4736                            }
4737                            return;
4738                        }
4739                        stack.push(CppWork::Node(CppNodeWork {
4740                            node: child,
4741                            scope: template_scope,
4742                        }));
4743                    }
4744                }
4745            }
4746            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4747            "linkage_specification" => {
4748                if let Some(body) = cpp_body_node(node) {
4749                    stack.push(CppWork::Container(CppContainer {
4750                        node: body,
4751                        scope: scope.clone(),
4752                    }));
4753                } else {
4754                    stack.push(CppWork::Container(CppContainer {
4755                        node,
4756                        scope: scope.clone(),
4757                    }));
4758                }
4759            }
4760            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4761                self.visit_class_like(node, scope, stack, ancestry)
4762            }
4763            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4764            // A bare namespace-begin sentinel can make tree-sitter promote the
4765            // wrapped declaration to an ERROR node instead of the usual bogus
4766            // function_definition envelope. Keep the recovery entry point on
4767            // the same structured path for both shapes; ordinary ERROR nodes
4768            // retain their declaration-preserving wrapper traversal when the
4769            // sentinel predicate does not match.
4770            "ERROR" => {
4771                self.visit_object_macro_error_classes(node, scope);
4772                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4773                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4774                    if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
4775                        return;
4776                    }
4777                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4778                        return;
4779                    }
4780                    self.visit_macro_swallowed_function_declarations(node, scope);
4781                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
4782                    self.visit_stranded_class_members(node, scope, ancestry);
4783                    stack.push(CppWork::Container(CppContainer {
4784                        node,
4785                        scope: scope.clone(),
4786                    }));
4787                }
4788            }
4789            "declaration" => {
4790                if node.has_error() {
4791                    // Object-like macro lines before a function-like
4792                    // export-macro class head keep the head in a `declaration`
4793                    // rather than in an `ERROR` (#2924). It is the same head
4794                    // and the same following-sibling body, so it takes the same
4795                    // recovery; without it the head's macro invocation indexes
4796                    // as a field named after the macro.
4797                    if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4798                        return;
4799                    }
4800                    self.visit_prototype_macro_declarations(node, scope);
4801                    if self.node_is_inside_consumed_fragment(node) {
4802                        // The whole declaration was a recovered K&R
4803                        // prototype macro invocation (issue #2932); the
4804                        // reparse above already indexed its Function.
4805                        // Falling through to the ordinary declaration
4806                        // visitor would additionally mint the swapped-field
4807                        // wreckage this recovery exists to replace.
4808                        return;
4809                    }
4810                }
4811                if scope.class_unit.is_some()
4812                    && scope.declarations_are_fields
4813                    && scope.recovered_specialization_member_scope
4814                    && let Some(alias_name) =
4815                        recovered_using_declaration_alias_name(node, self.source)
4816                {
4817                    self.add_type_aliases(node, scope, vec![alias_name], ancestry);
4818                } else {
4819                    self.visit_declaration(
4820                        node,
4821                        scope,
4822                        scope.declarations_are_fields,
4823                        stack,
4824                        ancestry,
4825                    )
4826                }
4827            }
4828            // A K&R prototype macro invocation with a pointer return type and a
4829            // simple argument list parses with no ERROR node at all: tree-sitter
4830            // reads `T *name _(( args ));` as a multiplication of a type by a
4831            // call (`T * name_(...)`), wrapped in an `expression_statement`
4832            // that `has_error()` only because of a `MISSING "::"` inside it
4833            // (issue #2932). Nothing else mints a declaration from an
4834            // `expression_statement` at declaration scope today, so there is
4835            // no existing behavior to preserve here if this node is not the
4836            // K&R shape; the admission gate inside
4837            // `visit_prototype_macro_declarations` (exactly one clean function
4838            // prototype spanning the recovered run) is what keeps this arm
4839            // safe on an ordinary errorful expression statement that happens
4840            // to contain nested parentheses.
4841            "expression_statement" => {
4842                if node.has_error() {
4843                    self.visit_prototype_macro_declarations(node, scope);
4844                }
4845            }
4846            "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4847            "preproc_call" => self.visit_preproc_call(node, scope),
4848            "type_definition" | "alias_declaration" => {
4849                self.visit_type_declaration(node, scope, stack, ancestry)
4850            }
4851            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4852            // `#include` is collected by `collect_cpp_includes` before the
4853            // walk, so a directive the container walk never reaches -- inside
4854            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
4855            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
4856            // include claim.
4857            "preproc_include" => {}
4858            kind if preserves_declaration_scope_through_wrapper(
4859                kind,
4860                scope.class_unit.is_some(),
4861            ) =>
4862            {
4863                // A preprocessor conditional gates every declaration inside it
4864                // on a configuration this analyzer never evaluates; record the
4865                // interval so declaration state can say so (issue #1476). The
4866                // else/elif branches are children of the `preproc_if` node, so
4867                // recording the openers covers every branch.
4868                if kind == "labeled_statement" {
4869                    self.visit_access_label_constructor(node, scope);
4870                }
4871                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4872                    let mut range = cpp_declaration_range(node);
4873                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4874                        range.end_byte = boundary.end_byte;
4875                        range.end_line = boundary.end_line;
4876                    }
4877                    self.parsed.record_materialization(
4878                        MaterializationRecord::ConfigurationConditional { range },
4879                    );
4880                    if node.has_error() {
4881                        // A malformed export-macro class can close the namespace
4882                        // node early while the enclosing include guard still owns
4883                        // the remaining class-head/body pairs. The ordinary walk
4884                        // cannot carry the lost namespace through those promoted
4885                        // siblings. Scan only structured ERROR nodes in this
4886                        // already-malformed conditional; the pair recovery's
4887                        // exact class/macro/body predicate remains the admission
4888                        // gate, and its namespace lifting restores the owner.
4889                        let mut candidates = vec![node];
4890                        while let Some(candidate) = candidates.pop() {
4891                            // An `ERROR` still inside a namespace body is one
4892                            // the ordinary walk reaches with that namespace in
4893                            // scope. Claiming it here registers the recovered
4894                            // class at file scope and the consumed region then
4895                            // suppresses the walk that would have named it
4896                            // correctly -- which is why Botan's `DL_Group` was
4897                            // `DL_Group` and not `Botan.DL_Group` in the real
4898                            // header, where an include guard wraps the
4899                            // namespace, and was right in a fixture without one
4900                            // (#2552).
4901                            if candidate.kind() == "ERROR"
4902                                && !cpp_is_inside_namespace_body(candidate, ancestry)
4903                                && self.visit_function_like_export_class_pair(
4904                                    candidate, scope, stack, ancestry,
4905                                )
4906                            {
4907                                continue;
4908                            }
4909                            for index in (0..candidate.named_child_count()).rev() {
4910                                candidates.push(
4911                                    candidate
4912                                        .named_child(index)
4913                                        .expect("index below the node's own named child count"),
4914                                );
4915                            }
4916                        }
4917                    }
4918                }
4919                stack.push(CppWork::Container(CppContainer {
4920                    node,
4921                    scope: scope.clone(),
4922                }))
4923            }
4924            // A macro invocation the parser could not group leaves its head in
4925            // whatever slot was free. The `function_definition` and `ERROR`
4926            // arms above try the same recovery on the envelopes they get; a
4927            // flattened run leaves a bare `identifier`/`type_identifier`, and a
4928            // swallowed tail can land in a `parameter_declaration` (#3094).
4929            _ => {
4930                self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
4931            }
4932        }
4933    }
4934
4935    fn visit_macro_swallowed_function_declarations<'tree>(
4936        &mut self,
4937        envelope: Node<'tree>,
4938        scope: &ScopeInfo,
4939    ) {
4940        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
4941            || envelope.kind() == "ERROR"
4942                && envelope
4943                    .parent()
4944                    .is_some_and(|parent| parent.kind() == "ERROR")
4945        {
4946            return;
4947        }
4948        let mut stack = (0..envelope.named_child_count())
4949            .filter_map(|index| envelope.named_child(index))
4950            .collect::<Vec<_>>();
4951        while let Some(node) = stack.pop() {
4952            if node.kind() == "function_declarator" {
4953                self.visit_error_swallowed_function_declaration(node, scope);
4954            }
4955            for child in named_children_iter(node) {
4956                stack.push(child);
4957            }
4958        }
4959    }
4960
4961    /// Index the declarations an attribute-like macro invocation swallowed into
4962    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
4963    /// the shape and why the parser produces it.
4964    fn visit_macro_wrapped_declarations<'tree>(
4965        &mut self,
4966        envelope: Node<'tree>,
4967        scope: &ScopeInfo,
4968        ancestry: &ParentIndex<'tree>,
4969    ) {
4970        let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
4971        if recovered.is_empty() {
4972            return;
4973        }
4974        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
4975        self.record_recovered_declarations(recovery, |visitor| {
4976            for declaration in recovered {
4977                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
4978            }
4979        });
4980    }
4981
4982    /// Index the declarations a macro invocation collapsed. See
4983    /// [`collapsed_macro_declaration_run`] for the shape and why the parser
4984    /// produces it. Returns whether it claimed the region beginning at
4985    /// `envelope`.
4986    ///
4987    /// The parser's own nodes cannot be read the way the swallowed tail of the
4988    /// one-line shape can. Of the 214 declarations whisper's `llama.h` hides in
4989    /// its envelope, 57 are shredded to bare identifier and punctuation tokens
4990    /// with no declarator left at all, and the declarators that do survive on
4991    /// the envelope's declarator spine pair one declaration's name with the
4992    /// *next* declaration's parameter list. Reading those would be a guess.
4993    ///
4994    /// The bytes are still ordinary declarations, though, and the collapse is
4995    /// the parser carrying the failure forward from one macro invocation. So
4996    /// reparse the region, walk the items the parser makes of it, and when one
4997    /// item is itself a collapsed run, recover that invocation from its own
4998    /// bytes and resume the scan just past it. Each pass starts later than the
4999    /// last, so the scan is a loop over the invocations that collapse, not over
5000    /// the declarations: `llama.h` needs three passes for 214 declarations.
5001    ///
5002    /// The region can reach past `envelope` into the siblings the parser handed
5003    /// the rest of one invocation, so it is recorded as consumed: those
5004    /// siblings are this recovery's, and the ordinary walk must not read them
5005    /// again. Recording it after the scan, not before, keeps the scan's own
5006    /// reparsed nodes -- which carry their original byte offsets -- visible to
5007    /// the walk it drives.
5008    fn visit_collapsed_macro_declaration_run<'tree>(
5009        &mut self,
5010        envelope: Node<'tree>,
5011        scope: &ScopeInfo,
5012        ancestry: &ParentIndex<'tree>,
5013    ) -> bool {
5014        let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
5015            return false;
5016        };
5017        let start = envelope.start_byte();
5018        let end = run.region_end;
5019        let recovery = cpp_recovery_window(self.source, start, end);
5020        self.record_recovered_declarations(recovery, |visitor| {
5021            let mut position = start;
5022            while position < end {
5023                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
5024                    return;
5025                };
5026                let root = tree.root_node();
5027                // A region reparse is its own tree and needs its own parent
5028                // index; the caller's answers nothing about these nodes.
5029                let ancestry = ParentIndex::new(root);
5030                let mut cursor = root.walk();
5031                let collapsed =
5032                    root.named_children(&mut cursor)
5033                        .enumerate()
5034                        .find_map(|(index, item)| {
5035                            collapsed_macro_declaration_run(item, visitor.source, &ancestry)
5036                                .map(|run| (index, item, run))
5037                        });
5038                // Walk only the items before the collapsed one. It swallowed
5039                // the rest of the region, so handing it to the ordinary walk
5040                // would re-enter this recovery on a region that starts where
5041                // this one did.
5042                let mut stack = Vec::new();
5043                push_cpp_sibling_range(
5044                    root,
5045                    0,
5046                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
5047                    scope.clone(),
5048                    &mut stack,
5049                );
5050                visitor.drain_cpp_work(stack, &ancestry);
5051                let Some((_, item, run)) = collapsed else {
5052                    return;
5053                };
5054                // On its own bytes the invocation is the declaration-scope
5055                // shape `macro_wrapped_declarations` reads, so its wrapped
5056                // declaration needs no reader of its own here.
5057                if let Some(head) =
5058                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
5059                {
5060                    let head_root = head.root_node();
5061                    visitor.run_container_work(
5062                        head_root,
5063                        scope.clone(),
5064                        &ParentIndex::new(head_root),
5065                    );
5066                }
5067                assert!(
5068                    run.invocation_end > position,
5069                    "a collapsed run at {position} must end after the byte the scan resumed \
5070                     from, but ended at {}",
5071                    run.invocation_end
5072                );
5073                position = run.invocation_end;
5074            }
5075        });
5076        self.consumed_fragment_regions.push((start, end));
5077        true
5078    }
5079
5080    /// Index the members a string-argument attribute macro stranded in an
5081    /// `ERROR` inside a class body.
5082    ///
5083    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
5084    /// grouping of the attributed member and of every member written after it
5085    /// until it recovers. The members are still whole `function_declarator`
5086    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
5087    /// export-macro class such as Botan's `DL_Group` was indexed with no
5088    /// members at all (#2552).
5089    fn visit_stranded_class_members<'tree>(
5090        &mut self,
5091        node: Node<'tree>,
5092        scope: &ScopeInfo,
5093        ancestry: &ParentIndex<'tree>,
5094    ) {
5095        if scope.class_unit.is_none() || !scope.declarations_are_fields {
5096            return;
5097        }
5098        for member in stranded_declaration_run(node, self.source).declarations {
5099            self.add_macro_wrapped_declaration(member, scope, ancestry);
5100        }
5101    }
5102
5103    /// Index the constructor an access label swallowed in a reparsed class
5104    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
5105    ///
5106    /// The declarator is recovered by reparsing from that call to the end of
5107    /// the label's own statement, which is the same offset-preserving region
5108    /// reparse every other recovery here uses, so the recovered nodes carry
5109    /// their true source positions.
5110    fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5111        let Some(class_unit) = scope.class_unit.clone() else {
5112            return;
5113        };
5114        if !scope.declarations_are_fields {
5115            return;
5116        }
5117        let class_name = class_unit.identifier().to_string();
5118        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
5119        else {
5120            return;
5121        };
5122        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
5123            return;
5124        };
5125        let root = tree.root_node();
5126        let Some(declarator) =
5127            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
5128        else {
5129            return;
5130        };
5131        let reparsed_ancestry = ParentIndex::new(root);
5132        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
5133        let range = cpp_declaration_range(definition.unwrap_or(declarator));
5134        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
5135        self.record_recovered_declarations(recovery, |visitor| {
5136            visitor.add_macro_wrapped_declaration(
5137                MacroWrappedDeclaration {
5138                    declarator,
5139                    range,
5140                    is_static: false,
5141                },
5142                scope,
5143                &reparsed_ancestry,
5144            );
5145        });
5146    }
5147
5148    fn add_macro_wrapped_declaration<'tree>(
5149        &mut self,
5150        declaration: MacroWrappedDeclaration<'tree>,
5151        scope: &ScopeInfo,
5152        ancestry: &ParentIndex<'tree>,
5153    ) {
5154        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
5155        else {
5156            return;
5157        };
5158        let code_unit =
5159            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
5160        if self.parsed.contains_declaration(&code_unit) {
5161            self.parsed
5162                .record_navigation_range(code_unit, declaration.range);
5163            return;
5164        }
5165        self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
5166        let signature = normalize_cpp_whitespace(
5167            self.source
5168                .get(declaration.range.start_byte..declaration.range.end_byte)
5169                .expect("a recovered declaration range covers one source range"),
5170        );
5171        let linkage = if declaration.is_static {
5172            CallableLinkage::Internal
5173        } else {
5174            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
5175        };
5176        // Whether this is a definition is a property of the recovered node, not
5177        // of the caller: a declarator that a `function_definition` gives a body
5178        // is a definition wherever the recovery found it.
5179        let declaration_only =
5180            cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
5181        self.parsed.add_signature_with_metadata(
5182            code_unit.clone(),
5183            cpp_signature_metadata(signature, declaration.declarator, self.source, ancestry)
5184                .with_declaration_only(declaration_only)
5185                .with_callable_linkage(linkage),
5186        );
5187        if let Some(parent) = &scope.class_unit {
5188            self.parsed.add_child(parent.clone(), code_unit);
5189        } else if let Some(module) = &scope.module {
5190            self.parsed.add_child(module.clone(), code_unit);
5191        }
5192    }
5193
5194    /// Index only anonymous aggregate declarations from an ordinary
5195    /// function body. Function bodies are otherwise outside the declaration
5196    /// walk, but their anonymous aggregate owners must exist so structured
5197    /// local binding inference can name their fields (#2994).
5198    fn visit_anonymous_local_aggregates_in_function<'tree>(
5199        &mut self,
5200        function: Node<'tree>,
5201        scope: &ScopeInfo,
5202        stack: &mut Vec<CppWork<'tree>>,
5203        ancestry: &ParentIndex<'tree>,
5204    ) {
5205        let Some(body) = cpp_body_node(function) else {
5206            return;
5207        };
5208        // A body opens a block scope, so nothing declared in it is a member of
5209        // the class the definition may be written inside. Dropping the
5210        // aggregate carrier gives an inline member function's body the same
5211        // block scope an out-of-line `void C::f()` body already has; without
5212        // it a C++ header, where member bodies are usually inline, would keep
5213        // none of these owners (#3301).
5214        let block_scope;
5215        let scope = if scope.class_unit.is_some() {
5216            block_scope = ScopeInfo {
5217                class_unit: None,
5218                declarations_are_fields: false,
5219                ..scope.clone()
5220            };
5221            &block_scope
5222        } else {
5223            scope
5224        };
5225        let mut pending = vec![body];
5226        while let Some(node) = pending.pop() {
5227            if matches!(node.kind(), "function_definition" | "lambda_expression") {
5228                continue;
5229            }
5230            if node.kind() == "declaration"
5231                && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
5232            {
5233                continue;
5234            }
5235            if matches!(
5236                node.kind(),
5237                "class_specifier" | "struct_specifier" | "union_specifier"
5238            ) {
5239                continue;
5240            }
5241            let mut cursor = node.walk();
5242            let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
5243            children.reverse();
5244            pending.extend(children);
5245        }
5246    }
5247
5248    fn visit_error_swallowed_function_declaration<'tree>(
5249        &mut self,
5250        node: Node<'tree>,
5251        scope: &ScopeInfo,
5252    ) -> bool {
5253        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
5254            return false;
5255        };
5256        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5257            return false;
5258        };
5259        let root = tree.root_node();
5260        let mut cursor = root.walk();
5261        let declarations = root
5262            .named_children(&mut cursor)
5263            .filter(|child| child.kind() != "comment")
5264            .collect::<Vec<_>>();
5265        let [declaration] = declarations.as_slice() else {
5266            return false;
5267        };
5268        if declaration.kind() != "declaration"
5269            || declaration.has_error()
5270            || declaration.start_byte() != start
5271            || declaration.end_byte() != end
5272        {
5273            return false;
5274        }
5275        let recovery = cpp_recovery_window(self.source, start, end);
5276        // The reparsed region is its own tree, so this walk indexes it itself.
5277        let reparsed_ancestry = ParentIndex::new(root);
5278        self.record_recovered_declarations(recovery, |visitor| {
5279            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5280        });
5281        true
5282    }
5283
5284    /// Recover a K&R-compatibility prototype macro invocation such as
5285    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
5286    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
5287    /// defined only in headers outside this workspace (ruby's own
5288    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
5289    /// `name _((args))` and turns each such line into a malformed
5290    /// `declaration` or pointer-expression statement. Before this
5291    /// recovery, the ordinary declaration visitor read that wreckage as a
5292    /// field with the type and name swapped (e.g. a field literally named
5293    /// `VALUE`) and indexed no prototype for the real name at all.
5294    ///
5295    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
5296    /// preserve the declared name, a known one-argument compatibility macro,
5297    /// the nested argument list, and a live terminator. It rejects arbitrary
5298    /// identifiers and bare `ERROR` envelopes that flattened those relations;
5299    /// guessing there previously turned malformed non-macro declarations into
5300    /// invented functions. For each proven candidate this reparses the range
5301    /// before the macro token, the inner parenthesized argument node, and the
5302    /// terminating `;` as one included-range parse. That is the parser-native
5303    /// equivalent of expanding `#define _(args) args` while retaining original
5304    /// byte offsets. The result is admitted only when it is exactly one clean
5305    /// function prototype spanning the recovered run.
5306    fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5307        for candidate in cpp_prototype_macro_candidates(node, self.source) {
5308            let start = candidate.run_start;
5309            let end = candidate.semicolon_end;
5310            if self.byte_range_is_inside_consumed_fragment(start, end) {
5311                continue;
5312            }
5313            let Some(tree) = parse_source_ranges_with_cancellation(
5314                &tree_sitter_cpp::LANGUAGE.into(),
5315                self.source,
5316                &candidate.ranges(),
5317                None,
5318            ) else {
5319                continue;
5320            };
5321            let root = tree.root_node();
5322            let mut cursor = root.walk();
5323            let declarations = root
5324                .named_children(&mut cursor)
5325                .filter(|child| child.kind() != "comment")
5326                .collect::<Vec<_>>();
5327            let [declaration] = declarations.as_slice() else {
5328                continue;
5329            };
5330            if declaration.kind() != "declaration"
5331                || declaration.has_error()
5332                || declaration.start_byte() != start
5333                || declaration.end_byte() != end
5334                || declaration
5335                    .child_by_field_name("declarator")
5336                    .and_then(extract_function_declarator)
5337                    .is_none()
5338            {
5339                continue;
5340            }
5341            let recovery = cpp_recovery_window(self.source, start, end);
5342            // The reparsed region is its own tree, so this walk indexes it
5343            // itself.
5344            let reparsed_ancestry = ParentIndex::new(root);
5345            self.record_recovered_declarations(recovery, |visitor| {
5346                visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5347            });
5348            self.consumed_fragment_regions.push((start, end));
5349        }
5350    }
5351
5352    /// Declare one Module per namespace level of `levels` under `package_name`,
5353    /// each recording the range of the construct that writes it, and return the
5354    /// innermost level's package name and Module. A level an earlier definition
5355    /// already declared is reused, which is also how a reopened namespace keeps
5356    /// the range of the definition that first declared it.
5357    fn declare_namespace_levels(
5358        &mut self,
5359        mut package_name: String,
5360        levels: Vec<(String, Range)>,
5361    ) -> (String, Option<CodeUnit>) {
5362        let mut module = None;
5363        for (component, range) in levels {
5364            let full_name = if package_name.is_empty() {
5365                component
5366            } else {
5367                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
5368            };
5369            let level = CodeUnit::new_fq(
5370                self.file.clone(),
5371                CodeUnitType::Module,
5372                "",
5373                full_name.clone(),
5374                cpp_namespace_fq(&full_name),
5375            );
5376            if !self.parsed.contains_declaration(&level) {
5377                self.add_declaration_with_range(level.clone(), range, None, None);
5378            }
5379            package_name = full_name;
5380            module = Some(level);
5381        }
5382        (package_name, module)
5383    }
5384
5385    /// Restore the complete lexical namespace path when recovery dropped an
5386    /// ancestor or retained it past its real close. Parsed namespaces beginning
5387    /// inside the recovered region still contribute their normal nesting.
5388    /// Crossing that boundary also clears any malformed class owner.
5389    fn recovered_namespace_scope(
5390        &mut self,
5391        node: Node<'_>,
5392        scope: &ScopeInfo,
5393    ) -> Option<ScopeInfo> {
5394        self.orphaned_namespaces.region_at(node.start_byte())?;
5395        // Each level carries the `namespace Name { ... }` construct that writes
5396        // it. Declaring them at `node` instead recorded the range of whichever
5397        // member the walk happened to reach first, so a namespace whose head
5398        // collapsed into an ERROR reported its first inner declaration's lines
5399        // as its own (#3309).
5400        let levels = self
5401            .orphaned_namespaces
5402            .enclosing_namespace_levels(node, self.source);
5403        let package_name = levels
5404            .iter()
5405            .map(|(component, _)| component.as_str())
5406            .collect::<Vec<_>>()
5407            .join(CPP_PACKAGE_SEPARATOR);
5408        if package_name == scope.package_name {
5409            return None;
5410        }
5411        let (package_name, module) = self.declare_namespace_levels(String::new(), levels);
5412        Some(ScopeInfo {
5413            package_name,
5414            module,
5415            // A recovered namespace body is no longer inside the class (or
5416            // function) that tree-sitter accidentally used as its wrapper.
5417            // Keep namespace/module context, but never carry that owner into
5418            // declarations published from the recovered region.
5419            class_unit: None,
5420            template_signature: None,
5421            template_metadata: None,
5422            declarations_are_fields: false,
5423            recovered_specialization_member_scope: false,
5424            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5425        })
5426    }
5427
5428    fn visit_namespace<'tree>(
5429        &mut self,
5430        node: Node<'tree>,
5431        scope: &ScopeInfo,
5432        stack: &mut Vec<CppWork<'tree>>,
5433        ancestry: &ParentIndex<'tree>,
5434    ) {
5435        let name_node = node.child_by_field_name("name");
5436        let Some(name_node) = name_node else {
5437            if let Some(body) = cpp_body_node(node) {
5438                stack.push(CppWork::Container(CppContainer {
5439                    node: body,
5440                    scope: scope.clone(),
5441                }));
5442            }
5443            return;
5444        };
5445        // Diagnostic corpora contain deliberately ill-formed global namespace
5446        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
5447        // the leading global `::` as the first anonymous child. Honor that AST
5448        // boundary instead of appending the name to the lexical namespace;
5449        // appending produced legacy names such as `outer::::outer::inner`, which
5450        // could not round-trip through the structured FqName boundary.
5451        let explicitly_global = name_node
5452            .child(0)
5453            .is_some_and(|child| !child.is_named() && child.kind() == "::");
5454        let components = cpp_namespace_name_components(name_node, self.source);
5455        if components.is_empty() {
5456            return;
5457        }
5458        // One Module per namespace level. C++17's `namespace a::b { ... }` is
5459        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
5460        // shorthand must declare `a` as well as `a::b` -- extracting only the
5461        // innermost level left the enclosing namespace undeclared and made the
5462        // two spellings of one construct disagree (issue #1878).
5463        let package_name = if explicitly_global {
5464            String::new()
5465        } else {
5466            scope.package_name.clone()
5467        };
5468        let definition = node_range(node);
5469        let (package_name, module) = self.declare_namespace_levels(
5470            package_name,
5471            components
5472                .into_iter()
5473                .map(|component| (component, definition))
5474                .collect(),
5475        );
5476
5477        let namespace_scope = ScopeInfo {
5478            package_name,
5479            module,
5480            // C++ never nests a namespace inside a class, so a surviving
5481            // class_unit here is always recovery bleed: a malformed-region
5482            // boundary upstream mis-scoped this namespace block. Keeping the
5483            // owner would mint the namespace's declarations as class members
5484            // under a re-appended package, desyncing the fq boundary assert
5485            // (#2306). Dropping it is identity-neutral for valid code, where
5486            // class_unit is always empty at a namespace definition.
5487            class_unit: None,
5488            template_signature: scope.template_signature.clone(),
5489            template_metadata: scope.template_metadata.clone(),
5490            declarations_are_fields: false,
5491            recovered_specialization_member_scope: false,
5492            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5493        };
5494        let container = cpp_body_node(node).unwrap_or(node);
5495        // A malformed export-macro class body may turn the following class
5496        // into a descendant of a bogus function/labeled/error envelope. Those
5497        // descendants are not declaration containers and the ordinary walk
5498        // intentionally does not descend into them. Scan the namespace tree
5499        // once for the strict embedded class geometry before scheduling its
5500        // normal declarations. When one envelope matches, its helper recovers
5501        // every embedded class and the walk need not inspect its descendants.
5502        let mut candidates = vec![container];
5503        while let Some(candidate) = candidates.pop() {
5504            if matches!(
5505                candidate.kind(),
5506                "ERROR" | "function_definition" | "labeled_statement"
5507            ) && self.visit_embedded_function_like_export_classes(
5508                candidate,
5509                &namespace_scope,
5510                stack,
5511                ancestry,
5512            ) {
5513                continue;
5514            }
5515            for index in (0..candidate.named_child_count()).rev() {
5516                candidates.push(
5517                    candidate
5518                        .named_child(index)
5519                        .expect("index below the node's own named child count"),
5520                );
5521            }
5522        }
5523        stack.push(CppWork::Container(CppContainer {
5524            node: container,
5525            scope: namespace_scope,
5526        }));
5527    }
5528
5529    fn visit_class_like<'tree>(
5530        &mut self,
5531        node: Node<'tree>,
5532        scope: &ScopeInfo,
5533        stack: &mut Vec<CppWork<'tree>>,
5534        ancestry: &ParentIndex<'tree>,
5535    ) {
5536        let Some(name) = class_like_name(node, self.source, ancestry) else {
5537            return;
5538        };
5539        let name = qualified_class_name_chain(node, self.source, scope)
5540            .map(|chain| chain.join("$"))
5541            .unwrap_or(name);
5542        self.visit_named_class_like(node, name, scope, stack, ancestry);
5543    }
5544
5545    fn visit_named_class_like<'tree>(
5546        &mut self,
5547        node: Node<'tree>,
5548        name: String,
5549        scope: &ScopeInfo,
5550        stack: &mut Vec<CppWork<'tree>>,
5551        ancestry: &ParentIndex<'tree>,
5552    ) {
5553        let body = cpp_body_node(node);
5554        let definition_body_present = body.is_some();
5555        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
5556            .then(|| extract_cpp_supertypes(node, self.source));
5557        self.visit_named_class_like_shape(
5558            node,
5559            name,
5560            body,
5561            definition_body_present,
5562            None,
5563            raw_supertypes,
5564            scope,
5565            stack,
5566            ancestry,
5567        );
5568    }
5569
5570    /// Whether this class-like declaration is a C tag that belongs to the
5571    /// enclosing non-aggregate scope rather than to the aggregate it is
5572    /// lexically written inside.
5573    ///
5574    /// `class_specifier` is deliberately excluded: `class` is not C, so text
5575    /// that spells one in a `.c` file is not C code and keeps the C++ reading
5576    /// rather than getting a half-C identity.
5577    fn mints_tag_at_enclosing_c_scope(
5578        &self,
5579        declaration_node: Node<'_>,
5580        scope: &ScopeInfo,
5581        ancestry: &ParentIndex<'_>,
5582    ) -> bool {
5583        self.c_tag_semantics
5584            && scope.class_unit.is_some()
5585            && class_like_name(declaration_node, self.source, ancestry).is_some()
5586            && matches!(
5587                declaration_node.kind(),
5588                "struct_specifier" | "union_specifier" | "enum_specifier"
5589            )
5590    }
5591
5592    #[allow(clippy::too_many_arguments)]
5593    fn visit_named_class_like_shape<'tree>(
5594        &mut self,
5595        declaration_node: Node<'tree>,
5596        name: String,
5597        body: Option<Node<'tree>>,
5598        definition_body_present: bool,
5599        explicit_range: Option<Range>,
5600        raw_supertypes: Option<Vec<String>>,
5601        scope: &ScopeInfo,
5602        stack: &mut Vec<CppWork<'tree>>,
5603        ancestry: &ParentIndex<'tree>,
5604    ) -> CodeUnit {
5605        let displaced_macro_tail = if explicit_range.is_none() {
5606            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
5607        } else {
5608            None
5609        };
5610        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
5611        let recovered_scope = self.scope_for_recovered_exported_class(
5612            declaration_node,
5613            &name,
5614            definition_body_present,
5615            scope,
5616            ancestry,
5617        );
5618        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
5619        // aggregate's member list is declared at the enclosing non-aggregate
5620        // scope, not nested inside the aggregate. `scope.class_unit` is the
5621        // only aggregate carrier in this walk, so dropping it puts the tag at
5622        // the nearest enclosing non-aggregate scope -- the module at file or
5623        // namespace scope, and the same block-scope representation a
5624        // function-local aggregate already gets. The tag's own body scope
5625        // below still owns its members, so fields and enumerators are
5626        // unaffected.
5627        let c_tag_scope;
5628        let scope =
5629            if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
5630                c_tag_scope = ScopeInfo {
5631                    class_unit: None,
5632                    ..recovered_scope.clone()
5633                };
5634                &c_tag_scope
5635            } else {
5636                &recovered_scope
5637            };
5638        let short_name = if let Some(parent) = &scope.class_unit {
5639            cpp_join_nested_short(parent.short_name(), &name)
5640        } else {
5641            name.clone()
5642        };
5643        // A top-level out-of-line qualified class definition (`struct
5644        // Outer::Inner { ... }` inside its namespace, #2246) carries its
5645        // nesting chain as the `$`-joined display name; push one Type/Nested
5646        // segment per class so segment-pop owner navigation keeps working.
5647        // Every other leaf name stays opaque so a literal `$` in a source
5648        // identifier never crosses the split/join boundary (#2140).
5649        let qualified_chain = if scope.class_unit.is_none() {
5650            qualified_class_name_chain(declaration_node, self.source, scope)
5651                .filter(|chain| chain.join("$") == name)
5652        } else {
5653            None
5654        };
5655        let fq = if let Some(chain) = qualified_chain {
5656            let mut fq = FqName::new();
5657            cpp_push_package(&mut fq, &scope.package_name);
5658            let mut first = true;
5659            for component in chain {
5660                let kind = if first {
5661                    SegmentKind::Type
5662                } else {
5663                    SegmentKind::Nested
5664                };
5665                fq.push(cpp_segment(&component, kind));
5666                first = false;
5667            }
5668            fq
5669        } else {
5670            cpp_leaf_fq(
5671                &scope.package_name,
5672                scope.class_unit.as_ref(),
5673                &name,
5674                SegmentKind::Nested,
5675                SegmentKind::Type,
5676            )
5677        };
5678        let code_unit = CodeUnit::with_signature_and_fq(
5679            self.file.clone(),
5680            CodeUnitType::Class,
5681            scope.package_name.clone(),
5682            short_name,
5683            scope.template_signature.clone(),
5684            false,
5685            fq,
5686        );
5687        let has_body = definition_body_present;
5688        if !has_body && self.parsed.contains_declaration(&code_unit) {
5689            self.parsed.record_navigation_range(
5690                code_unit.clone(),
5691                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
5692            );
5693            return code_unit;
5694        }
5695        if has_body {
5696            // A forward declaration above this definition is one of the
5697            // class's declaration sites: C++ injects the class name at the
5698            // forward declaration, so a reference between the two spells this
5699            // class (#3297). Moving the unit to its body must not drop the
5700            // earlier site, exactly as a function prototype and its body stay
5701            // two ranges of one declaration (#1650).
5702            let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
5703            if let Some(range) = explicit_range {
5704                self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
5705            } else {
5706                self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
5707            }
5708            for range in earlier_ranges {
5709                self.parsed.add_declaration_range(&code_unit, range);
5710            }
5711        } else {
5712            self.add_declaration(code_unit.clone(), declaration_node, None, None);
5713        }
5714        if let Some(raw_supertypes) = raw_supertypes {
5715            self.parsed
5716                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
5717        }
5718        self.parsed.add_signature(
5719            code_unit.clone(),
5720            render_cpp_type_signature(
5721                declaration_node,
5722                self.source,
5723                scope.template_signature.as_deref(),
5724            ),
5725        );
5726        if let Some(metadata) = &scope.template_metadata {
5727            let primary_short_name = if let Some(parent) = &scope.class_unit {
5728                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
5729            } else {
5730                metadata.primary_name.clone()
5731            };
5732            let primary_fq_name = CodeUnit::new(
5733                self.file.clone(),
5734                CodeUnitType::Class,
5735                scope.package_name.clone(),
5736                primary_short_name,
5737            )
5738            .fq_name();
5739            let mut metadata = metadata.clone();
5740            metadata.primary_fq_name = primary_fq_name;
5741            self.parsed
5742                .set_cpp_template_metadata(code_unit.clone(), metadata);
5743        }
5744        if let Some(parent) = &scope.class_unit {
5745            self.parsed.add_child(parent.clone(), code_unit.clone());
5746        } else if let Some(module) = &scope.module {
5747            self.parsed.add_child(module.clone(), code_unit.clone());
5748        }
5749
5750        if let Some(body) = body {
5751            let mut nested_scope = scope.clone();
5752            nested_scope.class_unit = Some(code_unit.clone());
5753            nested_scope.template_signature = scope.template_signature.clone();
5754            // Template metadata describes the class just created. It must not
5755            // leak into ordinary nested declarations in that class's body.
5756            // Recovered export-macro specializations carry a separate scope bit
5757            // for their declaration-shaped body members.
5758            nested_scope.template_metadata = None;
5759            // Export-macro class bodies recovered from a function_definition use
5760            // compound_statement children, whose direct fields are declarations.
5761            nested_scope.recovered_specialization_member_scope =
5762                scope.template_metadata.as_ref().is_some_and(|metadata| {
5763                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
5764                });
5765            nested_scope.declarations_are_fields =
5766                is_recovered_exported_class_container(declaration_node, self.source)
5767                    || nested_scope.recovered_specialization_member_scope;
5768            if let Some(displaced) = displaced_macro_tail {
5769                // A macro-shaped field without a source semicolon can make
5770                // tree-sitter consume the real class terminator as an ERROR
5771                // inside that field, then retain following namespace items as
5772                // later field-list children. Drain the proven class prefix
5773                // first and re-own only the structured tail with the outer
5774                // scope. The tail is pushed first because the work stack is
5775                // LIFO.
5776                push_cpp_sibling_range(
5777                    body,
5778                    displaced.split_index,
5779                    usize::MAX,
5780                    scope.clone(),
5781                    stack,
5782                );
5783                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
5784            } else {
5785                stack.push(CppWork::Container(CppContainer {
5786                    node: body,
5787                    scope: nested_scope,
5788                }));
5789            }
5790        }
5791        if declaration_node.kind() == "enum_specifier" {
5792            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
5793            if !self.has_enum_enumerator_units(&code_unit) {
5794                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
5795            }
5796        }
5797        code_unit
5798    }
5799
5800    /// Whether the parse product already holds enumerator fields for `parent`,
5801    /// answered from the walk's carried-forward field ownership index.
5802    ///
5803    /// Built on the first enum's question and advanced by every declaration
5804    /// recorded after it, so a file that declares no enum -- most files -- pays
5805    /// nothing, and one that declares thousands pays a single pass instead of
5806    /// one per enum (#2786).
5807    fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
5808        if self.field_owners.is_none() {
5809            self.field_owners = Some(CppFieldOwnerIndex::of(
5810                self.parsed.declarations().iter(),
5811                self.file,
5812            ));
5813        }
5814        debug_assert_eq!(
5815            parent.source(),
5816            self.file,
5817            "the walk's declarations are declarations of the file it is walking"
5818        );
5819        let carried = self
5820            .field_owners
5821            .as_ref()
5822            .expect("the index was just ensured")
5823            .owns_fields(parent.package_name(), parent.short_name());
5824
5825        #[cfg(debug_assertions)]
5826        assert_eq!(
5827            carried,
5828            cpp_declarations_hold_owned_fields(
5829                self.parsed.declarations(),
5830                self.file,
5831                parent.package_name(),
5832                parent.short_name()
5833            ),
5834            "the carried-forward field index must answer what a fresh declaration scan \
5835             answers for {}",
5836            parent.fq_name()
5837        );
5838
5839        carried
5840    }
5841
5842    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
5843        walk_named_tree_preorder(node, false, |child| {
5844            if child.kind() != "enumerator" {
5845                return WalkControl::Continue;
5846            }
5847            let Some(name_node) = child.child_by_field_name("name") else {
5848                return WalkControl::Continue;
5849            };
5850            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
5851            if name.is_empty() {
5852                return WalkControl::Continue;
5853            }
5854            let code_unit = CodeUnit::new_fq(
5855                self.file.clone(),
5856                CodeUnitType::Field,
5857                scope.package_name.clone(),
5858                cpp_join_member_short(parent.short_name(), &name),
5859                parent
5860                    .fq()
5861                    .clone()
5862                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
5863            );
5864            if self.parsed.contains_declaration(&code_unit) {
5865                return WalkControl::Continue;
5866            }
5867            self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
5868            self.parsed.add_signature(
5869                code_unit,
5870                normalize_cpp_whitespace(node_text(child, self.source)),
5871            );
5872            WalkControl::Continue
5873        });
5874    }
5875
5876    fn visit_enum_enumerators_from_text(
5877        &mut self,
5878        node: Node<'_>,
5879        scope: &ScopeInfo,
5880        parent: &CodeUnit,
5881    ) {
5882        let text = node_text(node, self.source);
5883        let Some((_, body)) = text.split_once('{') else {
5884            return;
5885        };
5886        let Some((body, _)) = body.rsplit_once('}') else {
5887            return;
5888        };
5889        for entry in body.split(',') {
5890            let trimmed = entry.trim();
5891            let name = trimmed
5892                .split('=')
5893                .next()
5894                .unwrap_or("")
5895                .split_whitespace()
5896                .next()
5897                .unwrap_or("");
5898            if name.is_empty() {
5899                continue;
5900            }
5901            let code_unit = CodeUnit::new_fq(
5902                self.file.clone(),
5903                CodeUnitType::Field,
5904                scope.package_name.clone(),
5905                cpp_join_member_short(parent.short_name(), name),
5906                parent
5907                    .fq()
5908                    .clone()
5909                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
5910            );
5911            if self.parsed.contains_declaration(&code_unit) {
5912                continue;
5913            }
5914            self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
5915            self.parsed.add_signature(code_unit, trimmed.to_string());
5916        }
5917    }
5918
5919    fn visit_function_definition<'tree>(
5920        &mut self,
5921        node: Node<'tree>,
5922        scope: &ScopeInfo,
5923        stack: &mut Vec<CppWork<'tree>>,
5924        ancestry: &ParentIndex<'tree>,
5925    ) {
5926        // An attribute-like macro invocation whose argument is a declaration can
5927        // swallow every declaration written after it into one bogus
5928        // `function_definition` (#2551). This owns the whole region, so it runs
5929        // ahead of `visit_macro_swallowed_function_declarations` below, which
5930        // admits the same envelope by its head macro token and reads single
5931        // declarators out of nodes this recovery reparses properly.
5932        if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
5933            return;
5934        }
5935        // A file-scope object-like macro sentinel the parser cannot see (issue
5936        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
5937        // prefixes as a bogus `function_definition` that swallows real namespaces,
5938        // classes, and members. Reparse the swallowed interior as C++ items so the
5939        // ordinary declaration visitors index it with byte/line-exact ownership.
5940        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5941            return;
5942        }
5943        if node.has_error() {
5944            self.visit_macro_swallowed_function_declarations(node, scope);
5945        }
5946        if let Some((class_node, name, raw_supertypes)) =
5947            recover_exported_class_function_definition(node, self.source)
5948        {
5949            if let Some(body) = cpp_body_node(node)
5950                && let Some(close) = self
5951                    .orphaned_namespaces
5952                    .matching_close_brace(body.start_byte())
5953                && close.end_byte < body.end_byte()
5954            {
5955                let class_range = Range {
5956                    start_byte: node.start_byte(),
5957                    end_byte: close.end_byte,
5958                    start_line: node.start_position().row + 1,
5959                    end_line: close.end_line,
5960                };
5961                // Parse the body in class context, preserving the original
5962                // keyword, recovered name and body as included ranges. Parsing
5963                // the interior at file scope turns constructors and access
5964                // labels into another oversized ERROR instead of members.
5965                let mut head = vec![node];
5966                let mut keyword = None;
5967                let mut name_node = None;
5968                while let Some(part) = head.pop() {
5969                    if part.start_byte() >= body.start_byte() || part.is_missing() {
5970                        continue;
5971                    }
5972                    if matches!(part.kind(), "class" | "struct" | "union") {
5973                        keyword = Some(part);
5974                    }
5975                    if matches!(part.kind(), "identifier" | "type_identifier")
5976                        && node_text(part, self.source) == name
5977                    {
5978                        name_node = Some(part);
5979                    }
5980                    let mut cursor = part.walk();
5981                    head.extend(part.children(&mut cursor));
5982                }
5983                if let (Some(keyword), Some(name_node)) = (keyword, name_node)
5984                    && let Some(type_name) = keyword
5985                        .parent()
5986                        .and_then(|parent| parent.child_by_field_name("name"))
5987                    && let Some(tree) = parse_source_ranges_with_cancellation(
5988                        &tree_sitter_cpp::LANGUAGE.into(),
5989                        self.source,
5990                        &[
5991                            (keyword.start_byte(), type_name.start_byte()),
5992                            (name_node.start_byte(), name_node.end_byte()),
5993                            (body.start_byte(), close.end_byte),
5994                        ],
5995                        None,
5996                    )
5997                    && let Some(reparsed_class) = tree.root_node().named_child(0)
5998                    && let Some(class_body) = cpp_body_node(reparsed_class)
5999                    && class_body.start_byte() == body.start_byte()
6000                    && class_body.end_byte() == close.end_byte
6001                    && let Some(tail) =
6002                        cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
6003                {
6004                    let class_unit = self.visit_named_class_like_shape(
6005                        class_node,
6006                        name,
6007                        None,
6008                        true,
6009                        Some(class_range),
6010                        raw_supertypes,
6011                        scope,
6012                        stack,
6013                        ancestry,
6014                    );
6015                    self.parsed.record_materialization(
6016                        MaterializationRecord::RecoveredDeclaration {
6017                            recovery: class_range,
6018                            unit: class_unit.clone(),
6019                        },
6020                    );
6021                    let member_scope = ScopeInfo {
6022                        package_name: class_unit.package_name().to_string(),
6023                        class_unit: Some(class_unit),
6024                        declarations_are_fields: true,
6025                        template_metadata: None,
6026                        recovered_specialization_member_scope: false,
6027                        ..scope.clone()
6028                    };
6029                    let class_body_range = class_body.byte_range();
6030                    let tail_range = tail.root_node().byte_range();
6031                    self.partitioned_regions
6032                        .push((tail, tail_range, scope.clone()));
6033                    self.partitioned_regions
6034                        .push((tree, class_body_range, member_scope));
6035                    return;
6036                }
6037            }
6038            let body = cpp_body_node(class_node);
6039            let displaced_namespace = cpp_body_node(node)
6040                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
6041            let fragmented = cpp_body_node(node).and_then(|body| {
6042                fragmented_export_function_body_region(
6043                    node,
6044                    body,
6045                    self.source,
6046                    displaced_namespace.as_ref(),
6047                )
6048            });
6049            // The recovery tuple's first node is the class-like type when the
6050            // parser exposes one, but the synthetic wrapper owns the compound
6051            // statement that contains the truncated class body. Use the
6052            // wrapper body for fragmented-member detection; retain the
6053            // class-node body for the ordinary (non-fragmented) path below.
6054            if let Some(fragmented) = fragmented {
6055                // The lifted sibling no longer sits below the parser-visible
6056                // namespace node. Restore the current parent scope when the
6057                // ordinary work walk reaches that class.
6058                if let Some(boundary) =
6059                    fragmented_export_sibling_class_boundary(node, self.source, ancestry)
6060                        .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
6061                {
6062                    let mut boundary_scope = scope.clone();
6063                    for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
6064                        if sibling.start_byte() >= boundary.start_byte() {
6065                            break;
6066                        }
6067                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
6068                            boundary_scope.visible_using_namespaces.push(namespace);
6069                        }
6070                    }
6071                    self.recovered_class_sibling_scopes
6072                        .insert(boundary.id(), boundary_scope);
6073                }
6074                let mut recovered_constructor = None;
6075                let mut recovered_prefix_tree = None;
6076                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
6077                {
6078                    Some(FragmentedExportMembers::Complete(tree)) => {
6079                        if let Some(body) = body
6080                            && let Some(range) =
6081                                cpp_reparsed_synthetic_initializer_constructor_range(
6082                                    tree.root_node(),
6083                                    &name,
6084                                    self.source,
6085                                    body.end_byte(),
6086                                )
6087                        {
6088                            recovered_constructor = Some(range);
6089                            recovered_prefix_tree = Some(tree);
6090                            None
6091                        } else {
6092                            Some(FragmentedExportMembers::Complete(tree))
6093                        }
6094                    }
6095                    outcome => outcome,
6096                };
6097                let mut class_stack = Vec::new();
6098                let class_unit = self.visit_named_class_like_shape(
6099                    class_node,
6100                    name,
6101                    None,
6102                    true,
6103                    Some(fragmented.class_range),
6104                    raw_supertypes,
6105                    scope,
6106                    &mut class_stack,
6107                    ancestry,
6108                );
6109                self.parsed
6110                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
6111                        recovery: fragmented.class_range,
6112                        unit: class_unit.clone(),
6113                    });
6114                let complete = outcome.is_some_and(|outcome| {
6115                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
6116                });
6117                if complete {
6118                    self.consumed_fragment_regions
6119                        .push((node.start_byte(), fragmented.class_range.end_byte));
6120                } else {
6121                    // The reparse can fail when the first constructor or a
6122                    // method body is split into statement-shaped siblings.
6123                    // Keep the recovered class envelope, but do not visit the
6124                    // synthetic wrapper body: its initializer expressions can
6125                    // look like same-named member functions (for example
6126                    // `Token.location(loc)`). Re-own only the original sibling
6127                    // nodes that fall inside the proven class range. Their CST
6128                    // shapes retain the real field/function kinds and ranges.
6129                    let member_scope = ScopeInfo {
6130                        package_name: class_unit.package_name().to_string(),
6131                        module: scope.module.clone(),
6132                        class_unit: Some(class_unit.clone()),
6133                        template_signature: scope.template_signature.clone(),
6134                        template_metadata: None,
6135                        declarations_are_fields: true,
6136                        recovered_specialization_member_scope: false,
6137                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
6138                    };
6139                    for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6140                        if candidate.start_byte() >= fragmented.reparse_end {
6141                            break;
6142                        }
6143                        if cpp_fragment_sibling_is_class_member(
6144                            candidate,
6145                            fragmented.reparse_end,
6146                            self.source,
6147                        ) {
6148                            self.recovered_class_sibling_scopes
6149                                .insert(candidate.id(), member_scope.clone());
6150                        }
6151                    }
6152                    if let Some(range) = recovered_constructor
6153                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
6154                    {
6155                        self.visit_recovered_fragment_prefix_members(
6156                            prefix_tree.root_node(),
6157                            range.start,
6158                            &class_unit,
6159                            scope,
6160                            ancestry,
6161                        );
6162                        self.visit_recovered_fragment_constructor(
6163                            range,
6164                            body,
6165                            class_node,
6166                            &class_unit,
6167                            scope,
6168                            ancestry,
6169                        );
6170                    }
6171                }
6172                if let Some(boundary) = displaced_namespace {
6173                    for item in boundary.namespace_items {
6174                        self.recovered_class_sibling_scopes
6175                            .insert(item.id(), scope.clone());
6176                    }
6177                }
6178                stack.extend(class_stack);
6179                return;
6180            }
6181            let mut stack = Vec::new();
6182            let class_unit = self.visit_named_class_like_shape(
6183                class_node,
6184                name,
6185                body,
6186                body.is_some(),
6187                None,
6188                raw_supertypes,
6189                scope,
6190                &mut stack,
6191                ancestry,
6192            );
6193            self.parsed
6194                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6195                    recovery: cpp_declaration_range(node),
6196                    unit: class_unit,
6197                });
6198            // Issue #1524: the bogus `function_definition` body can run past
6199            // the class's true closing brace (the parse ends it with a
6200            // zero-width `MISSING "}"`), swallowing following namespace-scope
6201            // siblings -- they would index as members of the recovered class.
6202            // When the body's text-balanced close lands before the body's own
6203            // end, re-own the swallowed tail with the outer scope instead.
6204            if let Some(body) = body
6205                && let Some(class_close) = self
6206                    .orphaned_namespaces
6207                    .matching_close_brace(body.start_byte())
6208                && class_close.start_byte < body.end_byte()
6209            {
6210                let split = {
6211                    let mut cursor = body.walk();
6212                    body.named_children(&mut cursor)
6213                        .position(|child| child.start_byte() > class_close.start_byte)
6214                };
6215                if let Some(split) = split {
6216                    // The seeded work is a single Container over the whole
6217                    // body with the class scope; replace it with the bounded
6218                    // head (class scope) plus the swallowed tail (outer
6219                    // scope). Push tail first so the head drains first.
6220                    let seeded = stack.pop();
6221                    match seeded {
6222                        Some(CppWork::Container(container)) => {
6223                            push_cpp_sibling_range(
6224                                body,
6225                                split,
6226                                usize::MAX,
6227                                scope.clone(),
6228                                &mut stack,
6229                            );
6230                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
6231                        }
6232                        // visit_named_class_like_shape always seeds exactly
6233                        // one Container when a body is present.
6234                        _ => unreachable!("exported-class seed is always one Container"),
6235                    }
6236                }
6237            }
6238            while let Some(work) = stack.pop() {
6239                match work {
6240                    CppWork::Container(container) => {
6241                        push_cpp_container_work(container.node, container.scope, &mut stack);
6242                    }
6243                    CppWork::Siblings(siblings) => {
6244                        advance_cpp_siblings(siblings, self.source, &mut stack);
6245                    }
6246                    CppWork::Node(work) => {
6247                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
6248                    }
6249                }
6250            }
6251            return;
6252        }
6253        let recovered_constraint_constructor =
6254            cpp_recovered_template_macro_constructor(node, self.source);
6255        let declarator = recovered_constraint_constructor
6256            .map(|(declarator, _)| declarator)
6257            .or_else(|| node.child_by_field_name("declarator"));
6258        let Some(declarator) = declarator else {
6259            self.visit_malformed_function_definition_container(node, scope, stack);
6260            return;
6261        };
6262        let Some(function_declarator) = extract_function_declarator(declarator) else {
6263            self.visit_malformed_function_definition_container(node, scope, stack);
6264            return;
6265        };
6266        let function = if let Some((_, callable_name)) =
6267            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
6268        {
6269            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
6270        } else {
6271            extract_function_info(function_declarator, self.source, scope)
6272        };
6273        let Some(mut function) = function else {
6274            self.visit_malformed_function_definition_container(node, scope, stack);
6275            return;
6276        };
6277        if let Some((_, template_parameter)) = recovered_constraint_constructor {
6278            function.signature = format!(
6279                "template <{}>{}",
6280                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
6281                function.signature
6282            );
6283        }
6284        let code_unit = function.code_unit(self.file.clone());
6285        // Keep an earlier same-file prototype as another physical occurrence
6286        // of this callable. `CodeUnit` already identifies the role-neutral
6287        // overload, while ranges and signature metadata describe its
6288        // declaration/definition occurrences.
6289        self.add_declaration(code_unit.clone(), node, None, None);
6290        let signature = if recovered_constraint_constructor.is_some() {
6291            normalize_cpp_whitespace(node_text(function_declarator, self.source))
6292        } else {
6293            render_cpp_function_display_signature_from_node(
6294                node,
6295                self.source,
6296                scope.template_signature.as_deref(),
6297                true,
6298                ancestry,
6299            )
6300        };
6301        self.parsed.add_signature_with_metadata(
6302            code_unit.clone(),
6303            cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
6304                .with_declaration_only(false)
6305                .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
6306        );
6307        if let Some(parent) = &scope.class_unit {
6308            self.parsed.add_child(parent.clone(), code_unit);
6309        } else if let Some(module) = &scope.module {
6310            self.parsed.add_child(module.clone(), code_unit);
6311        }
6312        self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
6313    }
6314
6315    /// Recover the namespace lost when tree-sitter promotes an export-macro
6316    /// class definition to a root-level `function_definition`.  Only a
6317    /// body-bearing, top-level recovery may borrow a namespace, and only when
6318    /// one earlier namespace-scope forward declaration proves the identity.
6319    fn scope_for_recovered_exported_class<'tree>(
6320        &mut self,
6321        node: Node<'tree>,
6322        name: &str,
6323        definition_body_present: bool,
6324        scope: &ScopeInfo,
6325        ancestry: &ParentIndex<'tree>,
6326    ) -> ScopeInfo {
6327        if !definition_body_present
6328            || !scope.package_name.is_empty()
6329            || scope.class_unit.is_some()
6330            || !(is_recovered_exported_class_container(node, self.source)
6331                || recover_function_like_export_class_pair(node, self.source).is_some()
6332                || recover_embedded_function_like_export_classes(node, self.source)
6333                    .iter()
6334                    .any(|recovered| recovered.name == name)
6335                || matches!(node.kind(), "declaration" | "field_declaration")
6336                    && recover_exported_class_declaration(node, self.source).is_some()
6337                || matches!(
6338                    node.kind(),
6339                    "class_specifier" | "struct_specifier" | "union_specifier"
6340                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
6341                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
6342                        name_node,
6343                        self.source,
6344                    )))
6345                }) || ancestry.parent(node).is_some_and(|parent| {
6346                    matches!(parent.kind(), "declaration" | "field_declaration")
6347                        && recover_exported_class_declaration(parent, self.source).is_some()
6348                        || is_recovered_exported_class_container(parent, self.source)
6349                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
6350        {
6351            return scope.clone();
6352        }
6353        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
6354        let Some(package_name) = borrowed_namespace
6355            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
6356        else {
6357            return scope.clone();
6358        };
6359
6360        let module = CodeUnit::new_fq(
6361            self.file.clone(),
6362            CodeUnitType::Module,
6363            "",
6364            package_name.clone(),
6365            cpp_namespace_fq(&package_name),
6366        );
6367        let mut recovered = scope.clone();
6368        recovered.package_name = package_name;
6369        recovered.module = Some(module);
6370        recovered
6371    }
6372
6373    /// The unique namespace-scope forward declaration of `name` that precedes
6374    /// `recovered_node`, answered from the walk's carried-forward scan of the
6375    /// tree `recovered_node` belongs to.
6376    ///
6377    /// The scan is built on the first question and advanced by each later one,
6378    /// so a file that never reaches this path -- almost every file -- pays
6379    /// nothing, and one that reaches it thousands of times pays a single pass
6380    /// (#2754).
6381    fn unique_earlier_namespace_forward<'tree>(
6382        &mut self,
6383        recovered_node: Node<'tree>,
6384        name: &str,
6385        ancestry: &ParentIndex<'tree>,
6386    ) -> Option<String> {
6387        let mut root = recovered_node;
6388        while let Some(parent) = ancestry.parent(root) {
6389            root = parent;
6390        }
6391        let source = self.source;
6392        let scan = self
6393            .namespace_forward_scans
6394            .entry(CppTreeIdentity::of(root))
6395            .or_default();
6396        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
6397        let borrowed = scan.unique_earlier_forward(name, recovered_node);
6398
6399        #[cfg(debug_assertions)]
6400        assert_eq!(
6401            borrowed,
6402            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
6403            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
6404             for {name} at byte {}",
6405            recovered_node.start_byte()
6406        );
6407
6408        borrowed
6409    }
6410
6411    fn visit_malformed_function_definition_container<'tree>(
6412        &mut self,
6413        node: Node<'tree>,
6414        scope: &ScopeInfo,
6415        stack: &mut Vec<CppWork<'tree>>,
6416    ) {
6417        let Some(body) = cpp_body_node(node) else {
6418            return;
6419        };
6420        if !cpp_contains_namespace_definition(body) {
6421            return;
6422        }
6423        stack.push(CppWork::Container(CppContainer {
6424            node: body,
6425            scope: scope.clone(),
6426        }));
6427    }
6428
6429    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
6430    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
6431    /// emits for a sentinel-prefixed region, reparse the interior after the
6432    /// sentinel identifier as real C++ items -- confined to the region so
6433    /// every reparsed node keeps its original byte/line position -- and run the
6434    /// ordinary container visitation over the result. Returns `true` when it fired
6435    /// (the caller must then skip normal function processing). Nested sentinel
6436    /// regions recover recursively: the reparsed interior is walked through the
6437    /// same `visit_function_definition` path, so a sentinel inside the region hits
6438    /// this recovery again.
6439    /// Runs `reparse_walk` and records every declaration it mints as a
6440    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
6441    /// `recovery` (issue #1657). A reparsed sentinel region has no single
6442    /// recovered envelope unit: the ordinary visitors mint namespaces,
6443    /// classes, and members directly from the reparsed tree, so the walk's
6444    /// declaration delta is the recovered set. Records are ordered by
6445    /// declaration start byte so the parse product stays deterministic.
6446    fn record_recovered_declarations(
6447        &mut self,
6448        recovery: Range,
6449        reparse_walk: impl FnOnce(&mut Self),
6450    ) {
6451        // The set difference this used to be, kept as the oracle every answer
6452        // is asserted against (#2787).
6453        #[cfg(any(debug_assertions, test))]
6454        let before = self.parsed.declarations().clone();
6455
6456        self.recovery_captures.push(CppRecoveryCapture::default());
6457        reparse_walk(self);
6458        let captured = self
6459            .recovery_captures
6460            .pop()
6461            .expect("the capture this call pushed is the one it pops");
6462
6463        // The capture holds every declaration created while it was open, once
6464        // each and in creation order, so the recovered set costs what the
6465        // recovery made rather than everything the file has declared so far.
6466        // One filter is left to apply: a created declaration that a later
6467        // deferred replacement removed is not in the parse product to report.
6468        let mut minted: Vec<CodeUnit> = captured
6469            .created
6470            .into_iter()
6471            .filter(|unit| self.parsed.contains_declaration(unit))
6472            .collect();
6473        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6474
6475        #[cfg(any(debug_assertions, test))]
6476        {
6477            let mut rediscovered: Vec<CodeUnit> = self
6478                .parsed
6479                .declarations()
6480                .iter()
6481                .filter(|unit| !before.contains(*unit))
6482                .cloned()
6483                .collect();
6484            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6485            assert_eq!(
6486                minted, rediscovered,
6487                "the captured recovered set must be the declaration delta of the reparse \
6488                 walk over {recovery:?}"
6489            );
6490        }
6491
6492        for unit in minted {
6493            self.parsed
6494                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6495                    recovery,
6496                    unit,
6497                });
6498        }
6499    }
6500
6501    /// Where one recovered declaration sorts: by start byte, then by name, so
6502    /// the parse product stays deterministic.
6503    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
6504        let start = self
6505            .parsed
6506            .declaration_ranges(unit)
6507            .first()
6508            .map(|range| range.start_byte)
6509            .unwrap_or(usize::MAX);
6510        (start, unit.fq_name().to_string())
6511    }
6512
6513    fn visit_sentinel_macro_region<'tree>(
6514        &mut self,
6515        node: Node<'tree>,
6516        scope: &ScopeInfo,
6517        stack: &mut Vec<CppWork<'tree>>,
6518        ancestry: &ParentIndex<'tree>,
6519    ) -> bool {
6520        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
6521            return true;
6522        }
6523        if let Some((
6524            reparse_start,
6525            class_start,
6526            body_start,
6527            class_close_start,
6528            class_close_end,
6529            class_close_line,
6530        )) = cpp_sentinel_macro_class_region(node, self.source)
6531        {
6532            let Some(class_tree) =
6533                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
6534            else {
6535                return false;
6536            };
6537            let class_root = class_tree.root_node();
6538            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
6539            // A region reparse is its own tree and needs its own parent index.
6540            let class_ancestry = ParentIndex::new(class_root);
6541            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
6542                class_root,
6543                template_node,
6544                self.source,
6545                &class_ancestry,
6546            ) else {
6547                return false;
6548            };
6549            let class_node = reparsed_class.declaration_node;
6550            let name = reparsed_class.name;
6551            let mut class_scope = scope.clone();
6552            if let Some(template_node) = template_node {
6553                class_scope.template_signature =
6554                    cpp_template_signature(template_node, class_node, self.source);
6555                class_scope.template_metadata =
6556                    cpp_template_metadata(template_node, class_node, self.source, ancestry);
6557            }
6558            let Some(body_tree) =
6559                cpp_reparse_region_items(self.source, body_start, class_close_start)
6560            else {
6561                return false;
6562            };
6563            let raw_supertypes = reparsed_class.raw_supertypes;
6564            let class_range = Range {
6565                start_byte: class_start,
6566                end_byte: class_close_end,
6567                start_line: class_node.start_position().row + 1,
6568                end_line: class_close_line,
6569            };
6570            let class_scope = self.scope_for_recovered_exported_class(
6571                class_node,
6572                &name,
6573                true,
6574                &class_scope,
6575                ancestry,
6576            );
6577            let mut class_stack = Vec::new();
6578            let class_unit = self.visit_named_class_like_shape(
6579                class_node,
6580                name,
6581                None,
6582                true,
6583                Some(class_range),
6584                raw_supertypes,
6585                &class_scope,
6586                &mut class_stack,
6587                ancestry,
6588            );
6589            self.parsed
6590                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6591                    recovery: class_range,
6592                    unit: class_unit.clone(),
6593                });
6594            let member_scope = ScopeInfo {
6595                package_name: class_scope.package_name.clone(),
6596                module: class_scope.module.clone(),
6597                class_unit: Some(class_unit),
6598                template_signature: class_scope.template_signature.clone(),
6599                template_metadata: None,
6600                declarations_are_fields: true,
6601                recovered_specialization_member_scope: false,
6602                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
6603            };
6604            // The padded body reparse is its own tree, so it indexes itself.
6605            let body_root = body_tree.root_node();
6606            self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
6607            // Register only after the padded body reparse: its nodes deliberately
6608            // retain offsets inside the consumed region and must be visited first.
6609            self.consumed_fragment_regions
6610                .push((node.start_byte(), class_close_end));
6611            // An ERROR envelope can hold real sibling declarations after the
6612            // recovered class's close (the suffix-reparse boundary in
6613            // `cpp_sentinel_macro_class_region` partitions, it does not
6614            // consume). Walk the envelope's remaining children normally; the
6615            // consumed region above keeps the recovered class from being
6616            // indexed twice.
6617            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
6618                stack.push(CppWork::Container(CppContainer {
6619                    node,
6620                    scope: scope.clone(),
6621                }));
6622            }
6623            return true;
6624        }
6625        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
6626            return false;
6627        };
6628        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6629            return false;
6630        };
6631        let root = tree.root_node();
6632        if !cpp_reparsed_items_are_indexable(root, self.source) {
6633            return false;
6634        }
6635        let recovery = cpp_recovery_window(self.source, start, end);
6636        // The reparsed region is its own tree, so this walk indexes it itself.
6637        let reparsed_ancestry = ParentIndex::new(root);
6638        self.record_recovered_declarations(recovery, |visitor| {
6639            visitor.visit_container(
6640                root,
6641                &reparsed_ancestry,
6642                &scope.package_name,
6643                scope.module.clone(),
6644                scope.class_unit.clone(),
6645                scope.template_signature.clone(),
6646                scope.visible_using_namespaces.clone(),
6647            );
6648        });
6649        if end > node.end_byte() {
6650            self.consumed_fragment_regions
6651                .push((node.start_byte(), end));
6652        } else if node.kind() == "ERROR" && node.end_byte() > end {
6653            // The sentinel region ended at the first recovered class-like item
6654            // but the ERROR envelope keeps real sibling declarations after it
6655            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
6656            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
6657            // envelope's remaining children normally; the consumed region
6658            // keeps the reparsed prefix from being indexed twice.
6659            self.consumed_fragment_regions
6660                .push((node.start_byte(), end));
6661            stack.push(CppWork::Container(CppContainer {
6662                node,
6663                scope: scope.clone(),
6664            }));
6665        }
6666        true
6667    }
6668
6669    /// Re-own complete class declarations from the structured Abseil
6670    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
6671    /// its direct CST children already prove both namespace components and the
6672    /// class bodies, so the ordinary class/member visitor can retain ownership
6673    /// and exact source ranges without admitting unrelated callable bodies.
6674    fn visit_nested_namespace_sentinel<'tree>(
6675        &mut self,
6676        node: Node<'tree>,
6677        scope: &ScopeInfo,
6678        ancestry: &ParentIndex<'tree>,
6679    ) -> bool {
6680        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
6681            return false;
6682        };
6683
6684        let mut package_name = scope.package_name.clone();
6685        let mut module = scope.module.clone();
6686        for component in recovered.namespace_components {
6687            package_name = if package_name.is_empty() {
6688                component
6689            } else {
6690                format!("{package_name}::{component}")
6691            };
6692            let namespace_module = CodeUnit::new_fq(
6693                self.file.clone(),
6694                CodeUnitType::Module,
6695                "",
6696                package_name.clone(),
6697                cpp_namespace_fq(&package_name),
6698            );
6699            if !self.parsed.contains_declaration(&namespace_module) {
6700                self.add_declaration(namespace_module.clone(), recovered.function, None, None);
6701            }
6702            module = Some(namespace_module);
6703        }
6704
6705        let recovered_scope = ScopeInfo {
6706            package_name,
6707            module,
6708            // This scope's package comes from the namespace levels the sentinel
6709            // shape proves, so its owner must come from the same place. C++
6710            // never nests a namespace inside a class, so a surviving
6711            // `class_unit` here is recovery bleed from an earlier malformed
6712            // region, and keeping it would publish declarations whose package
6713            // names one lexical position and whose owner names another -- the
6714            // hybrid identity that trips the package/short boundary assert in
6715            // `CodeUnit::with_signature_and_fq` (#2306, #2979). Dropping it is
6716            // identity-neutral for valid code, where `class_unit` is always
6717            // empty at a namespace.
6718            class_unit: None,
6719            template_signature: scope.template_signature.clone(),
6720            template_metadata: scope.template_metadata.clone(),
6721            declarations_are_fields: false,
6722            recovered_specialization_member_scope: false,
6723            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6724        };
6725        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
6726            recovered.function,
6727            recovered.body,
6728            self.source,
6729            ancestry,
6730        ) {
6731            let mut class_scope = recovered_scope.clone();
6732            if let Some(template_node) = fragmented.template_node {
6733                class_scope.template_signature =
6734                    cpp_template_signature(template_node, fragmented.class_node, self.source);
6735                class_scope.template_metadata = cpp_template_metadata(
6736                    template_node,
6737                    fragmented.class_node,
6738                    self.source,
6739                    ancestry,
6740                );
6741            }
6742            if let Some(outcome) = self
6743                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
6744            {
6745                let mut class_stack = Vec::new();
6746                let class_unit = self.visit_named_class_like_shape(
6747                    fragmented.class_node,
6748                    fragmented.name.clone(),
6749                    None,
6750                    true,
6751                    Some(fragmented.fragmented.class_range),
6752                    fragmented.raw_supertypes.clone(),
6753                    &class_scope,
6754                    &mut class_stack,
6755                    ancestry,
6756                );
6757                self.parsed
6758                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
6759                        recovery: fragmented.fragmented.class_range,
6760                        unit: class_unit.clone(),
6761                    });
6762                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
6763                    self.consumed_fragment_regions.push((
6764                        fragmented.consumed_start,
6765                        fragmented.fragmented.class_range.end_byte,
6766                    ));
6767                }
6768            }
6769        }
6770        // The class requirement above is the admission gate; once admitted,
6771        // traverse the whole proven inner namespace body so sibling aliases,
6772        // functions, and variables are not silently discarded. The body is a
6773        // node of the tree being walked, so it reuses that tree's index.
6774        self.run_container_work(recovered.body, recovered_scope, ancestry);
6775        true
6776    }
6777
6778    fn visit_declaration<'tree>(
6779        &mut self,
6780        node: Node<'tree>,
6781        scope: &ScopeInfo,
6782        in_class_body: bool,
6783        stack: &mut Vec<CppWork<'tree>>,
6784        ancestry: &ParentIndex<'tree>,
6785    ) {
6786        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6787            return;
6788        }
6789        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
6790            return;
6791        }
6792        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
6793            !cpp_active_template_type_parameter(
6794                node,
6795                node_text(declarator, self.source),
6796                self.source,
6797                ancestry,
6798            )
6799        }) {
6800            return;
6801        }
6802        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
6803            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
6804            // the following `Imaging image` member into one malformed field.
6805            // The ERROR's identifier is the actual declarator; route it
6806            // through the ordinary field path so its parent, range, and
6807            // signature metadata remain consistent with every other member.
6808            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
6809            return;
6810        }
6811        if in_class_body
6812            && let Some(parent) = scope.class_unit.as_ref()
6813            && let Some(call) =
6814                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
6815        {
6816            self.visit_recovered_macro_qualified_constructor_definition(
6817                node, call, scope, ancestry,
6818            );
6819            return;
6820        }
6821        if in_class_body
6822            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
6823        {
6824            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
6825            return;
6826        }
6827        if in_class_body
6828            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
6829        {
6830            for member in members {
6831                self.add_macro_wrapped_declaration(member, scope, ancestry);
6832            }
6833            return;
6834        }
6835        if in_class_body
6836            && let Some(declarators) =
6837                recovered_macro_qualified_field_declarators(node, self.source)
6838        {
6839            for declarator in declarators {
6840                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6841            }
6842            return;
6843        }
6844        let recovered_alias_names = recovered_type_alias_names(node, self.source);
6845        if !recovered_alias_names.is_empty() {
6846            self.add_type_aliases(node, scope, recovered_alias_names, ancestry);
6847            return;
6848        }
6849        if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
6850            return;
6851        }
6852        if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
6853            return;
6854        }
6855
6856        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
6857            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
6858                // Issue #938: the members tree-sitter scattered out of the fragmented
6859                // multiple-base export node are reparsed from their true body region
6860                // and re-owned as members of the recovered class, with an explicit
6861                // navigation range spanning to the displaced closing brace.
6862                if let Some(outcome) =
6863                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
6864                {
6865                    let consumed_region = (
6866                        recovered.declaration_node.end_byte(),
6867                        fragmented.class_range.end_byte,
6868                    );
6869                    let code_unit = self.visit_named_class_like_shape(
6870                        recovered.declaration_node,
6871                        recovered.name,
6872                        None,
6873                        true,
6874                        Some(fragmented.class_range),
6875                        recovered.raw_supertypes,
6876                        scope,
6877                        stack,
6878                        ancestry,
6879                    );
6880                    self.parsed.record_materialization(
6881                        MaterializationRecord::RecoveredDeclaration {
6882                            recovery: fragmented.class_range,
6883                            unit: code_unit.clone(),
6884                        },
6885                    );
6886                    let consume_fragment =
6887                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
6888                    // Everything between the fragmented declaration and its displaced
6889                    // closing brace now belongs to the recovered class; keep the
6890                    // ordinary walk from re-indexing those scattered siblings at top
6891                    // level. Register the consumed region only after indexing because
6892                    // the reparsed nodes retain byte offsets inside that same region.
6893                    if consume_fragment {
6894                        self.consumed_fragment_regions.push(consumed_region);
6895                    }
6896                    return;
6897                }
6898            }
6899            let uses_initializer_body = recovered.uses_initializer_body;
6900            let definition_body_present = recovered.body.is_some();
6901            let class_unit = self.visit_named_class_like_shape(
6902                recovered.declaration_node,
6903                recovered.name,
6904                recovered.body,
6905                definition_body_present,
6906                None,
6907                recovered.raw_supertypes,
6908                scope,
6909                stack,
6910                ancestry,
6911            );
6912            self.parsed
6913                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6914                    recovery: cpp_declaration_range(node),
6915                    unit: class_unit,
6916                });
6917            if uses_initializer_body {
6918                return;
6919            }
6920        }
6921
6922        // A function-like macro invocation that expands to declarations keeps
6923        // no terminator of its own: the semicolons live in the replacement
6924        // body, which is not part of this tree. Tree-sitter recovers one and
6925        // reads the macro name as the member type and its argument list as a
6926        // parenthesized declarator, so `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)`
6927        // published a field named `ClosedDetect` beside the real constructor
6928        // and split every reference to the constructor's name between the two
6929        // (#3298). The invented terminator is the whole difference from a real
6930        // member: a member terminates itself, and with a terminator the
6931        // grammar reads `T (name);` as a declaration rather than a field.
6932        if in_class_body && cpp_unterminated_macro_invocation_field(node) {
6933            return;
6934        }
6935
6936        let mut handled_function = false;
6937        let mut handled_declarator = false;
6938        let mut cursor = node.walk();
6939        for child in node.named_children(&mut cursor) {
6940            if matches!(
6941                child.kind(),
6942                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6943            ) {
6944                // A named class-like definition remains a declaration even when
6945                // the same statement also declares an object, for example
6946                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
6947                // declaration's type and `kind` as its declarator.  Dropping the
6948                // type here loses both its nested owner and every later lexical
6949                // reference to it.  A body is the structured proof that this is
6950                // a definition rather than an elaborated type use such as
6951                // `class Kind value;`.
6952                if cpp_body_node(child).is_some() {
6953                    self.visit_class_like(child, scope, stack, ancestry);
6954                }
6955                continue;
6956            }
6957        }
6958
6959        let mut cursor = node.walk();
6960        for child in node.children_by_field_name("declarator", &mut cursor) {
6961            if crate::structural::is_recovered_designator_init_declarator(child) {
6962                handled_declarator = true;
6963                continue;
6964            }
6965            if in_class_body
6966                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
6967            {
6968                handled_declarator = true;
6969                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
6970                continue;
6971            }
6972            if let Some(kind) = classify_declarator(child) {
6973                handled_declarator = true;
6974                match kind {
6975                    DeclaratorKind::Function(function_declarator) => {
6976                        handled_function = true;
6977                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
6978                    }
6979                    DeclaratorKind::Variable(variable_declarator) => {
6980                        self.visit_variable_declaration(
6981                            node,
6982                            variable_declarator,
6983                            scope,
6984                            in_class_body,
6985                            ancestry,
6986                        );
6987                    }
6988                }
6989            }
6990        }
6991
6992        if !handled_declarator {
6993            let mut cursor = node.walk();
6994            for child in node.named_children(&mut cursor) {
6995                if crate::structural::is_recovered_designator_init_declarator(child) {
6996                    handled_declarator = true;
6997                    continue;
6998                }
6999                if !is_unfielded_declarator_candidate(child) {
7000                    continue;
7001                }
7002                let Some(kind) = classify_declarator(child) else {
7003                    continue;
7004                };
7005                handled_declarator = true;
7006                match kind {
7007                    DeclaratorKind::Function(function_declarator) => {
7008                        handled_function = true;
7009                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
7010                    }
7011                    DeclaratorKind::Variable(variable_declarator) => {
7012                        self.visit_variable_declaration(
7013                            node,
7014                            variable_declarator,
7015                            scope,
7016                            in_class_body,
7017                            ancestry,
7018                        );
7019                    }
7020                }
7021            }
7022        }
7023
7024        if handled_function {
7025            return;
7026        }
7027
7028        if !handled_declarator {
7029            if in_class_body {
7030                self.visit_class_members_from_declaration(node, scope, ancestry);
7031            } else {
7032                self.visit_global_variables_from_declaration(node, scope, ancestry);
7033            }
7034        }
7035    }
7036
7037    /// Preserve the member structure of an anonymous aggregate.
7038    ///
7039    /// An anonymous union with no declarator promotes its fields into the
7040    /// containing aggregate. An anonymous struct/union followed by a named
7041    /// declarator, such as `struct { T *ops; } sock`, declares both the field
7042    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
7043    /// the declarator's structured nested identity so a later `value.sock.ops`
7044    /// chain can traverse it without parsing a type spelling (#2407).
7045    ///
7046    /// An anonymous aggregate declares no tag, so there is nothing for the C
7047    /// dialect to re-scope and nothing for the C++ dialect to nest: both
7048    /// readings of this shape are the same, and both need these members
7049    /// (#3301).
7050    fn visit_anonymous_aggregate_declaration<'tree>(
7051        &mut self,
7052        node: Node<'tree>,
7053        scope: &ScopeInfo,
7054        in_class_body: bool,
7055        stack: &mut Vec<CppWork<'tree>>,
7056        ancestry: &ParentIndex<'tree>,
7057    ) -> bool {
7058        if !in_class_body || scope.class_unit.is_none() {
7059            return false;
7060        }
7061        let Some(aggregate) = node.child_by_field_name("type") else {
7062            return false;
7063        };
7064        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7065            || aggregate.child_by_field_name("name").is_some()
7066        {
7067            return false;
7068        }
7069        let Some(body) = cpp_body_node(aggregate) else {
7070            return false;
7071        };
7072
7073        let mut cursor = node.walk();
7074        let declarators = node
7075            .children_by_field_name("declarator", &mut cursor)
7076            .filter_map(|declarator| match classify_declarator(declarator) {
7077                Some(DeclaratorKind::Variable(variable)) => Some(variable),
7078                Some(DeclaratorKind::Function(_)) | None => None,
7079            })
7080            .collect::<Vec<_>>();
7081        if declarators.is_empty() {
7082            stack.push(CppWork::Container(CppContainer {
7083                node: body,
7084                scope: scope.clone(),
7085            }));
7086            return true;
7087        }
7088
7089        for declarator in declarators {
7090            let Some(name) = extract_variable_name(declarator, self.source) else {
7091                continue;
7092            };
7093            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
7094            // The receiver type's identity is the declarator's name, so its
7095            // range is the field declaration that carries that name token.
7096            // The inner specifier ends at the closing brace, which leaves the
7097            // name the unit is known by outside its own text (#3287).
7098            self.visit_named_class_like_shape(
7099                aggregate,
7100                name,
7101                Some(body),
7102                true,
7103                Some(cpp_declaration_range(node)),
7104                None,
7105                scope,
7106                stack,
7107                ancestry,
7108            );
7109        }
7110        true
7111    }
7112
7113    /// Give a function-local anonymous aggregate a structured owner so its
7114    /// direct pointer bindings can be typed by the usage graph.  Unlike an
7115    /// anonymous aggregate in a class body, there is no source-level tag or
7116    /// typedef to supply an identity.  The aggregate's CST range is therefore
7117    /// part of a generated, collision-resistant identity; no source spelling
7118    /// is consulted.  The declaration's own variable remains a file-level
7119    /// field projection, while the aggregate body is visited as the generated
7120    /// class's field list.
7121    fn visit_anonymous_local_aggregate_declaration<'tree>(
7122        &mut self,
7123        node: Node<'tree>,
7124        scope: &ScopeInfo,
7125        stack: &mut Vec<CppWork<'tree>>,
7126        ancestry: &ParentIndex<'tree>,
7127    ) -> bool {
7128        if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
7129            return false;
7130        }
7131        let Some(aggregate) = node.child_by_field_name("type") else {
7132            return false;
7133        };
7134        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7135            || aggregate.child_by_field_name("name").is_some()
7136        {
7137            return false;
7138        }
7139        let Some(body) = cpp_body_node(aggregate) else {
7140            return false;
7141        };
7142        let mut cursor = node.walk();
7143        let declarators = node
7144            .children_by_field_name("declarator", &mut cursor)
7145            .filter_map(|declarator| match classify_declarator(declarator) {
7146                Some(DeclaratorKind::Variable(variable)) => Some(variable),
7147                Some(DeclaratorKind::Function(_)) | None => None,
7148            })
7149            .collect::<Vec<_>>();
7150        if declarators.is_empty() {
7151            return false;
7152        }
7153
7154        for declarator in &declarators {
7155            self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
7156        }
7157        let name = format!("<anonymous:{}>", aggregate.start_byte());
7158        self.visit_named_class_like_shape(
7159            aggregate,
7160            name,
7161            Some(body),
7162            true,
7163            None,
7164            None,
7165            scope,
7166            stack,
7167            ancestry,
7168        );
7169        true
7170    }
7171
7172    fn visit_function_declaration<'tree>(
7173        &mut self,
7174        declaration_node: Node<'tree>,
7175        declarator: Node<'tree>,
7176        scope: &ScopeInfo,
7177        ancestry: &ParentIndex<'tree>,
7178    ) {
7179        let Some(function) = extract_function_info(declarator, self.source, scope) else {
7180            return;
7181        };
7182        let code_unit =
7183            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7184        if self.parsed.contains_declaration(&code_unit) {
7185            self.parsed
7186                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7187            return;
7188        }
7189        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7190        let signature = render_cpp_function_display_signature_from_node(
7191            declaration_node,
7192            self.source,
7193            scope.template_signature.as_deref(),
7194            false,
7195            ancestry,
7196        );
7197        self.parsed.add_signature_with_metadata(
7198            code_unit.clone(),
7199            cpp_signature_metadata(signature, declarator, self.source, ancestry)
7200                .with_declaration_only(true)
7201                .with_callable_linkage(cpp_callable_linkage(
7202                    declaration_node,
7203                    self.source,
7204                    ancestry,
7205                )),
7206        );
7207        if let Some(parent) = &scope.class_unit {
7208            self.parsed.add_child(parent.clone(), code_unit);
7209        } else if let Some(module) = &scope.module {
7210            self.parsed.add_child(module.clone(), code_unit);
7211        }
7212    }
7213
7214    fn visit_recovered_macro_qualified_function_declaration<'tree>(
7215        &mut self,
7216        declaration_node: Node<'tree>,
7217        call: Node<'tree>,
7218        scope: &ScopeInfo,
7219        ancestry: &ParentIndex<'tree>,
7220    ) {
7221        let Some(parent) = &scope.class_unit else {
7222            return;
7223        };
7224        let Some(name_node) = call.child_by_field_name("function") else {
7225            return;
7226        };
7227        let Some(arguments) = call.child_by_field_name("arguments") else {
7228            return;
7229        };
7230        let Some((signature, parameter_labels)) =
7231            recovered_macro_qualified_function_parameters(arguments, self.source)
7232        else {
7233            return;
7234        };
7235        let arity = parameter_labels.len();
7236        let function = FunctionInfo {
7237            package_name: scope.package_name.clone(),
7238            owner: Some(CppMemberOwner::Unit(parent.clone())),
7239            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
7240            signature,
7241        };
7242        if function.name.is_empty() {
7243            return;
7244        }
7245        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7246        if self.parsed.contains_declaration(&code_unit) {
7247            self.parsed
7248                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7249            return;
7250        }
7251        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7252        let signature_label = render_cpp_function_display_signature_from_node(
7253            declaration_node,
7254            self.source,
7255            scope.template_signature.as_deref(),
7256            false,
7257            ancestry,
7258        );
7259        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7260            .with_declaration_only(true)
7261            .with_callable_arity(CallableArity::exact(arity))
7262            .with_callable_linkage(cpp_callable_linkage(
7263                declaration_node,
7264                self.source,
7265                ancestry,
7266            ));
7267        self.parsed
7268            .add_signature_with_metadata(code_unit.clone(), metadata);
7269        self.parsed.add_child(parent.clone(), code_unit);
7270    }
7271
7272    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
7273        &mut self,
7274        declaration_node: Node<'tree>,
7275        call: Node<'tree>,
7276        scope: &ScopeInfo,
7277        ancestry: &ParentIndex<'tree>,
7278    ) {
7279        let Some(parent) = &scope.class_unit else {
7280            return;
7281        };
7282        let Some(arguments) = call.child_by_field_name("arguments") else {
7283            return;
7284        };
7285        let Some((mut signature, parameter_labels)) =
7286            recovered_macro_qualified_function_parameters(arguments, self.source)
7287        else {
7288            return;
7289        };
7290        if let Some(template_signature) = &scope.template_signature {
7291            signature = format!("{template_signature}{signature}");
7292        }
7293        let arity = parameter_labels.len();
7294        let function = FunctionInfo {
7295            package_name: scope.package_name.clone(),
7296            owner: Some(CppMemberOwner::Unit(parent.clone())),
7297            name: parent.identifier().to_string(),
7298            signature,
7299        };
7300        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7301        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7302        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
7303        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7304            .with_declaration_only(false)
7305            .with_callable_arity(CallableArity::exact(arity))
7306            .with_callable_linkage(cpp_callable_linkage(
7307                declaration_node,
7308                self.source,
7309                ancestry,
7310            ));
7311        self.parsed
7312            .add_signature_with_metadata(code_unit.clone(), metadata);
7313        self.parsed.add_child(parent.clone(), code_unit);
7314    }
7315
7316    fn visit_variable_declaration<'tree>(
7317        &mut self,
7318        declaration_node: Node<'tree>,
7319        declarator: Node<'tree>,
7320        scope: &ScopeInfo,
7321        in_class_body: bool,
7322        ancestry: &ParentIndex<'tree>,
7323    ) {
7324        let Some(name) = extract_variable_name(declarator, self.source) else {
7325            return;
7326        };
7327        let parent = if in_class_body {
7328            let Some(parent) = &scope.class_unit else {
7329                return;
7330            };
7331            Some(parent)
7332        } else {
7333            None
7334        };
7335        let short_name = match parent {
7336            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
7337            None => name.clone(),
7338        };
7339        let fq = cpp_leaf_fq(
7340            &scope.package_name,
7341            parent,
7342            &name,
7343            SegmentKind::Member,
7344            SegmentKind::Member,
7345        );
7346        let code_unit = CodeUnit::new_fq(
7347            self.file.clone(),
7348            CodeUnitType::Field,
7349            scope.package_name.clone(),
7350            short_name,
7351            fq,
7352        );
7353        if self.parsed.contains_declaration(&code_unit) {
7354            return;
7355        }
7356        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7357        self.parsed.add_signature_with_metadata(
7358            code_unit.clone(),
7359            SignatureMetadata::new(
7360                render_cpp_field_signature(declaration_node, declarator, self.source),
7361                Vec::new(),
7362            )
7363            .with_cpp_field_linkage(cpp_field_declaration_linkage(
7364                declaration_node,
7365                self.source,
7366                ancestry,
7367            )),
7368        );
7369        if let Some(parent) = &scope.class_unit {
7370            self.parsed.add_child(parent.clone(), code_unit);
7371        } else if let Some(module) = &scope.module {
7372            self.parsed.add_child(module.clone(), code_unit);
7373        }
7374    }
7375
7376    fn visit_class_members_from_declaration<'tree>(
7377        &mut self,
7378        node: Node<'tree>,
7379        scope: &ScopeInfo,
7380        ancestry: &ParentIndex<'tree>,
7381    ) {
7382        let mut cursor = node.walk();
7383        for child in node.named_children(&mut cursor) {
7384            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
7385                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
7386            } else if child.kind() == "init_declarator"
7387                && let Some(inner) = child.child_by_field_name("declarator")
7388            {
7389                self.visit_variable_declaration(node, inner, scope, true, ancestry);
7390            } else if matches!(
7391                child.kind(),
7392                "identifier"
7393                    | "field_identifier"
7394                    | "pointer_declarator"
7395                    | "reference_declarator"
7396                    | "array_declarator"
7397                    | "parenthesized_declarator"
7398            ) {
7399                self.visit_variable_declaration(node, child, scope, true, ancestry);
7400            }
7401        }
7402    }
7403
7404    fn visit_global_variables_from_declaration<'tree>(
7405        &mut self,
7406        node: Node<'tree>,
7407        scope: &ScopeInfo,
7408        ancestry: &ParentIndex<'tree>,
7409    ) {
7410        let mut cursor = node.walk();
7411        for child in node.named_children(&mut cursor) {
7412            if child.kind() == "init_declarator"
7413                && let Some(inner) = child.child_by_field_name("declarator")
7414            {
7415                self.visit_variable_declaration(node, inner, scope, false, ancestry);
7416            } else if matches!(
7417                child.kind(),
7418                "identifier"
7419                    | "field_identifier"
7420                    | "pointer_declarator"
7421                    | "reference_declarator"
7422                    | "array_declarator"
7423                    | "parenthesized_declarator"
7424            ) {
7425                self.visit_variable_declaration(node, child, scope, false, ancestry);
7426            }
7427        }
7428    }
7429
7430    fn visit_type_declaration<'tree>(
7431        &mut self,
7432        node: Node<'tree>,
7433        scope: &ScopeInfo,
7434        stack: &mut Vec<CppWork<'tree>>,
7435        ancestry: &ParentIndex<'tree>,
7436    ) {
7437        let type_node = node.child_by_field_name("type");
7438        if let Some(type_node) = type_node
7439            && matches!(
7440                type_node.kind(),
7441                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7442            )
7443        {
7444            self.visit_class_like(type_node, scope, stack, ancestry);
7445        }
7446
7447        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
7448            let range = Range {
7449                start_byte: node.start_byte(),
7450                end_byte: recovered.end_node.end_byte(),
7451                start_line: node.start_position().row + 1,
7452                end_line: recovered.end_node.end_position().row + 1,
7453            };
7454            let signature = self
7455                .source
7456                .get(range.start_byte..range.end_byte)
7457                .map(normalize_cpp_whitespace)
7458                .unwrap_or_default();
7459            self.record_type_aliases(
7460                node,
7461                scope,
7462                vec![recovered.name],
7463                signature,
7464                range,
7465                ancestry,
7466            );
7467            return;
7468        }
7469
7470        let alias_names = match node.kind() {
7471            "alias_declaration" => extract_alias_declaration_name(node, self.source)
7472                .into_iter()
7473                .collect::<Vec<_>>(),
7474            "type_definition" => extract_typedef_alias_names(node, self.source),
7475            _ => Vec::new(),
7476        };
7477        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
7478            (type_node, alias_names.as_slice())
7479            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
7480            && type_node.child_by_field_name("name").is_none()
7481        {
7482            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
7483        } else {
7484            None
7485        };
7486        self.add_type_aliases(node, scope, alias_names, ancestry);
7487        if let Some((body, alias_name)) = anonymous_aggregate {
7488            // The typedef alias is also the only user-visible identity of an
7489            // anonymous aggregate. Reuse it as the member owner instead of
7490            // minting a second signatureless class with the same FQN. The
7491            // latter makes forward lookup ambiguous when conditional aliases
7492            // coexist and returns duplicate definitions even without guards.
7493            let signature = normalize_cpp_whitespace(node_text(node, self.source));
7494            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
7495            debug_assert!(self.parsed.contains_declaration(&alias_unit));
7496            let mut nested_scope = scope.clone();
7497            nested_scope.class_unit = Some(alias_unit);
7498            nested_scope.template_signature = scope.template_signature.clone();
7499            nested_scope.template_metadata = None;
7500            nested_scope.declarations_are_fields = false;
7501            nested_scope.recovered_specialization_member_scope = false;
7502            stack.push(CppWork::Container(CppContainer {
7503                node: body,
7504                scope: nested_scope,
7505            }));
7506        }
7507    }
7508
7509    fn add_type_aliases(
7510        &mut self,
7511        node: Node<'_>,
7512        scope: &ScopeInfo,
7513        alias_names: Vec<String>,
7514        ancestry: &ParentIndex<'_>,
7515    ) {
7516        let signature = normalize_cpp_whitespace(node_text(node, self.source));
7517        self.record_type_aliases(
7518            node,
7519            scope,
7520            alias_names,
7521            signature,
7522            cpp_declaration_range(node),
7523            ancestry,
7524        );
7525    }
7526
7527    fn record_type_aliases(
7528        &mut self,
7529        node: Node<'_>,
7530        scope: &ScopeInfo,
7531        alias_names: Vec<String>,
7532        signature: String,
7533        range: Range,
7534        ancestry: &ParentIndex<'_>,
7535    ) {
7536        if signature.is_empty() {
7537            return;
7538        }
7539        let type_name = node
7540            .child_by_field_name("type")
7541            .and_then(|type_node| type_node.child_by_field_name("name"))
7542            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
7543        for alias_name in alias_names {
7544            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
7545                continue;
7546            }
7547            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
7548            // Declaration identity does not include the alias signature. Keep
7549            // each physical range so conditional aliases retain their guards.
7550            self.add_declaration_with_range(code_unit.clone(), range, None, None);
7551            let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
7552            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
7553                cpp_structured_type_identity(type_node, self.source, &lexical_scope)
7554            });
7555            self.parsed.add_signature_with_metadata(
7556                code_unit.clone(),
7557                SignatureMetadata::new(signature.clone(), Vec::new())
7558                    .with_underlying_type_identity(underlying_type_identity),
7559            );
7560            if let Some(metadata) = &scope.template_metadata {
7561                let mut metadata = metadata.clone();
7562                metadata.primary_fq_name = code_unit.fq_name();
7563                self.parsed
7564                    .set_cpp_template_metadata(code_unit.clone(), metadata);
7565            }
7566            if let Some(parent) = &scope.class_unit {
7567                self.parsed.add_child(parent.clone(), code_unit.clone());
7568            } else if let Some(module) = &scope.module {
7569                self.parsed.add_child(module.clone(), code_unit.clone());
7570            }
7571            self.parsed.mark_type_alias(code_unit);
7572        }
7573    }
7574
7575    fn type_alias_unit(
7576        &self,
7577        scope: &ScopeInfo,
7578        alias_name: String,
7579        signature: String,
7580    ) -> CodeUnit {
7581        let short_name = if let Some(parent) = &scope.class_unit {
7582            cpp_join_nested_short(parent.short_name(), &alias_name)
7583        } else {
7584            alias_name.clone()
7585        };
7586        let fq = cpp_leaf_fq(
7587            &scope.package_name,
7588            scope.class_unit.as_ref(),
7589            &alias_name,
7590            SegmentKind::Nested,
7591            SegmentKind::Type,
7592        );
7593        CodeUnit::with_signature_and_fq(
7594            self.file.clone(),
7595            CodeUnitType::Class,
7596            scope.package_name.clone(),
7597            short_name,
7598            Some(signature),
7599            false,
7600            fq,
7601        )
7602    }
7603
7604    fn visit_macro(&mut self, node: Node<'_>) {
7605        // A comment can terminate tree-sitter's preproc_arg before the logical
7606        // directive ends. Its remaining declarations are replacement locals,
7607        // not file-scope fields, even when recovery exposes them as siblings.
7608        if let Some(replacement) =
7609            crate::graph::syntax::function_macro_replacement_span(node, self.source)
7610        {
7611            self.consumed_fragment_regions
7612                .push((replacement.start, replacement.end));
7613        }
7614        let Some(name) = extract_macro_name(node, self.source) else {
7615            return;
7616        };
7617        let signature = node_text(node, self.source).trim_end().to_string();
7618        if signature.is_empty() {
7619            return;
7620        }
7621        let fq = cpp_member_fq("", &name);
7622        // A macro can be undefined and redefined later in the same file. Its
7623        // structured directive is part of the declaration identity so the
7624        // temporal environment can navigate to the definition active at a
7625        // reference instead of collapsing every spelling to the first range.
7626        // The same physical directive parsed through another C/C++ reading
7627        // still produces the same unit and remains deduplicated.
7628        let code_unit = CodeUnit::with_signature_and_fq(
7629            self.file.clone(),
7630            CodeUnitType::Macro,
7631            "",
7632            name.clone(),
7633            Some(signature.clone()),
7634            false,
7635            fq,
7636        );
7637        if !self.parsed.contains_declaration(&code_unit) {
7638            self.add_declaration(code_unit.clone(), node, None, None);
7639            let name_range = node
7640                .child_by_field_name("name")
7641                .map(cpp_declaration_range)
7642                .unwrap_or_else(|| cpp_declaration_range(node));
7643            self.parsed
7644                .record_materialization(MaterializationRecord::GeneratedDeclaration {
7645                    site: cpp_declaration_range(node),
7646                    argument: name_range,
7647                    kind: GenerationKind::PreprocessorDefinition,
7648                    unit: code_unit.clone(),
7649                });
7650            self.parsed.add_signature(code_unit, signature);
7651        }
7652        if node.kind() == "preproc_def" {
7653            update_object_macro_field_environment(
7654                node,
7655                self.source,
7656                &mut self.object_macro_fields,
7657                &mut self.ambiguous_object_macro_fields,
7658            );
7659        } else {
7660            self.object_macro_fields.remove(&name);
7661            self.ambiguous_object_macro_fields.remove(&name);
7662        }
7663    }
7664
7665    fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7666        let Some(directive) = node.child_by_field_name("directive") else {
7667            return;
7668        };
7669        let name = node_text(directive, self.source).trim();
7670        let range = cpp_declaration_range(node);
7671        let fields = object_macro_field_closure(&self.object_macro_fields, name);
7672        self.materialize_object_macro_fields(fields, range, scope);
7673    }
7674
7675    /// Bare object-like field-list macros inside an otherwise well-formed
7676    /// aggregate remain identifier nodes in a field declaration. More than
7677    /// one adjacent invocation can be folded into the same declaration, so
7678    /// inspect all of its structured identifier nodes.
7679    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7680        if !matches!(node.kind(), "declaration" | "field_declaration") {
7681            return false;
7682        }
7683        // Claiming the declaration means its members are accounted for, which
7684        // is only true when there is an owner to materialize them into. A file
7685        // scope reaches this with a declaration that merely *contains* an
7686        // aggregate spelling an invocation (tree-sitter wraps libuv's
7687        // `struct uv_fs_poll_s { UV_HANDLE_FIELDS ... }` in a top-level
7688        // `field_declaration` under a malformed header), and consuming it there
7689        // would discard the aggregate itself (#2985).
7690        if scope.class_unit.is_none() {
7691            return false;
7692        }
7693        let macro_nodes =
7694            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7695        for macro_node in &macro_nodes {
7696            let name = node_text(*macro_node, self.source).trim();
7697            let fields = object_macro_field_closure(&self.object_macro_fields, name);
7698            self.materialize_object_macro_fields(fields, cpp_declaration_range(*macro_node), scope);
7699        }
7700        let Some(last) = macro_nodes.last() else {
7701            return false;
7702        };
7703        // Claiming the declaration accounts for every member it spells, not
7704        // only the donated ones: an invocation folded together with the
7705        // members after it (libuv's `UV_HANDLE_FIELDS ... void* poll_ctx;`)
7706        // leaves those members shapeless, and the grammar recovers them from
7707        // the byte range that follows the invocation.
7708        self.record_collapsed_aggregate_fields(
7709            last.end_byte()..node.end_byte(),
7710            node.start_position().row
7711                + 1
7712                + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
7713            scope,
7714        );
7715        true
7716    }
7717
7718    fn materialize_object_macro_fields(
7719        &mut self,
7720        fields: Vec<MacroReplacementField>,
7721        range: Range,
7722        scope: &ScopeInfo,
7723    ) {
7724        let Some(owner) = scope.class_unit.as_ref() else {
7725            return;
7726        };
7727        for field in fields {
7728            let signature = field.declaration.clone();
7729            let mut fq = owner.fq().clone();
7730            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7731            let short_name = if owner.short_name().is_empty() {
7732                field.name.clone()
7733            } else {
7734                format!("{}.{}", owner.short_name(), field.name)
7735            };
7736            let code_unit = CodeUnit::with_signature_and_fq(
7737                self.file.clone(),
7738                CodeUnitType::Field,
7739                owner.package_name().to_string(),
7740                short_name,
7741                Some(field.declaration),
7742                true,
7743                fq,
7744            );
7745            if self.parsed.contains_declaration(&code_unit) {
7746                continue;
7747            }
7748            self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7749            self.parsed.add_signature(code_unit, signature);
7750        }
7751    }
7752
7753    /// Recover ordinary aggregates whose field-list macro invocations made
7754    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
7755    /// The class markers, aggregate braces, field nodes, and macro identifier
7756    /// nodes are all read from the AST; no source delimiter or token scan is
7757    /// used. A close-brace node inside a malformed field closes the innermost
7758    /// recovered aggregate, preserving nested-owner identity.
7759    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7760        let mut cursor = node.walk();
7761        let children = node.children(&mut cursor).collect::<Vec<_>>();
7762        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember>)>::new();
7763        let mut object_macro_fields = self.object_macro_fields.clone();
7764        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
7765        let mut open = Vec::<usize>::new();
7766        let mut index = 0;
7767        while index < children.len() {
7768            let keyword = children[index];
7769            if update_object_macro_field_environment(
7770                keyword,
7771                self.source,
7772                &mut object_macro_fields,
7773                &mut ambiguous_object_macro_fields,
7774            ) {
7775                index += 1;
7776                continue;
7777            }
7778            if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
7779                let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7780                if !name.is_empty() {
7781                    let parent = open
7782                        .last()
7783                        .and_then(|class| recovered.get(*class))
7784                        .map(|(owner, _, _, _)| owner.clone())
7785                        .or_else(|| scope.class_unit.clone());
7786                    let short_name = parent.as_ref().map_or_else(
7787                        || name.clone(),
7788                        |parent| cpp_join_nested_short(parent.short_name(), &name),
7789                    );
7790                    let fq = cpp_leaf_fq(
7791                        &scope.package_name,
7792                        parent.as_ref(),
7793                        &name,
7794                        SegmentKind::Nested,
7795                        SegmentKind::Type,
7796                    );
7797                    let owner = CodeUnit::with_signature_and_fq(
7798                        self.file.clone(),
7799                        CodeUnitType::Class,
7800                        scope.package_name.clone(),
7801                        short_name,
7802                        None,
7803                        false,
7804                        fq,
7805                    );
7806                    recovered.push((
7807                        owner,
7808                        head.key.start_byte(),
7809                        head.opening.end_byte(),
7810                        Vec::new(),
7811                    ));
7812                    let class_index = recovered.len() - 1;
7813                    match head.folded_members {
7814                        // The ordinary shape leaves the aggregate open: every
7815                        // child that follows belongs to it until a close brace
7816                        // ends it.
7817                        None => open.push(class_index),
7818                        // A folded head carries its own member list and its own
7819                        // closing brace, so it closes here. The invocations that
7820                        // precede the class key are the dangling tail of the
7821                        // replacement that folded it, and belong to whatever
7822                        // aggregate is open around it, not to this one.
7823                        Some(members) => {
7824                            let macro_nodes = object_macro_identifier_nodes_with_environment(
7825                                children[index],
7826                                self.source,
7827                                &mut object_macro_fields,
7828                                &mut ambiguous_object_macro_fields,
7829                            );
7830                            let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7831                                .iter()
7832                                .partition(|node| node.start_byte() < head.key.start_byte());
7833                            if let Some(&enclosing) = open.last() {
7834                                for macro_node in preceding {
7835                                    recovered[enclosing]
7836                                        .3
7837                                        .push(CppCollapsedMember::MacroFields {
7838                                            range: cpp_declaration_range(macro_node),
7839                                            fields: object_macro_field_closure(
7840                                                &object_macro_fields,
7841                                                &normalize_cpp_whitespace(node_text(
7842                                                    macro_node,
7843                                                    self.source,
7844                                                )),
7845                                            ),
7846                                        });
7847                                }
7848                            }
7849                            let closing = cpp_collapsed_aggregate_closing_brace(members);
7850                            recovered[class_index]
7851                                .3
7852                                .extend(cpp_collapsed_aggregate_members(
7853                                    &inner,
7854                                    head.opening.end_byte()..closing,
7855                                    head.opening.end_position().row + 1,
7856                                    self.source,
7857                                    &object_macro_fields,
7858                                ));
7859                            recovered[class_index].2 = members.end_byte();
7860                            for &open_class in &open {
7861                                recovered[open_class].2 =
7862                                    recovered[open_class].2.max(members.end_byte());
7863                            }
7864                        }
7865                    }
7866                    index += head.width;
7867                    continue;
7868                }
7869            }
7870            if let Some(&class_index) = open.last()
7871                && children[index].kind() == "field_declaration"
7872            {
7873                let field = children[index];
7874                let macro_nodes = object_macro_identifier_nodes_with_environment(
7875                    field,
7876                    self.source,
7877                    &mut object_macro_fields,
7878                    &mut ambiguous_object_macro_fields,
7879                );
7880                recovered[class_index]
7881                    .3
7882                    .extend(cpp_collapsed_aggregate_members(
7883                        &macro_nodes,
7884                        field.start_byte()..field.end_byte(),
7885                        field.start_position().row + 1,
7886                        self.source,
7887                        &object_macro_fields,
7888                    ));
7889                let end = field.end_byte();
7890                for &open_class in &open {
7891                    recovered[open_class].2 = recovered[open_class].2.max(end);
7892                }
7893                let closes = count_close_brace_nodes(field);
7894                for _ in 0..closes {
7895                    if let Some(closed) = open.pop() {
7896                        recovered[closed].2 = end;
7897                    }
7898                }
7899            }
7900            index += 1;
7901        }
7902
7903        let mut owners = Vec::with_capacity(recovered.len());
7904        for (owner, start, end, members) in recovered {
7905            let parent = owners
7906                .iter()
7907                .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
7908                .cloned()
7909                .or_else(|| scope.class_unit.clone());
7910            self.declare_collapsed_aggregate(owner.clone(), start..end, parent, members, scope);
7911            owners.push(owner);
7912        }
7913    }
7914
7915    /// Record one recovered aggregate and the members it declares.
7916    fn declare_collapsed_aggregate(
7917        &mut self,
7918        owner: CodeUnit,
7919        span: std::ops::Range<usize>,
7920        parent: Option<CodeUnit>,
7921        members: Vec<CppCollapsedMember>,
7922        scope: &ScopeInfo,
7923    ) {
7924        let range = Range {
7925            start_byte: span.start,
7926            end_byte: span.end,
7927            start_line: self.source.get(..span.start).map_or(1, |source| {
7928                source.bytes().filter(|byte| *byte == b'\n').count() + 1
7929            }),
7930            end_line: self.source.get(..span.end).map_or(1, |source| {
7931                source.bytes().filter(|byte| *byte == b'\n').count() + 1
7932            }),
7933        };
7934        // The tag can already be declared by a typedef or a forward
7935        // declaration, and this range is the definition's. Record it either
7936        // way: a declaration carries every range it is written at, and
7937        // dropping this one leaves nothing able to prove that the aggregate
7938        // has a body (#3098). Recording a range that is already held, or a
7939        // unit that is already declared, changes nothing.
7940        self.add_declaration_with_range(owner.clone(), range, parent, None);
7941        let owner_scope = ScopeInfo {
7942            class_unit: Some(owner),
7943            declarations_are_fields: true,
7944            ..scope.clone()
7945        };
7946        for member in members {
7947            match member {
7948                CppCollapsedMember::MacroFields { range, fields } => {
7949                    self.materialize_object_macro_fields(fields, range, &owner_scope);
7950                }
7951                CppCollapsedMember::Declarations { span, start_line } => {
7952                    self.record_collapsed_aggregate_fields(span, start_line, &owner_scope);
7953                }
7954            }
7955        }
7956    }
7957
7958    /// Recover an aggregate whose head tree-sitter folded into the declaration
7959    /// that precedes it, outside any collapsed `ERROR` container.
7960    ///
7961    /// A comment inside a field-list macro's replacement ends the replacement
7962    /// token, so the rest of the replacement is read at file scope. When the
7963    /// aggregate that follows declares its member list with one invocation,
7964    /// the parser reads the dangling tail as that declaration's type, the
7965    /// class key as an `ERROR`, and the member list as an initializer (#3098).
7966    /// The invocations before the class key belong to the replacement, not to
7967    /// this aggregate, so they go to whatever aggregate encloses it.
7968    fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7969        if node.parent().is_some_and(|parent| parent.is_error()) {
7970            // The container's own recovery reads this child in source order,
7971            // with the preprocessor environment its earlier children build.
7972            return false;
7973        }
7974        let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
7975            return false;
7976        };
7977        let members = head
7978            .folded_members
7979            .expect("a folded aggregate head carries its member list");
7980        let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7981        if name.is_empty() {
7982            return false;
7983        }
7984        let macro_nodes =
7985            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7986        let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7987            .iter()
7988            .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
7989        for macro_node in preceding {
7990            let fields = object_macro_field_closure(
7991                &self.object_macro_fields,
7992                &normalize_cpp_whitespace(node_text(macro_node, self.source)),
7993            );
7994            self.materialize_object_macro_fields(fields, cpp_declaration_range(macro_node), scope);
7995        }
7996        let parent = scope.class_unit.clone();
7997        let short_name = parent.as_ref().map_or_else(
7998            || name.clone(),
7999            |parent| cpp_join_nested_short(parent.short_name(), &name),
8000        );
8001        let fq = cpp_leaf_fq(
8002            &scope.package_name,
8003            parent.as_ref(),
8004            &name,
8005            SegmentKind::Nested,
8006            SegmentKind::Type,
8007        );
8008        let owner = CodeUnit::with_signature_and_fq(
8009            self.file.clone(),
8010            CodeUnitType::Class,
8011            scope.package_name.clone(),
8012            short_name,
8013            None,
8014            false,
8015            fq,
8016        );
8017        let recovered = cpp_collapsed_aggregate_members(
8018            &inner,
8019            head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
8020            head.opening.end_position().row + 1,
8021            self.source,
8022            &self.object_macro_fields,
8023        );
8024        self.declare_collapsed_aggregate(
8025            owner,
8026            head.key.start_byte()..members.end_byte(),
8027            parent,
8028            recovered,
8029            scope,
8030        );
8031        true
8032    }
8033
8034    /// Record the members of a collapsed aggregate region as fields of its
8035    /// recovered owner. The names, declaration text, and byte ranges all come
8036    /// from the grammar's reading of the region, so a region that is not a
8037    /// member list contributes nothing.
8038    fn record_collapsed_aggregate_fields(
8039        &mut self,
8040        span: std::ops::Range<usize>,
8041        start_line: usize,
8042        scope: &ScopeInfo,
8043    ) {
8044        let Some(owner) = scope.class_unit.as_ref() else {
8045            return;
8046        };
8047        for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
8048            let range = Range {
8049                start_byte: field.range.start,
8050                end_byte: field.range.end,
8051                start_line: start_line
8052                    + cpp_line_breaks_between(self.source, span.start, field.range.start),
8053                end_line: start_line
8054                    + cpp_line_breaks_between(self.source, span.start, field.range.end),
8055            };
8056            let mut fq = owner.fq().clone();
8057            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
8058            let short_name = if owner.short_name().is_empty() {
8059                field.name.clone()
8060            } else {
8061                format!("{}.{}", owner.short_name(), field.name)
8062            };
8063            let signature = normalize_cpp_whitespace(&field.declaration);
8064            let code_unit = CodeUnit::with_signature_and_fq(
8065                self.file.clone(),
8066                CodeUnitType::Field,
8067                owner.package_name().to_string(),
8068                short_name,
8069                Some(signature.clone()),
8070                false,
8071                fq,
8072            );
8073            if self.parsed.contains_declaration(&code_unit) {
8074                continue;
8075            }
8076            self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
8077            self.parsed.add_signature(code_unit, signature);
8078        }
8079    }
8080
8081    fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
8082        let Some(_directive) = node.child_by_field_name("directive") else {
8083            return;
8084        };
8085        if is_cpp_undef_directive(node, self.source) {
8086            update_object_macro_field_environment(
8087                node,
8088                self.source,
8089                &mut self.object_macro_fields,
8090                &mut self.ambiguous_object_macro_fields,
8091            );
8092            return;
8093        }
8094        let directly_in_field_list = node
8095            .parent()
8096            .is_some_and(|parent| parent.kind() == "field_declaration_list");
8097        if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
8098            self.visit_object_macro_fields(node, scope);
8099        }
8100    }
8101}
8102
8103/// Every member a field-list macro contributes under `environment`, including
8104/// the members of the field-list macros it composes.
8105///
8106/// Composition is resolved at the invocation, not at the definition, because a
8107/// nested name's active replacement is a property of the environment the
8108/// invocation sees. The traversal is iterative and visits each name once, so a
8109/// self-composing or mutually composing pair terminates.
8110fn object_macro_field_closure(
8111    environment: &HashMap<String, ObjectMacroReplacement>,
8112    name: &str,
8113) -> Vec<MacroReplacementField> {
8114    let mut fields = Vec::new();
8115    let mut visited = HashSet::default();
8116    let mut stack = vec![name.to_string()];
8117    while let Some(current) = stack.pop() {
8118        if !visited.insert(current.clone()) {
8119            continue;
8120        }
8121        let Some(replacement) = environment.get(&current) else {
8122            continue;
8123        };
8124        fields.extend(replacement.fields.iter().cloned());
8125        stack.extend(replacement.nested.iter().rev().cloned());
8126    }
8127    fields
8128}
8129
8130/// The structured replacement an object-like `#define` contributes.
8131///
8132/// The directive's whole logical line is the replacement list, which
8133/// [`object_macro_replacement_span`] recovers, because a comment inside the
8134/// replacement ends tree-sitter's `preproc_arg` token early.
8135fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
8136    crate::graph::syntax::object_macro_replacement_span(node, source)
8137        .and_then(|span| source.get(span))
8138        .map(crate::graph::syntax::object_macro_replacement)
8139        .unwrap_or_default()
8140}
8141
8142/// Apply one preprocessor directive to an object-like field-list environment.
8143/// The environment is intentionally separate from the declaration visitor so
8144/// malformed parser regions can replay directives in source order without
8145/// mutating the live walk's state ahead of those directives.
8146fn update_object_macro_field_environment(
8147    node: Node<'_>,
8148    source: &str,
8149    fields: &mut HashMap<String, ObjectMacroReplacement>,
8150    ambiguous: &mut HashSet<String>,
8151) -> bool {
8152    match node.kind() {
8153        "preproc_def" => {
8154            let Some(name) = extract_macro_name(node, source) else {
8155                return false;
8156            };
8157            let replacement = object_macro_replacement_of(node, source);
8158            if replacement.is_empty() || ambiguous.contains(&name) {
8159                fields.remove(&name);
8160                ambiguous.insert(name);
8161            } else if let Some(previous) = fields.get(&name) {
8162                if previous != &replacement {
8163                    fields.remove(&name);
8164                    ambiguous.insert(name);
8165                }
8166            } else {
8167                fields.insert(name, replacement);
8168            }
8169            true
8170        }
8171        "preproc_call" if is_cpp_undef_directive(node, source) => {
8172            if let Some(argument) = node.child_by_field_name("argument") {
8173                let name = node_text(argument, source).trim();
8174                fields.remove(name);
8175                if inside_preprocessor_conditional(node) {
8176                    ambiguous.insert(name.to_string());
8177                } else {
8178                    ambiguous.remove(name);
8179                }
8180            }
8181            true
8182        }
8183        _ => false,
8184    }
8185}
8186
8187fn object_macro_identifier_nodes<'tree>(
8188    node: Node<'tree>,
8189    source: &str,
8190    fields: &HashMap<String, ObjectMacroReplacement>,
8191) -> Vec<Node<'tree>> {
8192    let mut result = Vec::new();
8193    let mut stack = vec![node];
8194    while let Some(current) = stack.pop() {
8195        if matches!(
8196            current.kind(),
8197            "identifier" | "field_identifier" | "type_identifier"
8198        ) && fields.contains_key(node_text(current, source).trim())
8199        {
8200            result.push(current);
8201        }
8202        let mut cursor = current.walk();
8203        let children = current.children(&mut cursor).collect::<Vec<_>>();
8204        stack.extend(children.into_iter().rev());
8205    }
8206    result.sort_by_key(|node| node.start_byte());
8207    result
8208}
8209
8210/// Collect macro identifiers while replaying any nested preprocessor
8211/// directives in source order. Recovery regions can contain a later `#undef`
8212/// in the same parser error envelope; using the live visitor map for the
8213/// entire envelope would incorrectly materialize invocations after it.
8214fn object_macro_identifier_nodes_with_environment<'tree>(
8215    node: Node<'tree>,
8216    source: &str,
8217    fields: &mut HashMap<String, ObjectMacroReplacement>,
8218    ambiguous: &mut HashSet<String>,
8219) -> Vec<Node<'tree>> {
8220    let mut result = Vec::new();
8221    let mut stack = vec![node];
8222    while let Some(current) = stack.pop() {
8223        if update_object_macro_field_environment(current, source, fields, ambiguous) {
8224            continue;
8225        }
8226        if matches!(
8227            current.kind(),
8228            "identifier" | "field_identifier" | "type_identifier"
8229        ) && fields.contains_key(node_text(current, source).trim())
8230        {
8231            result.push(current);
8232        }
8233        let mut cursor = current.walk();
8234        let children = current.children(&mut cursor).collect::<Vec<_>>();
8235        stack.extend(children.into_iter().rev());
8236    }
8237    result.sort_by_key(|node| node.start_byte());
8238    result
8239}
8240
8241/// One member region of an aggregate whose head and body tree-sitter collapsed
8242/// into an `ERROR` container, in source order.
8243enum CppCollapsedMember {
8244    /// The members a field-list macro invocation donates, all sharing the
8245    /// invocation's range.
8246    MacroFields {
8247        range: Range,
8248        fields: Vec<MacroReplacementField>,
8249    },
8250    /// A byte span whose ordinary member declarations lost their shape with
8251    /// the aggregate body, with the 1-based line the span starts on.
8252    Declarations {
8253        span: std::ops::Range<usize>,
8254        start_line: usize,
8255    },
8256}
8257
8258/// One aggregate head recovered from the children of a collapsed `ERROR`
8259/// container.
8260///
8261/// A member list that opens with an object-like field-list macro invocation
8262/// has no grammar rule, so the aggregate never forms a specifier and its head
8263/// reaches the tree as loose children. Two shapes carry it. Ordinarily the
8264/// class key, the name, and the opening brace are three consecutive children.
8265/// When the aggregate follows the dangling tail of a comment-truncated
8266/// replacement, the parser reads that tail as the declaration's type and folds
8267/// the whole aggregate into it: libuv's `UV_HANDLE_PRIVATE_FIELDS` line ends
8268/// the `UV_HANDLE_FIELDS` replacement token, and the `struct uv_handle_s { ...
8269/// }` that follows becomes one `field_declaration` whose class key is an
8270/// `ERROR`, whose name is a `field_identifier`, and whose member list is an
8271/// `initializer_list` (#3098).
8272struct CppCollapsedAggregateHead<'tree> {
8273    key: Node<'tree>,
8274    name: Node<'tree>,
8275    opening: Node<'tree>,
8276    /// The member list when the head folded it into its own node, which also
8277    /// carries the aggregate's closing brace. `None` leaves the aggregate open
8278    /// for the children that follow it.
8279    folded_members: Option<Node<'tree>>,
8280    /// How many children the head consumes.
8281    width: usize,
8282}
8283
8284/// The aggregate head that starts at `children[index]`, if one does.
8285fn cpp_collapsed_aggregate_head<'tree>(
8286    children: &[Node<'tree>],
8287    index: usize,
8288    source: &str,
8289) -> Option<CppCollapsedAggregateHead<'tree>> {
8290    let key = *children.get(index)?;
8291    if matches!(key.kind(), "struct" | "class" | "union") {
8292        let name = *children.get(index + 1)?;
8293        let opening = *children.get(index + 2)?;
8294        if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
8295            return None;
8296        }
8297        return Some(CppCollapsedAggregateHead {
8298            key,
8299            name,
8300            opening,
8301            folded_members: None,
8302            width: 3,
8303        });
8304    }
8305    cpp_folded_aggregate_head(key, source)
8306}
8307
8308/// The aggregate head tree-sitter folded into one declaration node, reading
8309/// the class key as an `ERROR` and the member list as an initializer.
8310fn cpp_folded_aggregate_head<'tree>(
8311    node: Node<'tree>,
8312    source: &str,
8313) -> Option<CppCollapsedAggregateHead<'tree>> {
8314    if !matches!(node.kind(), "declaration" | "field_declaration") {
8315        return None;
8316    }
8317    let mut cursor = node.walk();
8318    let children = node.children(&mut cursor).collect::<Vec<_>>();
8319    let key_index = children.iter().position(|child| {
8320        child.is_error()
8321            && matches!(
8322                node_text(*child, source).trim(),
8323                "struct" | "class" | "union"
8324            )
8325    })?;
8326    let declarator = *children.get(key_index + 1)?;
8327    // At file scope the grammar has an `init_declarator` for the name and its
8328    // initializer; inside an `ERROR` container the two are loose siblings.
8329    let (name, members) = if declarator.kind() == "init_declarator" {
8330        (
8331            declarator.child_by_field_name("declarator")?,
8332            declarator.child_by_field_name("value")?,
8333        )
8334    } else {
8335        (declarator, *children.get(key_index + 2)?)
8336    };
8337    if !matches!(
8338        name.kind(),
8339        "field_identifier" | "type_identifier" | "identifier"
8340    ) {
8341        return None;
8342    }
8343    if members.kind() != "initializer_list" {
8344        return None;
8345    }
8346    let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
8347    Some(CppCollapsedAggregateHead {
8348        key: children[key_index],
8349        name,
8350        opening,
8351        folded_members: Some(members),
8352        width: 1,
8353    })
8354}
8355
8356/// Where a folded member list ends: at its own closing brace when it has one,
8357/// so the reparse of the declarations it holds sees a member list and not a
8358/// stray brace.
8359fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
8360    let mut cursor = members.walk();
8361    members
8362        .children(&mut cursor)
8363        .filter(|child| child.kind() == "}" && !child.is_missing())
8364        .last()
8365        .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
8366}
8367
8368/// The members one collapsed aggregate region declares, in source order: what
8369/// its field-list macro invocations donate, then the ordinary declarations
8370/// that follow the last invocation and lost their shape with the body.
8371fn cpp_collapsed_aggregate_members(
8372    macro_nodes: &[Node<'_>],
8373    region: std::ops::Range<usize>,
8374    region_start_line: usize,
8375    source: &str,
8376    environment: &HashMap<String, ObjectMacroReplacement>,
8377) -> Vec<CppCollapsedMember> {
8378    let mut members = macro_nodes
8379        .iter()
8380        .map(|macro_node| CppCollapsedMember::MacroFields {
8381            range: cpp_declaration_range(*macro_node),
8382            fields: object_macro_field_closure(
8383                environment,
8384                &normalize_cpp_whitespace(node_text(*macro_node, source)),
8385            ),
8386        })
8387        .collect::<Vec<_>>();
8388    // Whatever follows the last invocation is an ordinary member list that
8389    // lost its declaration shape with the aggregate's body; the grammar
8390    // recovers it from its own byte range.
8391    let declarations_start = macro_nodes
8392        .last()
8393        .map_or(region.start, |macro_node| macro_node.end_byte());
8394    members.push(CppCollapsedMember::Declarations {
8395        span: declarations_start..region.end,
8396        start_line: region_start_line
8397            + cpp_line_breaks_between(source, region.start, declarations_start),
8398    });
8399    members
8400}
8401
8402/// Line breaks in `source[from..to]`, for mapping a recovered byte offset to a
8403/// line without rescanning the file from its start.
8404fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
8405    source.get(from..to).map_or(0, |slice| {
8406        slice.bytes().filter(|byte| *byte == b'\n').count()
8407    })
8408}
8409
8410/// How many enclosing aggregates a collapsed member declaration closes: the
8411/// close braces it carries that its own braces do not balance. A member that
8412/// declares a nested aggregate of its own (`union { ... } active_reqs;` in
8413/// libuv's `uv_loop_s`) closes nothing, so counting every `}` node ended the
8414/// owner early and lost every member after it (#2985).
8415fn count_close_brace_nodes(node: Node<'_>) -> usize {
8416    let mut opened = 0usize;
8417    let mut closed = 0usize;
8418    let mut stack = vec![node];
8419    while let Some(current) = stack.pop() {
8420        if !current.is_missing() {
8421            match current.kind() {
8422                "{" => opened += 1,
8423                "}" => closed += 1,
8424                _ => {}
8425            }
8426        }
8427        let mut cursor = current.walk();
8428        stack.extend(current.children(&mut cursor));
8429    }
8430    closed.saturating_sub(opened)
8431}
8432
8433/// Classify a C++ field while its declaration syntax is already available.
8434///
8435/// The persisted result lets later visibility queries avoid reparsing the
8436/// complete source file only to recover linkage.
8437pub fn cpp_field_declaration_linkage<'tree>(
8438    declaration: Node<'tree>,
8439    source: &str,
8440    ancestry: &ParentIndex<'tree>,
8441) -> CppFieldLinkage {
8442    let mut current = ancestry.parent(declaration);
8443    let mut enclosed_by_class = false;
8444    while let Some(node) = current {
8445        if node.kind() == "namespace_definition"
8446            && node
8447                .child_by_field_name("name")
8448                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8449        {
8450            return CppFieldLinkage::Internal;
8451        }
8452        if matches!(
8453            node.kind(),
8454            "class_specifier" | "struct_specifier" | "union_specifier"
8455        ) && node
8456            .child_by_field_name("name")
8457            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8458        {
8459            return CppFieldLinkage::Internal;
8460        }
8461        if matches!(
8462            node.kind(),
8463            "class_specifier" | "struct_specifier" | "union_specifier"
8464        ) {
8465            enclosed_by_class = true;
8466        }
8467        if matches!(node.kind(), "function_definition" | "lambda_expression") {
8468            return CppFieldLinkage::Internal;
8469        }
8470        current = ancestry.parent(node);
8471    }
8472    if enclosed_by_class {
8473        return CppFieldLinkage::External;
8474    }
8475    let mut cursor = declaration.walk();
8476    let mut has_static = false;
8477    let mut has_extern = false;
8478    let mut has_inline = false;
8479    let mut has_const = false;
8480    let mut has_constexpr = false;
8481    for child in declaration.named_children(&mut cursor) {
8482        let text = normalize_cpp_whitespace(node_text(child, source));
8483        match (child.kind(), text.as_str()) {
8484            ("storage_class_specifier", "static") => has_static = true,
8485            ("storage_class_specifier", "extern") => has_extern = true,
8486            ("storage_class_specifier", "inline") => has_inline = true,
8487            ("storage_class_specifier", "constexpr") => has_constexpr = true,
8488            ("type_qualifier", "const") => has_const = true,
8489            ("type_qualifier", "constexpr") => has_constexpr = true,
8490            _ => {}
8491        }
8492    }
8493    if has_static {
8494        CppFieldLinkage::Internal
8495    } else if has_extern || has_inline {
8496        CppFieldLinkage::External
8497    } else if has_const || has_constexpr {
8498        CppFieldLinkage::InternalUnlessExternalPeer
8499    } else {
8500        CppFieldLinkage::External
8501    }
8502}
8503
8504fn cpp_declaration_range(node: Node<'_>) -> Range {
8505    Range {
8506        start_byte: node.start_byte(),
8507        end_byte: node.end_byte(),
8508        start_line: node.start_position().row + 1,
8509        end_line: node.end_position().row + 1,
8510    }
8511}
8512
8513/// A recovery interval as a [`Range`], for materialization records whose
8514/// window is a byte region rather than one parser node (the sentinel-macro
8515/// region reparses, issue #941/#1657).
8516fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
8517    let line_at = |byte: usize| {
8518        source.as_bytes()[..byte]
8519            .iter()
8520            .filter(|&&b| b == b'\n')
8521            .count()
8522            + 1
8523    };
8524    Range {
8525        start_byte,
8526        end_byte,
8527        start_line: line_at(start_byte),
8528        end_line: line_at(end_byte),
8529    }
8530}
8531
8532/// Every `#include` directive the tree holds, in source order.
8533///
8534/// A preorder sweep rather than a step of the declaration walk: the container
8535/// walk descends only through declaration scopes, so an include written inside
8536/// a class body or a function body would otherwise never be seen, and an
8537/// include is an include wherever it is written.
8538pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
8539    walk_named_tree_preorder(root, true, |node| {
8540        if node.kind() == "preproc_include" {
8541            let raw = normalize_cpp_whitespace(node_text(node, source));
8542            if !raw.is_empty() {
8543                parsed.imports.push(ImportInfo {
8544                    raw_snippet: raw,
8545                    is_wildcard: false,
8546                    is_global: false,
8547                    identifier: None,
8548                    alias: None,
8549                    path: None,
8550                    binder_span: None,
8551                });
8552            }
8553            return WalkControl::SkipChildren;
8554        }
8555        WalkControl::Continue
8556    });
8557}
8558
8559pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
8560    let mut in_block_comment = false;
8561    for line in source.lines() {
8562        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
8563        let trimmed = stripped.trim();
8564        if !looks_like_quoted_include_line(trimmed) {
8565            continue;
8566        }
8567
8568        let raw = normalize_cpp_whitespace(trimmed);
8569        // The tree-sitter walk already recorded every `#include` it could see;
8570        // this line scan only recovers the ones a parse error hid, so skip a
8571        // snippet that is already an import binding.
8572        if parsed
8573            .imports
8574            .iter()
8575            .any(|import| import.raw_snippet == raw)
8576        {
8577            continue;
8578        }
8579
8580        parsed.imports.push(ImportInfo {
8581            raw_snippet: raw,
8582            is_wildcard: false,
8583            is_global: false,
8584            identifier: None,
8585            alias: None,
8586            path: None,
8587            binder_span: None,
8588        });
8589    }
8590}
8591
8592fn looks_like_quoted_include_line(line: &str) -> bool {
8593    let Some(rest) = line.trim_start().strip_prefix('#') else {
8594        return false;
8595    };
8596    let Some(rest) = rest.trim_start().strip_prefix("include") else {
8597        return false;
8598    };
8599    rest.trim_start().starts_with('"')
8600}
8601
8602fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
8603    let mut raw = Vec::new();
8604    let mut cursor = node.walk();
8605    for child in node.named_children(&mut cursor) {
8606        if child.kind() == "base_class_clause" {
8607            collect_cpp_base_nodes(child, source, &mut raw);
8608        }
8609    }
8610    raw
8611}
8612
8613fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
8614    walk_named_tree_preorder(node, false, |child| match child.kind() {
8615        "type_identifier" | "qualified_identifier" | "template_type" => {
8616            let text = normalize_cpp_whitespace(node_text(child, source));
8617            if !text.is_empty() {
8618                raw.push(text);
8619            }
8620            WalkControl::SkipChildren
8621        }
8622        _ => WalkControl::Continue,
8623    });
8624}
8625
8626fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
8627    let mut out = String::new();
8628    let chars: Vec<char> = line.chars().collect();
8629    let mut index = 0;
8630    let mut in_string = false;
8631    let mut in_char = false;
8632    let mut escape = false;
8633
8634    while index < chars.len() {
8635        let ch = chars[index];
8636        let next = chars.get(index + 1).copied();
8637
8638        if *in_block_comment {
8639            if ch == '*' && next == Some('/') {
8640                *in_block_comment = false;
8641                index += 2;
8642            } else {
8643                index += 1;
8644            }
8645            continue;
8646        }
8647
8648        if in_string {
8649            out.push(ch);
8650            if escape {
8651                escape = false;
8652            } else if ch == '\\' {
8653                escape = true;
8654            } else if ch == '"' {
8655                in_string = false;
8656            }
8657            index += 1;
8658            continue;
8659        }
8660
8661        if in_char {
8662            out.push(ch);
8663            if escape {
8664                escape = false;
8665            } else if ch == '\\' {
8666                escape = true;
8667            } else if ch == '\'' {
8668                in_char = false;
8669            }
8670            index += 1;
8671            continue;
8672        }
8673
8674        if ch == '/' && next == Some('/') {
8675            break;
8676        }
8677        if ch == '/' && next == Some('*') {
8678            *in_block_comment = true;
8679            index += 2;
8680            continue;
8681        }
8682        if ch == '"' {
8683            in_string = true;
8684            out.push(ch);
8685            index += 1;
8686            continue;
8687        }
8688        if ch == '\'' {
8689            in_char = true;
8690            out.push(ch);
8691            index += 1;
8692            continue;
8693        }
8694
8695        out.push(ch);
8696        index += 1;
8697    }
8698
8699    out
8700}
8701
8702#[derive(Clone)]
8703struct FunctionInfo {
8704    package_name: String,
8705    owner: Option<CppMemberOwner>,
8706    name: String,
8707    signature: String,
8708}
8709
8710/// Owner of a member function, kept structured so a literal `$` inside a
8711/// source-level class name never crosses a join/split boundary: the legacy
8712/// `$`-joined owner string was re-split at fq construction, dropping a leading
8713/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
8714/// and tripping the package/short boundary assert -- the #2140 corruption one
8715/// level up (#2362).
8716#[derive(Clone)]
8717enum CppMemberOwner {
8718    /// Source-level owner class chain from a qualified declarator-id, one
8719    /// class name per component (`Outer::Inner::method` -> `["Outer",
8720    /// "Inner"]`); each component may itself contain a literal `$`.
8721    Chain(Vec<String>),
8722    /// The lexically enclosing or recovered class unit; the member fq extends
8723    /// its fq directly instead of re-splitting its `$`-joined short chain.
8724    Unit(CodeUnit),
8725}
8726
8727impl CppMemberOwner {
8728    /// The legacy `$`-joined owner chain used in the member's short name.
8729    fn short_chain(&self) -> String {
8730        match self {
8731            Self::Chain(chain) => chain.join("$"),
8732            Self::Unit(parent) => parent.short_name().to_string(),
8733        }
8734    }
8735}
8736
8737enum DeclaratorKind<'a> {
8738    Function(Node<'a>),
8739    Variable(Node<'a>),
8740}
8741
8742impl FunctionInfo {
8743    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
8744        self.code_unit_with_synthetic(file, false)
8745    }
8746
8747    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
8748        let short_name = match &self.owner {
8749            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
8750            None => self.name.clone(),
8751        };
8752        let fq = match &self.owner {
8753            Some(CppMemberOwner::Chain(chain)) => {
8754                debug_assert!(
8755                    !chain.is_empty(),
8756                    "an empty owner chain is no owner; producers return None instead"
8757                );
8758                let mut fq = FqName::new();
8759                cpp_push_package(&mut fq, &self.package_name);
8760                let mut first = true;
8761                for component in chain {
8762                    let kind = if first {
8763                        SegmentKind::Type
8764                    } else {
8765                        SegmentKind::Nested
8766                    };
8767                    fq.push(cpp_segment(component, kind));
8768                    first = false;
8769                }
8770                fq.push(cpp_segment(&self.name, SegmentKind::Member));
8771                fq
8772            }
8773            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
8774                .fq()
8775                .clone()
8776                .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
8777            // An anonymous parent (empty short chain) contributes no owner
8778            // segment -- the same guard as cpp_join_member_short above.
8779            Some(CppMemberOwner::Unit(_)) | None => {
8780                let mut fq = FqName::new();
8781                cpp_push_package(&mut fq, &self.package_name);
8782                fq.push(cpp_segment(&self.name, SegmentKind::Member));
8783                fq
8784            }
8785        };
8786        CodeUnit::with_signature_and_fq(
8787            file,
8788            CodeUnitType::Function,
8789            self.package_name.clone(),
8790            short_name,
8791            Some(self.signature.clone()),
8792            synthetic,
8793            fq,
8794        )
8795    }
8796}
8797
8798fn extract_function_info(
8799    declarator: Node<'_>,
8800    source: &str,
8801    scope: &ScopeInfo,
8802) -> Option<FunctionInfo> {
8803    let parameters_node = declarator.child_by_field_name("parameters")?;
8804    let declarator_name_node = declarator
8805        .child_by_field_name("declarator")
8806        .or_else(|| parameters_node.prev_named_sibling())?;
8807    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
8808}
8809
8810fn extract_function_info_from_name(
8811    declarator: Node<'_>,
8812    declarator_name_node: Node<'_>,
8813    source: &str,
8814    scope: &ScopeInfo,
8815) -> Option<FunctionInfo> {
8816    let parameters_node = declarator.child_by_field_name("parameters")?;
8817    let parameters_text = cpp_parameter_signature(parameters_node, source);
8818    let recovered_specialization_member = scope
8819        .recovered_specialization_member_scope
8820        .then(|| {
8821            let terminal = declarator_name_node
8822                .child_by_field_name("name")
8823                .unwrap_or(declarator_name_node);
8824            let name = canonical_cpp_qualified_component(terminal, source)?.name;
8825            let owner = scope.class_unit.as_ref()?;
8826            Some((
8827                Some(CppMemberOwner::Unit(owner.clone())),
8828                name,
8829                scope.package_name.clone(),
8830            ))
8831        })
8832        .flatten();
8833    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
8834        parts
8835    } else if let Some(parts) =
8836        split_structured_templated_cpp_name(declarator_name_node, source, scope)
8837    {
8838        parts
8839    } else {
8840        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
8841            declarator_name_node,
8842            source,
8843        )?);
8844        if raw_name.is_empty() {
8845            return None;
8846        }
8847        split_cpp_name(&raw_name, scope)
8848    };
8849    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
8850    let mut signature = if suffix.is_empty() {
8851        parameters_text
8852    } else {
8853        format!("{parameters_text} {suffix}")
8854    };
8855    if let Some(template_signature) = &scope.template_signature {
8856        signature = format!("{template_signature}{signature}");
8857    }
8858
8859    Some(FunctionInfo {
8860        package_name,
8861        owner,
8862        name,
8863        signature,
8864    })
8865}
8866
8867/// Recover the semantic return type and callable name when a declaration macro
8868/// occupies a function definition's `type` field. Tree-sitter either exposes a
8869/// scalar return as the declarator's apparent name and the callable as the sole
8870/// identifier in an `ERROR`, or joins a template return and callable into a
8871/// qualified identifier with a missing `::`. Both shapes retain the complete
8872/// parameter list and body; a concrete separator remains an out-of-line member.
8873fn cpp_macro_displaced_callable_parts<'tree>(
8874    function_declarator: Node<'tree>,
8875    source: &str,
8876    ancestry: &ParentIndex<'tree>,
8877) -> Option<(Node<'tree>, Node<'tree>)> {
8878    let definition = ancestry.parent(function_declarator)?;
8879    if definition.kind() != "function_definition"
8880        || definition.child_by_field_name("declarator") != Some(function_declarator)
8881        || definition
8882            .child_by_field_name("body")
8883            .is_none_or(|body| body.kind() != "compound_statement")
8884    {
8885        return None;
8886    }
8887    let macro_type = definition.child_by_field_name("type")?;
8888    if macro_type.kind() != "type_identifier"
8889        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8890    {
8891        return None;
8892    }
8893
8894    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
8895    if apparent_return_type.kind() == "qualified_identifier"
8896        && let (Some(return_type), Some(callable_name)) = (
8897            apparent_return_type.child_by_field_name("scope"),
8898            apparent_return_type.child_by_field_name("name"),
8899        )
8900        && return_type.kind() == "template_type"
8901        && matches!(callable_name.kind(), "identifier" | "field_identifier")
8902        && (0..apparent_return_type.child_count())
8903            .filter_map(|index| apparent_return_type.child(index))
8904            .any(|child| child.kind() == "::" && child.is_missing())
8905        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
8906        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8907    {
8908        return Some((return_type, callable_name));
8909    }
8910    if !matches!(
8911        apparent_return_type.kind(),
8912        "identifier" | "field_identifier" | "type_identifier"
8913    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
8914    {
8915        return None;
8916    }
8917    let parameters = function_declarator.child_by_field_name("parameters")?;
8918    let mut cursor = function_declarator.walk();
8919    let between = function_declarator
8920        .named_children(&mut cursor)
8921        .filter(|child| child.kind() != "comment")
8922        .filter(|child| {
8923            child.start_byte() >= apparent_return_type.end_byte()
8924                && child.end_byte() <= parameters.start_byte()
8925                && !same_node(*child, apparent_return_type)
8926                && !same_node(*child, parameters)
8927        })
8928        .collect::<Vec<_>>();
8929    let [name_error] = between.as_slice() else {
8930        return None;
8931    };
8932    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
8933        return None;
8934    }
8935    let callable_name = name_error.named_child(0)?;
8936    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
8937        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8938    {
8939        return None;
8940    }
8941    Some((apparent_return_type, callable_name))
8942}
8943
8944/// The part of a `function_declarator` after its parameter list that belongs to
8945/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
8946/// specification, a trailing return type and a trailing requires-clause.
8947///
8948/// The grammar makes each of these a distinct sibling of the `parameters`
8949/// field, so they are read from the tree. Splitting the declarator's text on
8950/// the parameter list instead silently dropped every qualifier whenever the
8951/// parameter list was spelled with whitespace that normalization rewrote - a
8952/// line break or a double space was enough to make a `const` member definition
8953/// a different logical symbol from its declaration (#1827).
8954///
8955/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
8956/// are deliberately excluded. C++ does not make them part of the signature and
8957/// an out-of-line definition never repeats them, so including them would split
8958/// a declaration from its own definition.
8959fn cpp_declarator_identity_suffix(
8960    declarator: Node<'_>,
8961    parameters_node: Node<'_>,
8962    source: &str,
8963) -> String {
8964    let mut cursor = declarator.walk();
8965    let parts = declarator
8966        .named_children(&mut cursor)
8967        .filter(|child| child.start_byte() >= parameters_node.end_byte())
8968        .filter(|child| {
8969            matches!(
8970                child.kind(),
8971                "type_qualifier"
8972                    | "ref_qualifier"
8973                    | "noexcept"
8974                    | "throw_specifier"
8975                    | "trailing_return_type"
8976                    | "requires_clause"
8977            )
8978        })
8979        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8980        .filter(|text| !text.is_empty())
8981        .collect::<Vec<_>>();
8982    normalize_cpp_qualifier_suffix(&parts.join(" "))
8983}
8984
8985/// The identity suffix of one callable declarator, for a consumer that holds
8986/// the declarator rather than the declaration walk's parts.
8987///
8988/// The persisted signature concatenates the parameter spelling and this suffix,
8989/// so a comparison that must agree on the suffix alone recomputes it here
8990/// instead of splitting the stored string.
8991pub(crate) fn cpp_callable_identity_suffix(
8992    function_declarator: Node<'_>,
8993    source: &str,
8994) -> Option<String> {
8995    let parameters_node = function_declarator.child_by_field_name("parameters")?;
8996    Some(cpp_declarator_identity_suffix(
8997        function_declarator,
8998        parameters_node,
8999        source,
9000    ))
9001}
9002
9003pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
9004    match classify_declarator(node)? {
9005        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
9006        DeclaratorKind::Variable(_) => None,
9007    }
9008}
9009
9010fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
9011    match node.kind() {
9012        "function_declarator" => {
9013            let inner = node
9014                .child_by_field_name("declarator")
9015                .or_else(|| node.child_by_field_name("name"))
9016                .or_else(|| last_named_child(node));
9017            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
9018                Some(DeclaratorKind::Variable(node))
9019            } else {
9020                Some(DeclaratorKind::Function(node))
9021            }
9022        }
9023        "init_declarator"
9024        | "pointer_declarator"
9025        | "reference_declarator"
9026        | "parenthesized_declarator"
9027        | "array_declarator"
9028        | "attributed_declarator"
9029        | "template_function" => node
9030            .child_by_field_name("declarator")
9031            .or_else(|| node.child_by_field_name("name"))
9032            .or_else(|| last_named_child(node))
9033            .and_then(classify_declarator),
9034        "identifier" | "field_identifier" | "qualified_identifier" => {
9035            Some(DeclaratorKind::Variable(node))
9036        }
9037        _ => node
9038            .child_by_field_name("declarator")
9039            .or_else(|| node.child_by_field_name("name"))
9040            .or_else(|| last_named_child(node))
9041            .and_then(classify_declarator),
9042    }
9043}
9044
9045fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
9046    matches!(
9047        node.kind(),
9048        "function_declarator"
9049            | "init_declarator"
9050            | "pointer_declarator"
9051            | "reference_declarator"
9052            | "parenthesized_declarator"
9053            | "array_declarator"
9054            | "attributed_declarator"
9055            | "template_function"
9056            | "identifier"
9057            | "field_identifier"
9058            | "qualified_identifier"
9059    )
9060}
9061
9062fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
9063    let class_like = first_class_like_child(node);
9064    let mut cursor = node.walk();
9065    node.named_children(&mut cursor).any(|child| {
9066        matches!(
9067            child.kind(),
9068            "init_declarator"
9069                | "pointer_declarator"
9070                | "reference_declarator"
9071                | "array_declarator"
9072                | "function_declarator"
9073                | "parenthesized_declarator"
9074                | "attributed_declarator"
9075        ) || matches!(
9076            child.kind(),
9077            "identifier" | "field_identifier" | "qualified_identifier"
9078        ) && class_like.is_none_or(|class_node| {
9079            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
9080        })
9081    })
9082}
9083
9084/// One namespace-scope forward class declaration that a recovered export-macro
9085/// class definition may borrow its identity from.  Tree-sitter can close a
9086/// malformed class at the enclosing namespace's closing brace, leaving the later
9087/// class definitions as root-level recovered `function_definition` nodes.  A
9088/// preceding `class Name;` in the same namespace is the only structured identity
9089/// signal available in that shape.
9090///
9091/// Everything recorded here is a property of the forward declaration alone.
9092/// What depends on the node doing the asking -- that the forward and its
9093/// namespace both close before it, with nothing but recovery trivia between --
9094/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
9095/// tree answers every later question about it.
9096struct CppNamespaceForward {
9097    name: String,
9098    start_byte: usize,
9099    /// Where the malformed namespace that held the forward ended.  No query
9100    /// asks anything else about that node.
9101    namespace_end_byte: usize,
9102    package_name: String,
9103}
9104
9105/// Read `node` as a borrowable namespace forward declaration.
9106///
9107/// The admission is deliberately conservative: only a body-less class specifier
9108/// whose declaration has no declarator, at namespace scope rather than inside a
9109/// function or class body, in a namespace that itself failed to parse.
9110fn cpp_namespace_forward_entry<'tree>(
9111    node: Node<'tree>,
9112    source: &str,
9113    ancestry: &ParentIndex<'tree>,
9114) -> Option<CppNamespaceForward> {
9115    if !matches!(
9116        node.kind(),
9117        "class_specifier" | "struct_specifier" | "union_specifier"
9118    ) || cpp_body_node(node).is_some()
9119    {
9120        return None;
9121    }
9122    let parent = node.parent()?;
9123    if !(parent.kind() == "declaration_list"
9124        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
9125    {
9126        return None;
9127    }
9128    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
9129    // Borrowing is only justified by the parser-recovery shape we are
9130    // repairing: the namespace that held the forward must itself contain a
9131    // syntax error. A clean, unrelated namespace forward is not an identity
9132    // proof.
9133    if !namespace.has_error() {
9134        return None;
9135    }
9136    Some(CppNamespaceForward {
9137        name: class_like_name(node, source, ancestry)?,
9138        start_byte: node.start_byte(),
9139        namespace_end_byte: namespace.end_byte(),
9140        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
9141    })
9142}
9143
9144/// Whether `forward` stands in the recovery relation to the node asking about
9145/// it: it and its malformed namespace both closed before the recovered class,
9146/// and nothing but recovery trivia separates the two.
9147fn cpp_namespace_forward_matches_recovery(
9148    forward: &CppNamespaceForward,
9149    recovered_node: Node<'_>,
9150) -> bool {
9151    forward.start_byte < recovered_node.start_byte()
9152        && forward.namespace_end_byte < recovered_node.start_byte()
9153        && malformed_namespace_is_nearest_recovery_region(
9154            forward.namespace_end_byte,
9155            recovered_node,
9156        )
9157}
9158
9159/// What one open [`CppVisitor::record_recovered_declarations`] has watched
9160/// happen to the declaration set.
9161///
9162/// The recovered set used to be a difference against a clone of the whole
9163/// declaration set, taken once per recovery: O(recoveries x declarations) on
9164/// exactly the error-recovered files that already walk slowest (#2787). The
9165/// walk knows which declarations it creates, so the capture collects them as
9166/// they are made and the difference is never needed.
9167///
9168/// `removed_pre_existing` is what makes that equal to the difference. A
9169/// deferred replacement removes the replaced declaration's children
9170/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
9171/// re-creates them. Creation alone cannot tell that apart from a first mint, so
9172/// a removal of a declaration this capture did not create records that it was
9173/// already there when the capture opened.
9174#[derive(Debug, Default)]
9175pub struct CppRecoveryCapture {
9176    /// Declarations created while this capture was open, in creation order.
9177    created: Vec<CodeUnit>,
9178    /// Membership for `created`.
9179    created_units: HashSet<CodeUnit>,
9180    /// Declarations that predate this capture and have been removed during it.
9181    removed_pre_existing: HashSet<CodeUnit>,
9182}
9183
9184/// Which owners the parse product already holds field declarations for, folded
9185/// in as the walk records them.
9186///
9187/// [`CppVisitor::has_enum_enumerator_units`] asks that question once per enum
9188/// and used to answer it by scanning every declaration accumulated so far:
9189/// O(enums x declarations) on exactly the generated headers that declare many
9190/// of both (#2786). The answer only grows by declaration, so the walk carries
9191/// it. A field's short name names its owner chain, `Owner.member`, so one field
9192/// answers for every dotted prefix of its own short name; an anonymous enum's
9193/// or union's enumerators carry bare short names instead (#2140), which is what
9194/// an empty owner short name asks about.
9195#[derive(Debug, Default)]
9196pub struct CppFieldOwnerIndex {
9197    /// Package name -> the owner short names its fields name.
9198    owners: HashMap<String, HashSet<String>>,
9199    /// Packages holding at least one field that names no owner.
9200    ownerless_packages: HashSet<String>,
9201}
9202
9203impl CppFieldOwnerIndex {
9204    /// The index of the declarations recorded so far, built when the first
9205    /// question arrives.
9206    fn of<'unit>(
9207        declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9208        file: &ProjectFile,
9209    ) -> Self {
9210        let mut index = Self::default();
9211        for declaration in declarations {
9212            index.record(declaration, file);
9213        }
9214        index
9215    }
9216
9217    fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
9218        if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
9219            return;
9220        }
9221        let short_name = code_unit.short_name();
9222        let package_name = code_unit.package_name();
9223        if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
9224            self.ownerless_packages.insert(package_name.to_string());
9225        }
9226        if !short_name.contains('.') {
9227            return;
9228        }
9229        if !self.owners.contains_key(package_name) {
9230            self.owners
9231                .insert(package_name.to_string(), HashSet::default());
9232        }
9233        let owners = self
9234            .owners
9235            .get_mut(package_name)
9236            .expect("the package entry was just ensured");
9237        for (offset, _) in short_name.match_indices('.') {
9238            let owner = &short_name[..offset];
9239            if !owners.contains(owner) {
9240                owners.insert(owner.to_string());
9241            }
9242        }
9243    }
9244
9245    /// Whether a field declaration in `package_name` names `owner_short_name`
9246    /// as its owner. An empty owner asks about ownerless fields instead.
9247    fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
9248        if owner_short_name.is_empty() {
9249            self.ownerless_packages.contains(package_name)
9250        } else {
9251            self.owners
9252                .get(package_name)
9253                .is_some_and(|owners| owners.contains(owner_short_name))
9254        }
9255    }
9256}
9257
9258/// The declaration scan [`CppFieldOwnerIndex`] replaces, kept as the oracle a
9259/// debug build asserts every carried answer against and as the release-mode
9260/// parity tests' reference (#2786).
9261#[cfg(any(debug_assertions, test))]
9262fn cpp_declarations_hold_owned_fields<'unit>(
9263    declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9264    file: &ProjectFile,
9265    package_name: &str,
9266    owner_short_name: &str,
9267) -> bool {
9268    let prefix = format!("{owner_short_name}.");
9269    declarations.into_iter().any(|unit| {
9270        unit.kind() == CodeUnitType::Field
9271            && unit.source() == file
9272            && unit.package_name() == package_name
9273            && if owner_short_name.is_empty() {
9274                // Anonymous enum/union parent: its enumerators carry bare
9275                // short names (#2140), so presence means any ownerless
9276                // field in this file.
9277                !unit.short_name().contains(['.', '$'])
9278            } else {
9279                unit.short_name().starts_with(&prefix)
9280            }
9281    })
9282}
9283
9284/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
9285///
9286/// A region reparse is its own tree and is dropped while the walk that made it
9287/// continues, so a later parse can be allocated at the same address and hand out
9288/// the same node ids.  The root's span and shape pin the identity its address
9289/// alone does not: two roots agreeing on all of this are the same parse of the
9290/// same bytes, and a scan of one is a scan of the other.
9291#[derive(PartialEq, Eq, Hash)]
9292pub struct CppTreeIdentity {
9293    root_id: usize,
9294    start_byte: usize,
9295    end_byte: usize,
9296    kind_id: u16,
9297    child_count: usize,
9298}
9299
9300impl CppTreeIdentity {
9301    fn of(root: Node<'_>) -> Self {
9302        Self {
9303            root_id: root.id(),
9304            start_byte: root.start_byte(),
9305            end_byte: root.end_byte(),
9306            kind_id: root.kind_id(),
9307            child_count: root.child_count(),
9308        }
9309    }
9310}
9311
9312/// The namespace forward declarations one tree's prefix holds, folded in as the
9313/// walk asks about them.
9314///
9315/// `scope_for_recovered_exported_class` asks the same question once per
9316/// recovered class, and the answer depends only on the part of the tree that
9317/// starts before the asking node.  Rescanning that prefix per question is
9318/// quadratic in the file, and a generated header whose parse recovery leaves
9319/// thousands of class-like declarations at file scope pays all of it: 1,904
9320/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
9321/// (#2754).  This carries the scan forward instead.  Each question advances the
9322/// traversal from wherever the last one stopped to the asking node's start byte,
9323/// so a whole walk pays at most one pass over the prefix its furthest question
9324/// reaches, and each question then costs a name lookup.
9325#[derive(Default)]
9326pub struct CppNamespaceForwardScan {
9327    /// Every named node starting before this byte has been folded in.
9328    scanned_through: usize,
9329    forwards: HashMap<String, Vec<CppNamespaceForward>>,
9330}
9331
9332impl CppNamespaceForwardScan {
9333    /// Fold in every named node of `root` that starts at or after the fold
9334    /// watermark and before `cutoff`.
9335    ///
9336    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
9337    /// pass owes are exactly the ones no earlier pass reached, and a question
9338    /// about an earlier byte than one already answered costs nothing.
9339    fn advance_to<'tree>(
9340        &mut self,
9341        root: Node<'tree>,
9342        cutoff: usize,
9343        source: &str,
9344        ancestry: &ParentIndex<'tree>,
9345    ) {
9346        if cutoff <= self.scanned_through {
9347            return;
9348        }
9349        let folded_through = self.scanned_through;
9350        let mut cursor = root.walk();
9351        let mut stack = vec![root];
9352        while let Some(current) = stack.pop() {
9353            if (folded_through..cutoff).contains(&current.start_byte())
9354                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9355            {
9356                self.forwards
9357                    .entry(forward.name.clone())
9358                    .or_default()
9359                    .push(forward);
9360            }
9361            // A subtree ending before the watermark holds only nodes an earlier
9362            // pass already folded, and one starting at or after the cutoff is
9363            // outside the prefix being asked about. Skipping both is what keeps
9364            // the total traversal to one pass.
9365            for child in current.named_children(&mut cursor) {
9366                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
9367                    stack.push(child);
9368                }
9369            }
9370        }
9371        self.scanned_through = cutoff;
9372    }
9373
9374    /// The one namespace `name` was forward declared in before `recovered_node`.
9375    /// More than one matching forward declaration is ambiguous and answers
9376    /// nothing rather than guessing.
9377    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
9378        let mut matching = self
9379            .forwards
9380            .get(name)
9381            .into_iter()
9382            .flatten()
9383            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
9384        let first = matching.next()?;
9385        matching
9386            .next()
9387            .is_none()
9388            .then(|| first.package_name.clone())
9389    }
9390}
9391
9392/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
9393/// debug build checks every answer against (and the one the parity tests drive
9394/// directly).  It walks the whole prefix per question, which is exactly the cost
9395/// #2754 removed from the release path.
9396#[cfg(any(debug_assertions, test))]
9397fn unique_earlier_cpp_namespace_forward<'tree>(
9398    recovered_node: Node<'tree>,
9399    name: &str,
9400    source: &str,
9401    ancestry: &ParentIndex<'tree>,
9402) -> Option<String> {
9403    let mut root = recovered_node;
9404    while let Some(parent) = ancestry.parent(root) {
9405        root = parent;
9406    }
9407
9408    let mut candidates = Vec::new();
9409    let mut stack = vec![root];
9410    while let Some(current) = stack.pop() {
9411        if current.start_byte() < recovered_node.start_byte()
9412            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9413            && forward.name == name
9414            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
9415        {
9416            candidates.push(forward.package_name);
9417        }
9418
9419        let mut cursor = current.walk();
9420        for child in current.named_children(&mut cursor) {
9421            if child.start_byte() < recovered_node.start_byte() {
9422                stack.push(child);
9423            }
9424        }
9425    }
9426
9427    if candidates.len() == 1 {
9428        candidates.pop()
9429    } else {
9430        None
9431    }
9432}
9433
9434fn malformed_namespace_is_nearest_recovery_region(
9435    namespace_end_byte: usize,
9436    recovered_node: Node<'_>,
9437) -> bool {
9438    let mut root = recovered_node;
9439    while let Some(parent) = root.parent() {
9440        root = parent;
9441    }
9442    let mut cursor = root.walk();
9443    root.named_children(&mut cursor)
9444        .filter(|sibling| {
9445            namespace_end_byte <= sibling.start_byte()
9446                && sibling.end_byte() <= recovered_node.start_byte()
9447        })
9448        .all(is_malformed_namespace_recovery_trivia)
9449}
9450
9451fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
9452    matches!(node.kind(), "ERROR" | "comment")
9453        || node.kind().starts_with("preproc_")
9454        || node.kind() == "expression_statement" && node.named_child_count() == 0
9455}
9456
9457/// Return the namespace path for a forward class only when the declaration is
9458/// at namespace scope.  A declaration nested in a function/class body may share
9459/// the same namespace ancestor but cannot identify a top-level class definition.
9460fn cpp_namespace_name_for_forward<'tree>(
9461    node: Node<'tree>,
9462    source: &str,
9463    ancestry: &ParentIndex<'tree>,
9464) -> Option<String> {
9465    cpp_namespace_definition_for_forward(node, ancestry)?;
9466    cpp_lexical_namespace_name(node, source, ancestry)
9467}
9468
9469fn cpp_namespace_definition_for_forward<'tree>(
9470    node: Node<'tree>,
9471    ancestry: &ParentIndex<'tree>,
9472) -> Option<Node<'tree>> {
9473    let declaration = ancestry.parent(node)?;
9474    let mut ancestor = ancestry.parent(declaration);
9475    while let Some(current) = ancestor {
9476        if matches!(
9477            current.kind(),
9478            "compound_statement"
9479                | "field_declaration_list"
9480                | "class_specifier"
9481                | "struct_specifier"
9482                | "union_specifier"
9483                | "function_definition"
9484                | "lambda_expression"
9485        ) {
9486            return None;
9487        }
9488        if current.kind() == "namespace_definition" {
9489            return Some(current);
9490        }
9491        ancestor = ancestry.parent(current);
9492    }
9493    None
9494}
9495
9496fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
9497    match node.kind() {
9498        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9499        "parenthesized_declarator" => node
9500            .child_by_field_name("declarator")
9501            .or_else(|| last_named_child(node))
9502            .is_some_and(is_pointer_wrapper_declarator),
9503        "template_function" => node
9504            .child_by_field_name("name")
9505            .is_some_and(is_function_pointer_like_inner_declarator),
9506        _ => false,
9507    }
9508}
9509
9510fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
9511    match node.kind() {
9512        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9513        "parenthesized_declarator" => node
9514            .child_by_field_name("declarator")
9515            .or_else(|| last_named_child(node))
9516            .is_some_and(is_pointer_wrapper_declarator),
9517        _ => false,
9518    }
9519}
9520
9521fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
9522    let cleaned = raw_name.trim_start_matches("template ").trim();
9523    // A leading `::` is the explicit-global marker, not an empty owner segment.
9524    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
9525    // erroneous macro envelope swallowing the first identifier of an
9526    // out-of-line `X::X` constructor, chromium #1573); without this strip the
9527    // split below yields owner_parts `[""]`, constructing a unit with an empty
9528    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
9529    let cleaned = cleaned.trim_start_matches("::");
9530    // Parser recovery can preserve two adjacent scope operators around a
9531    // missing component (for example `X::/**/::method` in compiler diagnostic
9532    // fixtures). Empty components are syntax-recovery artifacts, never C++
9533    // owners. Keeping one as the final owner constructed `short_name=".method"`
9534    // and violated the structured package/short boundary during a large LLVM
9535    // workspace build. This is the same legacy-string-to-FqName bridge as the
9536    // ordinary split above; discard only components that the delimiter itself
9537    // proves empty.
9538    let parts: Vec<_> = cleaned
9539        .split("::")
9540        .filter(|component| !component.is_empty())
9541        .collect();
9542    if parts.is_empty() {
9543        return (None, cleaned.to_string(), scope.package_name.clone());
9544    }
9545    if parts.len() > 1 {
9546        let name = parts.last().unwrap_or(&cleaned).to_string();
9547        let owner_parts = &parts[..parts.len() - 1];
9548        if let Some(class_unit) = &scope.class_unit {
9549            // Lexically inside a class body: the owner is that class, whatever
9550            // the declarator re-qualifies it as.
9551            return (
9552                Some(CppMemberOwner::Unit(class_unit.clone())),
9553                name,
9554                scope.package_name.clone(),
9555            );
9556        }
9557        if !scope.package_name.is_empty() {
9558            // Out-of-line member definition written *inside* an enclosing
9559            // `namespace {}` block (scope package is that namespace). Every
9560            // owner segment before the terminal member is a class-nesting step
9561            // -- an out-of-line nested-class member `Outer::Inner::method` in
9562            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
9563            // namespace path: `using namespace` never brings nested-class
9564            // access into unqualified scope, so C++ always writes the full
9565            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
9566            // that redundantly re-states the enclosing namespace it already
9567            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
9568            // strip that re-qualifying prefix (which duplicates a suffix of the
9569            // enclosing package path) before treating what remains as the
9570            // nested-class chain, so the redundant spelling still lands on the
9571            // same `log4cxx.Foo.method` identity as its header declaration.
9572            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
9573            let owner = (!nested.is_empty()).then(|| {
9574                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
9575            });
9576            return (owner, name, scope.package_name.clone());
9577        }
9578        // File scope (no enclosing `namespace {}` block, scope package empty).
9579        let (owner, package_name) = if owner_parts.len() > 1 {
9580            // A multi-segment qualifier at file scope with no enclosing
9581            // namespace: treat all but the last owner segment as the namespace
9582            // path and the last as the owning class (`ns1::ns2::Class::method`
9583            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
9584            // is really a namespace or an outer class cannot be told from the
9585            // declarator text alone here, and no enclosing namespace or
9586            // in-index owner is available at per-file extraction to confirm the
9587            // class reading, so the far-more-common namespace interpretation is
9588            // kept rather than guessed away (the nested-class-at-file-scope and
9589            // using-directive-qualified nested-class shapes remain on this
9590            // behavior; see #1121).
9591            (
9592                Some(CppMemberOwner::Chain(vec![
9593                    owner_parts.last().unwrap_or(&"").to_string(),
9594                ])),
9595                owner_parts[..owner_parts.len() - 1].join("::"),
9596            )
9597        } else {
9598            // A bare `Class::member` qualifier at file scope carries no
9599            // namespace segment of its own. The declarator alone cannot say
9600            // which namespace owns `Class` -- but a `using namespace X;`
9601            // directive already in effect at this point in the file (#1093,
9602            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
9603            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
9604            // is the remaining structural signal for it, so fall back to it
9605            // rather than leaving the definition's package empty while its
9606            // header declaration (parsed inside the `namespace {}` block) keeps
9607            // the real one -- an identity split that made the same member
9608            // unresolvable under its own displayed spelling.
9609            (
9610                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
9611                cpp_using_directive_namespace_for_bare_owner(scope),
9612            )
9613        };
9614        return (owner, name, package_name);
9615    }
9616
9617    let package_name = scope.package_name.clone();
9618    let owner = scope
9619        .class_unit
9620        .as_ref()
9621        .map(|parent| CppMemberOwner::Unit(parent.clone()));
9622    (owner, cleaned.to_string(), package_name)
9623}
9624
9625/// Drop the leading owner segments of an out-of-line member qualifier that
9626/// merely re-state the enclosing namespace the definition already sits in, so
9627/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
9628/// definition may redundantly write `a::b::Outer::Inner::method` (or the
9629/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
9630/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
9631/// noise, not class-nesting steps. Returns the owner segments with the longest
9632/// such re-qualifying prefix removed (possibly all of them, when the qualifier
9633/// names only the enclosing namespace before the terminal member -- a
9634/// re-qualified free function). `package_name` is the enclosing namespace path
9635/// in its stored `::`-joined form; both sides are split on the same delimiter
9636/// the namespace walker joined them with, so this compares namespace *segments*
9637/// rather than scanning text.
9638fn strip_redundant_namespace_prefix<'a>(
9639    owner_parts: &'a [&'a str],
9640    package_name: &str,
9641) -> &'a [&'a str] {
9642    if package_name.is_empty() {
9643        return owner_parts;
9644    }
9645    let package_segments: Vec<&str> = package_name.split("::").collect();
9646    let max_prefix = owner_parts.len().min(package_segments.len());
9647    for prefix_len in (1..=max_prefix).rev() {
9648        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
9649        if &owner_parts[..prefix_len] == package_suffix {
9650            return &owner_parts[prefix_len..];
9651        }
9652    }
9653    owner_parts
9654}
9655
9656/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
9657/// class name at file/namespace scope, from the `using namespace` directives
9658/// visible at this point in the file. Several may be in scope at once (a
9659/// primary `using namespace NS;` alongside deeper conveniences like `using
9660/// namespace NS::helpers;`); since the declarator gives no way to tell which
9661/// one actually declares the owner class, prefer the shallowest (fewest
9662/// `::`-separated segments) as the file's most likely "home" namespace,
9663/// breaking ties by declaration order. Returns an empty string (leaving the
9664/// caller's package unqualified, as before) when no using-namespace directive
9665/// is in scope.
9666fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
9667    scope
9668        .visible_using_namespaces
9669        .iter()
9670        .min_by_key(|namespace| namespace.split("::").count())
9671        .cloned()
9672        .unwrap_or_default()
9673}
9674
9675struct CppQualifiedNameComponent {
9676    name: String,
9677    is_template_id: bool,
9678}
9679
9680/// Canonical nested-class chain for an out-of-line class definition written
9681/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
9682/// component per class (`["Outer", "Inner"]`).
9683///
9684/// The enclosing namespace fixes the namespace/class boundary: after an
9685/// optional redundant spelling of that namespace, every component belongs to
9686/// the class chain. File-scope qualified class names remain untouched because
9687/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
9688///
9689/// The components stay structured (rather than being `$`-joined here) so the
9690/// fq construction can push one Type/Nested segment per class; the `$`-joined
9691/// short-name display form is derived at the call sites that need it.
9692fn qualified_class_name_chain(
9693    class_node: Node<'_>,
9694    source: &str,
9695    scope: &ScopeInfo,
9696) -> Option<Vec<String>> {
9697    if scope.package_name.is_empty() || scope.class_unit.is_some() {
9698        return None;
9699    }
9700    let name = class_node.child_by_field_name("name")?;
9701    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
9702    if explicitly_global
9703        || components.len() < 2
9704        || components.iter().any(|component| component.is_template_id)
9705    {
9706        return None;
9707    }
9708    let names = components
9709        .iter()
9710        .map(|component| component.name.as_str())
9711        .collect::<Vec<_>>();
9712    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
9713    if class_chain.is_empty() {
9714        return None;
9715    }
9716    Some(class_chain.iter().map(|name| name.to_string()).collect())
9717}
9718
9719fn structured_cpp_qualified_components(
9720    qualified_name: Node<'_>,
9721    source: &str,
9722) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
9723    if qualified_name.kind() != "qualified_identifier" {
9724        return None;
9725    }
9726
9727    let mut components = Vec::new();
9728    let mut current = qualified_name;
9729    let mut explicitly_global = false;
9730    loop {
9731        if current.kind() == "qualified_identifier" {
9732            if let Some(component) = current.child_by_field_name("scope") {
9733                components.push(canonical_cpp_qualified_component(component, source)?);
9734            } else if components.is_empty() {
9735                explicitly_global = true;
9736            } else {
9737                return None;
9738            }
9739            current = current.child_by_field_name("name")?;
9740        } else {
9741            components.push(canonical_cpp_qualified_component(current, source)?);
9742            break;
9743        }
9744    }
9745    Some((components, explicitly_global))
9746}
9747
9748fn split_structured_templated_cpp_name(
9749    declarator_name: Node<'_>,
9750    source: &str,
9751    scope: &ScopeInfo,
9752) -> Option<(Option<CppMemberOwner>, String, String)> {
9753    let (mut components, explicitly_global) =
9754        structured_cpp_qualified_components(declarator_name, source)?;
9755
9756    let terminal = components.pop()?;
9757    let owner_start = components
9758        .iter()
9759        .position(|component| component.is_template_id)?;
9760    let explicit_package = components[..owner_start]
9761        .iter()
9762        .map(|component| component.name.as_str())
9763        .collect::<Vec<_>>()
9764        .join("::");
9765    let explicit_package_is_empty = explicit_package.is_empty();
9766    let package_name = match (
9767        explicitly_global,
9768        scope.package_name.is_empty(),
9769        explicit_package_is_empty,
9770    ) {
9771        (true, _, _) => explicit_package,
9772        (false, _, true) => scope.package_name.clone(),
9773        (false, true, false) => explicit_package,
9774        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
9775    };
9776    // Same identity-split fallback as `split_cpp_name` (#1093): a template
9777    // specialization's owner class named with no namespace segment of its own
9778    // (`explicit_package` empty) at file scope (`explicitly_global` false)
9779    // with nothing enclosing (`package_name` still empty) has no structural
9780    // signal for its namespace besides an in-scope `using namespace X;`.
9781    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
9782    {
9783        cpp_using_directive_namespace_for_bare_owner(scope)
9784    } else {
9785        package_name
9786    };
9787    let owner_chain = components[owner_start..]
9788        .iter()
9789        .map(|component| component.name.clone())
9790        .collect::<Vec<_>>();
9791    if owner_chain.is_empty() || terminal.name.is_empty() {
9792        return None;
9793    }
9794
9795    Some((
9796        Some(CppMemberOwner::Chain(owner_chain)),
9797        terminal.name,
9798        package_name,
9799    ))
9800}
9801
9802fn canonical_cpp_qualified_component(
9803    mut component: Node<'_>,
9804    source: &str,
9805) -> Option<CppQualifiedNameComponent> {
9806    let mut is_template_id = false;
9807    loop {
9808        match component.kind() {
9809            "template_type" => {
9810                is_template_id = true;
9811                component = component.child_by_field_name("name")?;
9812            }
9813            "dependent_name" => component = component.named_child(0)?,
9814            "identifier"
9815            | "field_identifier"
9816            | "namespace_identifier"
9817            | "type_identifier"
9818            | "operator_name"
9819            | "destructor_name" => {
9820                let name = normalize_cpp_whitespace(node_text(component, source));
9821                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
9822                    name,
9823                    is_template_id,
9824                });
9825            }
9826            _ => component = component.child_by_field_name("name")?,
9827        }
9828    }
9829}
9830
9831fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
9832    if let Some(name) = macro_decorated_unqualified_name(node) {
9833        return extract_declarator_name(name, source);
9834    }
9835    match node.kind() {
9836        "identifier"
9837        | "field_identifier"
9838        | "type_identifier"
9839        | "operator_name"
9840        | "destructor_name"
9841        | "qualified_identifier" => node_text(node, source).to_string(),
9842        "function_declarator"
9843        | "pointer_declarator"
9844        | "reference_declarator"
9845        | "parenthesized_declarator"
9846        | "array_declarator"
9847        | "template_function" => node
9848            .child_by_field_name("declarator")
9849            .or_else(|| node.child_by_field_name("name"))
9850            .or_else(|| last_named_child(node))
9851            .map(|child| extract_declarator_name(child, source))
9852            .unwrap_or_else(|| node_text(node, source).to_string()),
9853        _ => node
9854            .child_by_field_name("name")
9855            .map(|child| extract_declarator_name(child, source))
9856            .unwrap_or_else(|| node_text(node, source).to_string()),
9857    }
9858}
9859
9860/// Extract a callable identity only through declaration-shaped AST nodes.
9861/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
9862/// expose the call's parameter list as a false function declarator; accepting
9863/// arbitrary node text there emitted bogus names such as `.*f`.
9864fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
9865    if let Some(name) = macro_decorated_unqualified_name(node) {
9866        return extract_callable_declarator_name(name, source);
9867    }
9868    match node.kind() {
9869        "identifier"
9870        | "field_identifier"
9871        | "type_identifier"
9872        | "operator_name"
9873        | "destructor_name"
9874        | "qualified_identifier" => Some(node_text(node, source).to_string()),
9875        "function_declarator"
9876        | "pointer_declarator"
9877        | "reference_declarator"
9878        | "parenthesized_declarator"
9879        | "array_declarator"
9880        | "template_function" => node
9881            .child_by_field_name("declarator")
9882            .or_else(|| node.child_by_field_name("name"))
9883            .and_then(|child| extract_callable_declarator_name(child, source)),
9884        _ => None,
9885    }
9886}
9887
9888fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
9889    match node.kind() {
9890        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
9891            let name = node_text(node, source).trim().to_string();
9892            (!name.is_empty()).then_some(name)
9893        }
9894        _ => node
9895            .child_by_field_name("declarator")
9896            .or_else(|| node.child_by_field_name("name"))
9897            .or_else(|| last_named_child(node))
9898            .and_then(|child| extract_variable_name(child, source)),
9899    }
9900}
9901
9902/// Recover a C field whose name is wrapped in the function-like
9903/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
9904/// function declarator, while retaining its argument as a structured child.
9905/// The exact macro name and one-argument shape keep ordinary function-pointer
9906/// fields and unknown malformed declarators fail-closed.
9907#[derive(Clone, Copy)]
9908pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
9909    pub(crate) name: Node<'tree>,
9910    pub(crate) declarator: Node<'tree>,
9911}
9912
9913impl RecoveredFunctionLikeFieldDeclarator<'_> {
9914    pub(crate) fn pointer_depth(self) -> i32 {
9915        let mut depth = 0;
9916        let mut current = self.declarator;
9917        while current.kind() != "function_declarator" {
9918            if current.kind() == "pointer_declarator" {
9919                depth += 1;
9920            }
9921            current = current
9922                .child_by_field_name("declarator")
9923                .expect("recovered field wrapper has an inner declarator");
9924        }
9925        depth
9926    }
9927}
9928
9929/// Whether a class-scope field declaration is really a function-like macro
9930/// invocation whose replacement the parser never saw.
9931///
9932/// The invocation carries no terminator of its own, so tree-sitter invents a
9933/// zero-width `;` and reads the macro name as the member's type and the
9934/// argument list as a `parenthesized_declarator`. That shape is not a member:
9935/// a member terminates itself, and with a real terminator the grammar reads
9936/// `T (name);` as a declaration rather than a field. Only an identifier-shaped
9937/// type can name the macro being invoked; a builtin type keeps whatever
9938/// recovery the ordinary declarator walk makes of it.
9939fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
9940    if node.kind() != "field_declaration" {
9941        return false;
9942    }
9943    let Some(type_node) = node.child_by_field_name("type") else {
9944        return false;
9945    };
9946    if !matches!(type_node.kind(), "identifier" | "type_identifier") {
9947        return false;
9948    }
9949    let Some(declarator) = node.child_by_field_name("declarator") else {
9950        return false;
9951    };
9952    if declarator.kind() != "parenthesized_declarator" {
9953        return false;
9954    }
9955    let mut invented_terminator = false;
9956    for child in children_iter(node) {
9957        if child.kind() != ";" {
9958            continue;
9959        }
9960        if !child.is_missing() {
9961            return false;
9962        }
9963        invented_terminator = true;
9964    }
9965    invented_terminator
9966}
9967
9968pub(crate) fn recovered_function_like_field_declarator<'tree>(
9969    node: Node<'tree>,
9970    source: &str,
9971) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
9972    if node.kind() != "field_declaration" {
9973        return None;
9974    }
9975    let outer_declarator = node.child_by_field_name("declarator")?;
9976    let mut declarator = outer_declarator;
9977    while matches!(
9978        declarator.kind(),
9979        "pointer_declarator"
9980            | "reference_declarator"
9981            | "array_declarator"
9982            | "parenthesized_declarator"
9983    ) {
9984        declarator = declarator.child_by_field_name("declarator")?;
9985    }
9986    if declarator.kind() != "function_declarator" {
9987        return None;
9988    }
9989    let macro_name = declarator.child_by_field_name("declarator")?;
9990    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
9991    {
9992        return None;
9993    }
9994    let parameters = declarator.child_by_field_name("parameters")?;
9995    let mut cursor = parameters.walk();
9996    let mut arguments = parameters.named_children(&mut cursor);
9997    let parameter = arguments.next()?;
9998    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
9999        return None;
10000    }
10001    let name = parameter.child_by_field_name("type").filter(|argument| {
10002        matches!(
10003            argument.kind(),
10004            "identifier" | "field_identifier" | "type_identifier"
10005        )
10006    })?;
10007    Some(RecoveredFunctionLikeFieldDeclarator {
10008        name,
10009        declarator: outer_declarator,
10010    })
10011}
10012
10013fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
10014    let count = node.named_child_count();
10015    if count == 0 {
10016        None
10017    } else {
10018        node.named_child(count - 1)
10019    }
10020}
10021
10022fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
10023    let name_node = node.child_by_field_name("name")?;
10024    let name = normalize_cpp_whitespace(node_text(name_node, source));
10025    (!name.is_empty()).then_some(name)
10026}
10027
10028fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
10029    if node.kind() != "declaration" {
10030        return Vec::new();
10031    }
10032    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
10033        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
10034    }) else {
10035        return Vec::new();
10036    };
10037    let Some(declarator) = node.child_by_field_name("declarator") else {
10038        return Vec::new();
10039    };
10040    if node_text(keyword, source) == "using"
10041        && (declarator.kind() != "init_declarator"
10042            || declarator.child_by_field_name("value").is_none())
10043    {
10044        return Vec::new();
10045    }
10046    if node_text(keyword, source) == "typedef"
10047        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
10048    {
10049        return vec![alias_name];
10050    }
10051    extract_typedef_declarator_name(declarator, source)
10052        .into_iter()
10053        .collect()
10054}
10055
10056fn recovered_typedef_error_alias_name(
10057    declaration: Node<'_>,
10058    declarator: Node<'_>,
10059    source: &str,
10060) -> Option<String> {
10061    // An export macro between `class` and its name can make tree-sitter parse
10062    // the recovered class body as a function body. In that shape,
10063    //
10064    //     typedef spi::Filter BASE_CLASS;
10065    //
10066    // becomes a declaration whose `declarator` is the underlying qualified
10067    // type (`spi::Filter`) and whose actual alias name is displaced into the
10068    // following ERROR node. Do not publish the terminal underlying type
10069    // (`Filter`) as a false class-owned alias.
10070    if declarator.kind() != "qualified_identifier" {
10071        return None;
10072    }
10073    let mut cursor = declaration.walk();
10074    let mut errors = declaration
10075        .named_children(&mut cursor)
10076        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
10077    let error = errors.next()?;
10078    if errors.next().is_some() || error.named_child_count() != 1 {
10079        return None;
10080    }
10081    let name = error.named_child(0)?;
10082    if !matches!(
10083        name.kind(),
10084        "identifier" | "field_identifier" | "type_identifier"
10085    ) {
10086        return None;
10087    }
10088    let name = normalize_cpp_whitespace(node_text(name, source));
10089    (!name.is_empty()).then_some(name)
10090}
10091
10092fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
10093    // A function-like token in the type position can make tree-sitter expose
10094    // its argument as a parenthesized declarator. Do not publish that argument
10095    // as an alias. The macro-specific recovery below handles the proven shape.
10096    if fragmented_parenthesized_typedef_type(node).is_some() {
10097        return Vec::new();
10098    }
10099    let has_function_like_macro_type = node
10100        .child_by_field_name("type")
10101        .filter(|type_node| type_node.kind() == "type_identifier")
10102        .is_some_and(|type_node| {
10103            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10104        });
10105    let mut names = Vec::new();
10106    let mut cursor = node.walk();
10107    for declarator in node.children_by_field_name("declarator", &mut cursor) {
10108        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
10109            continue;
10110        }
10111        if let Some(name) = extract_typedef_declarator_name(declarator, source)
10112            && !names.contains(&name)
10113        {
10114            names.push(name);
10115        }
10116    }
10117    names
10118}
10119
10120struct RecoveredMacroTypedefAlias<'tree> {
10121    name: String,
10122    end_node: Node<'tree>,
10123}
10124
10125/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
10126/// into an identifier expression statement. The uppercase macro token, missing
10127/// typedef terminator, and complete sibling terminator prove this exact shape.
10128fn recovered_macro_typedef_alias<'tree>(
10129    node: Node<'tree>,
10130    source: &str,
10131) -> Option<RecoveredMacroTypedefAlias<'tree>> {
10132    let type_node = fragmented_parenthesized_typedef_type(node)?;
10133    if type_node.kind() != "type_identifier"
10134        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10135    {
10136        return None;
10137    }
10138
10139    let end_node = node.next_named_sibling()?;
10140    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
10141        return None;
10142    }
10143    let name_node = end_node.named_child(0)?;
10144    if name_node.kind() != "identifier" {
10145        return None;
10146    }
10147    let has_terminator = (0..end_node.child_count()).any(|index| {
10148        end_node
10149            .child(index)
10150            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
10151    });
10152    if !has_terminator {
10153        return None;
10154    }
10155    let name = normalize_cpp_whitespace(node_text(name_node, source));
10156    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
10157}
10158
10159fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
10160    if node.kind() != "type_definition" {
10161        return None;
10162    }
10163    let mut declarator_cursor = node.walk();
10164    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
10165    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
10166        return None;
10167    }
10168    let has_missing_terminator = (0..node.child_count()).any(|index| {
10169        node.child(index)
10170            .is_some_and(|child| child.kind() == ";" && child.is_missing())
10171    });
10172    if !has_missing_terminator {
10173        return None;
10174    }
10175    node.child_by_field_name("type")
10176}
10177
10178fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
10179    match node.kind() {
10180        "identifier" | "field_identifier" | "type_identifier" => {
10181            let name = normalize_cpp_whitespace(node_text(node, source));
10182            (!name.is_empty()).then_some(name)
10183        }
10184        "qualified_identifier" => node
10185            .child_by_field_name("name")
10186            .and_then(|name| extract_typedef_declarator_name(name, source)),
10187        _ => node
10188            .child_by_field_name("declarator")
10189            .or_else(|| node.child_by_field_name("name"))
10190            .or_else(|| last_named_child(node))
10191            .and_then(|child| extract_typedef_declarator_name(child, source)),
10192    }
10193}
10194
10195fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
10196    let name = node
10197        .child_by_field_name("name")
10198        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10199        .or_else(|| {
10200            let mut cursor = node.walk();
10201            node.named_children(&mut cursor)
10202                .find(|child| {
10203                    matches!(
10204                        child.kind(),
10205                        "identifier" | "field_identifier" | "type_identifier"
10206                    )
10207                })
10208                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10209        })?;
10210    (!name.is_empty()).then_some(name)
10211}
10212
10213fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
10214    left.id() == right.id()
10215}
10216
10217fn render_cpp_type_signature(
10218    node: Node<'_>,
10219    source: &str,
10220    template_signature: Option<&str>,
10221) -> String {
10222    let text = normalize_cpp_whitespace(node_text(node, source));
10223    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
10224    let rendered = if head.ends_with(';') {
10225        head.to_string()
10226    } else {
10227        format!("{head} {{")
10228    };
10229    if let Some(template_signature) = template_signature {
10230        format!("template {template_signature} {rendered}")
10231    } else {
10232        rendered
10233    }
10234}
10235
10236fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
10237    if let Some(recovered) = recovered_pyobject_head_field(node, source)
10238        && recovered.declarator == declarator
10239    {
10240        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
10241        let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
10242        return format!("{type_text} {declarator};");
10243    }
10244    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
10245        && recovered.name == declarator
10246    {
10247        let type_text = node
10248            .child_by_field_name("type")
10249            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
10250            .unwrap_or_default();
10251        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
10252        let mut prefix = String::new();
10253        let mut suffix = String::new();
10254        let mut current = recovered.declarator;
10255        while current.kind() != "function_declarator" {
10256            match current.kind() {
10257                "pointer_declarator" => prefix.push('*'),
10258                "reference_declarator" => prefix.push('&'),
10259                "array_declarator" => {
10260                    let size = current
10261                        .child_by_field_name("size")
10262                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
10263                        .unwrap_or_default();
10264                    suffix.push('[');
10265                    suffix.push_str(&size);
10266                    suffix.push(']');
10267                }
10268                "parenthesized_declarator" => {}
10269                _ => unreachable!("validated recovered field declarator wrapper"),
10270            }
10271            current = current
10272                .child_by_field_name("declarator")
10273                .expect("recovered field wrapper has an inner declarator");
10274        }
10275        let separator = if prefix.is_empty() { "" } else { " " };
10276        return format!("{type_text} {prefix}{separator}{name}{suffix};");
10277    }
10278    if let Some(signature) =
10279        render_recovered_macro_qualified_field_signature(node, declarator, source)
10280    {
10281        return signature;
10282    }
10283    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
10284    let prefix = cpp_declaration_prefix(node, source);
10285    let name = extract_variable_name(declarator, source).unwrap_or_default();
10286    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
10287    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
10288        || (prefix.ends_with('&') && raw_suffix == "&")
10289    {
10290        String::new()
10291    } else {
10292        raw_suffix
10293    };
10294
10295    let mut rendered = if suffix.is_empty() {
10296        format!("{prefix} {name}")
10297    } else if suffix.starts_with('*') || suffix.starts_with('&') {
10298        format!("{prefix}{suffix} {name}")
10299    } else if suffix.starts_with('[') || suffix.starts_with('(') {
10300        format!("{prefix} {name}{suffix}")
10301    } else {
10302        format!("{prefix} {suffix}{name}")
10303    };
10304    rendered = collapse_cpp_whitespace(&rendered);
10305
10306    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10307        format!("{rendered} = {initializer};")
10308    } else if declaration_text.ends_with(';') {
10309        format!("{rendered};")
10310    } else {
10311        rendered
10312    }
10313}
10314
10315fn render_recovered_macro_qualified_field_signature(
10316    node: Node<'_>,
10317    declarator: Node<'_>,
10318    source: &str,
10319) -> Option<String> {
10320    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
10321    if !recovered
10322        .iter()
10323        .any(|candidate| same_node(*candidate, declarator))
10324    {
10325        return None;
10326    }
10327    let pseudo_declarator = node.child_by_field_name("declarator")?;
10328    let mut cursor = node.walk();
10329    let clause = node
10330        .named_children(&mut cursor)
10331        .find(|child| child.kind() == "bitfield_clause")?;
10332    let mut cursor = clause.walk();
10333    let error = clause
10334        .named_children(&mut cursor)
10335        .find(|child| child.kind() == "ERROR")?;
10336    let qualified_type =
10337        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
10338    let prefix = cpp_declaration_prefix(node, source);
10339    let name = extract_variable_name(declarator, source)?;
10340    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
10341    let mut rendered = if suffix.is_empty() {
10342        format!("{prefix} {qualified_type} {name}")
10343    } else {
10344        format!("{prefix} {qualified_type} {suffix} {name}")
10345    };
10346    rendered = collapse_cpp_whitespace(&rendered);
10347
10348    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
10349        Some(format!(
10350            "{rendered} = {};",
10351            normalize_cpp_whitespace(node_text(initializer, source))
10352        ))
10353    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10354        Some(format!("{rendered} = {initializer};"))
10355    } else {
10356        Some(format!("{rendered};"))
10357    }
10358}
10359
10360fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
10361    match node.kind() {
10362        "pointer_expression" => {
10363            let operator = node
10364                .child_by_field_name("operator")
10365                .or_else(|| node.child(0))
10366                .map(|operator| node_text(operator, source))
10367                .unwrap_or("*");
10368            let argument = node
10369                .child_by_field_name("argument")
10370                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10371                .unwrap_or_default();
10372            format!("{operator}{argument}")
10373        }
10374        "unary_expression" => {
10375            let operator = node
10376                .child_by_field_name("operator")
10377                .or_else(|| node.child(0))
10378                .map(|operator| node_text(operator, source))
10379                .unwrap_or_default();
10380            let argument = node
10381                .child_by_field_name("argument")
10382                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10383                .unwrap_or_default();
10384            format!("{operator}{argument}")
10385        }
10386        "identifier" | "field_identifier" => String::new(),
10387        _ => cpp_declarator_suffix_without_name(node, source),
10388    }
10389}
10390
10391fn recovered_macro_qualified_field_initializer<'tree>(
10392    clause: Node<'tree>,
10393    declarator: Node<'tree>,
10394) -> Option<Node<'tree>> {
10395    let mut stack = vec![clause];
10396    while let Some(current) = stack.pop() {
10397        if current.kind() == "assignment_expression"
10398            && current
10399                .child_by_field_name("left")
10400                .is_some_and(|left| same_node(left, declarator))
10401        {
10402            return current.child_by_field_name("right");
10403        }
10404        let mut cursor = current.walk();
10405        stack.extend(current.named_children(&mut cursor));
10406    }
10407    None
10408}
10409
10410fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
10411    let text = node_text(node, source);
10412    let mut cursor = node.walk();
10413    let first_declarator = node.named_children(&mut cursor).find(|child| {
10414        matches!(
10415            child.kind(),
10416            "init_declarator"
10417                | "identifier"
10418                | "field_identifier"
10419                | "pointer_declarator"
10420                | "reference_declarator"
10421                | "array_declarator"
10422                | "function_declarator"
10423        )
10424    });
10425    let prefix = if let Some(first_declarator) = first_declarator {
10426        let end = first_declarator
10427            .start_byte()
10428            .saturating_sub(node.start_byte());
10429        let mut prefix = text.get(..end).unwrap_or(text).to_string();
10430        let declarator_suffix = match first_declarator.kind() {
10431            "init_declarator" => first_declarator
10432                .child_by_field_name("declarator")
10433                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
10434                .unwrap_or_default(),
10435            _ => cpp_declarator_suffix_without_name(first_declarator, source),
10436        };
10437        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
10438            prefix.push_str(&declarator_suffix);
10439        }
10440        return collapse_cpp_whitespace(&prefix)
10441            .trim_end_matches(',')
10442            .trim_end_matches(';')
10443            .trim()
10444            .to_string();
10445    } else {
10446        text
10447    };
10448    collapse_cpp_whitespace(prefix)
10449        .trim_end_matches(',')
10450        .trim_end_matches(';')
10451        .trim()
10452        .to_string()
10453}
10454
10455fn cpp_preserved_initializer(
10456    declaration_node: Node<'_>,
10457    declarator: Node<'_>,
10458    source: &str,
10459) -> Option<String> {
10460    let name = extract_variable_name(declarator, source)?;
10461    let mut cursor = declaration_node.walk();
10462    for child in declaration_node.named_children(&mut cursor) {
10463        if child.kind() != "init_declarator" {
10464            continue;
10465        }
10466        let Some(inner) = child.child_by_field_name("declarator") else {
10467            continue;
10468        };
10469        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
10470            continue;
10471        }
10472        let value = child.child_by_field_name("value")?;
10473        let kind = value.kind();
10474        if matches!(
10475            kind,
10476            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
10477        ) {
10478            return Some(normalize_cpp_whitespace(node_text(value, source)));
10479        }
10480        break;
10481    }
10482    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
10483    let pattern = format!(
10484        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
10485        regex::escape(&name)
10486    );
10487    Regex::new(&pattern)
10488        .ok()
10489        .and_then(|regex| regex.captures(&declaration_text))
10490        .and_then(|captures| captures.get(1))
10491        .map(|value| value.as_str().to_string())
10492}
10493
10494fn render_cpp_function_display_signature_from_node<'tree>(
10495    node: Node<'tree>,
10496    source: &str,
10497    template_signature: Option<&str>,
10498    has_body: bool,
10499    ancestry: &ParentIndex<'tree>,
10500) -> String {
10501    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
10502    let parent_text = node_text(root, source);
10503    let body_local_start = root
10504        .child_by_field_name("body")
10505        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
10506        .unwrap_or(parent_text.len());
10507    let display = parent_text
10508        .get(..body_local_start)
10509        .unwrap_or(parent_text)
10510        .trim()
10511        .trim();
10512    let display = if let Some(template_signature) = template_signature {
10513        if display.starts_with("template ") {
10514            display.to_string()
10515        } else {
10516            format!("template {template_signature} {display}")
10517        }
10518    } else {
10519        display.to_string()
10520    };
10521    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
10522    if has_body {
10523        format!("{display} {{...}}")
10524    } else {
10525        format!("{display};")
10526    }
10527}
10528
10529fn cpp_template_signature(
10530    template_node: Node<'_>,
10531    declaration_child: Node<'_>,
10532    source: &str,
10533) -> Option<String> {
10534    let text = source
10535        .get(template_node.start_byte()..declaration_child.start_byte())
10536        .unwrap_or("");
10537    let text = normalize_cpp_whitespace(text);
10538    let start = text.find('<')?;
10539    let end = text.rfind('>')?;
10540    if end < start {
10541        return None;
10542    }
10543    Some(text[start..=end].to_string())
10544}
10545
10546struct RecoveredFragmentedPartialSpecialization<'tree> {
10547    declaration_node: Node<'tree>,
10548    name: String,
10549    range: Range,
10550    prefix_members: Vec<Node<'tree>>,
10551    member_siblings: Vec<Node<'tree>>,
10552    following_declarations: Vec<Node<'tree>>,
10553}
10554
10555struct RecoveredFragmentedPreprocessorClass<'tree> {
10556    declaration_node: Node<'tree>,
10557    class_node: Node<'tree>,
10558    body: Node<'tree>,
10559    name: String,
10560    range: Range,
10561    tail_members: Vec<Node<'tree>>,
10562    member_siblings: Vec<Node<'tree>>,
10563}
10564
10565/// Recover a class whose preprocessor-fragmented parse closes at an early
10566/// member body and publishes the remaining in-class declarations as siblings
10567/// of the surrounding alternative. Primary classes are admitted only when an
10568/// earlier branch contains the matching bodyless declaration and the class
10569/// node retains the displaced `#endif`. Partial specializations instead carry
10570/// their identity structurally in the `template_type` name and template
10571/// metadata. Retain the original AST nodes and re-own only the siblings through
10572/// the displaced structural `};` terminator.
10573fn recover_fragmented_preprocessor_class<'tree>(
10574    template_node: Node<'tree>,
10575    source: &str,
10576    ancestry: &ParentIndex<'tree>,
10577) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
10578    let alternative = ancestry.parent(template_node)?;
10579    if alternative.kind() != "preproc_else" {
10580        return None;
10581    }
10582    let conditional = alternative.parent()?;
10583    if conditional.kind() != "preproc_if" {
10584        return None;
10585    }
10586    let declaration_node = template_node
10587        .named_children(&mut template_node.walk())
10588        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
10589    let class_node = declaration_node
10590        .named_children(&mut declaration_node.walk())
10591        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
10592    let body = cpp_body_node(class_node)?;
10593    if class_node.end_byte() >= declaration_node.end_byte() {
10594        return None;
10595    }
10596    let name = class_like_name(class_node, source, ancestry)?;
10597    let is_partial_specialization = class_node
10598        .child_by_field_name("name")
10599        .is_some_and(|class_name| class_name.kind() == "template_type");
10600    if is_partial_specialization {
10601        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
10602        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
10603            return None;
10604        }
10605    } else {
10606        if !class_has_displaced_preprocessor_terminator(class_node) {
10607            return None;
10608        }
10609        let matching_other_branch = conditional
10610            .named_children(&mut conditional.walk())
10611            .take_while(|child| !same_node(*child, alternative))
10612            .filter(|child| child.kind() == "template_declaration")
10613            .filter_map(first_class_like_child)
10614            .any(|candidate| {
10615                cpp_body_node(candidate).is_none()
10616                    && class_like_name(candidate, source, ancestry).as_deref()
10617                        == Some(name.as_str())
10618            });
10619        if !matching_other_branch {
10620            return None;
10621        }
10622    }
10623
10624    let mut tail_members = Vec::new();
10625    let mut saw_class = false;
10626    let mut declaration_cursor = declaration_node.walk();
10627    for child in declaration_node.named_children(&mut declaration_cursor) {
10628        if same_node(child, class_node) {
10629            saw_class = true;
10630        } else if saw_class {
10631            tail_members.push(child);
10632        }
10633    }
10634
10635    let mut member_siblings = Vec::new();
10636    let mut saw_template = false;
10637    let mut terminator = None;
10638    for index in 0..alternative.child_count() {
10639        let Some(child) = alternative.child(index) else {
10640            continue;
10641        };
10642        if same_node(child, template_node) {
10643            saw_template = true;
10644            continue;
10645        }
10646        if !saw_template {
10647            continue;
10648        }
10649        if displaced_fragmented_class_terminator(alternative, index) {
10650            terminator = alternative.child(index + 1);
10651            break;
10652        }
10653        if child.is_named() {
10654            member_siblings.push(child);
10655        }
10656    }
10657    let terminator = terminator?;
10658    Some(RecoveredFragmentedPreprocessorClass {
10659        declaration_node,
10660        class_node,
10661        body,
10662        name,
10663        range: Range {
10664            start_byte: class_node.start_byte(),
10665            end_byte: terminator.end_byte(),
10666            start_line: class_node.start_position().row + 1,
10667            end_line: terminator.end_position().row + 1,
10668        },
10669        tail_members,
10670        member_siblings,
10671    })
10672}
10673
10674fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
10675    (0..class_node.child_count()).any(|index| {
10676        class_node.child(index).is_some_and(|child| {
10677            child.kind() == "ERROR"
10678                && (0..child.child_count()).any(|error_index| {
10679                    child
10680                        .child(error_index)
10681                        .is_some_and(|token| token.kind() == "#endif")
10682                })
10683        })
10684    })
10685}
10686
10687/// The real `#endif` that tree-sitter consumed inside an error subtree.
10688///
10689/// A preprocessor directive inside a malformed array bound can cause later
10690/// declarations to remain children of the conditional. The non-missing token
10691/// still gives the exact structured boundary. Ignore nested conditionals and
10692/// select the last unpaired error-owned token. Tree-sitter can pair a later
10693/// outer `#endif` with this conditional, so the direct terminator is not
10694/// necessarily missing.
10695pub fn cpp_displaced_preprocessor_terminator<'tree>(
10696    conditional: Node<'tree>,
10697) -> Option<Node<'tree>> {
10698    if !conditional.has_error() {
10699        return None;
10700    }
10701    let has_concrete_direct_terminator = conditional
10702        .child_count()
10703        .checked_sub(1)
10704        .and_then(|index| conditional.child(index))
10705        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
10706    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
10707        // A structured alternative proves that the direct `#endif` closes
10708        // this family. An error-owned terminator inside either branch belongs
10709        // to a damaged nested conditional, not to this one.
10710        return None;
10711    }
10712    // Pair concrete directive tokens while walking the recovered subtree. An
10713    // inner conditional may be represented only by its `#ifndef`/`#endif`
10714    // tokens under an `ERROR`, so skipping nested preprocessor nodes is not
10715    // sufficient: an inner `#endif` can otherwise be mistaken for this
10716    // conditional's displaced terminator.
10717    let mut displaced = None;
10718    let mut conditional_depth = 0usize;
10719    let mut stack = children_iter(conditional)
10720        .map(|child| (child, false))
10721        .collect::<Vec<_>>();
10722    stack.reverse();
10723    while let Some((node, inside_error)) = stack.pop() {
10724        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
10725            continue;
10726        }
10727        if node != conditional
10728            && matches!(
10729                node.kind(),
10730                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
10731            )
10732        {
10733            continue;
10734        }
10735        let inside_error = inside_error || node.kind() == "ERROR";
10736        match node.kind() {
10737            "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
10738                conditional_depth += 1;
10739            }
10740            "#endif" if !node.is_missing() => {
10741                if conditional_depth == 0
10742                    && inside_error
10743                    && displaced
10744                        .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
10745                {
10746                    displaced = Some(node);
10747                }
10748                conditional_depth = conditional_depth.saturating_sub(1);
10749            }
10750            _ => {}
10751        }
10752        let first_pushed = stack.len();
10753        stack.extend(children_iter(node).map(|child| (child, inside_error)));
10754        stack[first_pushed..].reverse();
10755    }
10756    displaced
10757}
10758
10759/// The effective end of a conditional whose real terminator tree-sitter
10760/// displaced into declaration recovery.
10761///
10762/// Most damaged conditionals retain a concrete `#endif` token below an
10763/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
10764/// boundary. A preprocessor family that selects the middle of a declaration
10765/// can lose the directive tokens entirely. In that shape tree-sitter leaves
10766/// the declaration's `typedef` token as the sole child of the immediately
10767/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
10768/// declarator name inside the conditional's first declaration. The declaration
10769/// end is then the smallest structured boundary that contains the whole split
10770/// declaration.
10771#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10772pub struct CppDisplacedPreprocessorBoundary {
10773    pub end_byte: usize,
10774    pub end_line: usize,
10775}
10776
10777pub fn cpp_displaced_preprocessor_boundary(
10778    conditional: Node<'_>,
10779) -> Option<CppDisplacedPreprocessorBoundary> {
10780    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
10781        return Some(CppDisplacedPreprocessorBoundary {
10782            end_byte: terminator.end_byte(),
10783            end_line: terminator.end_position().row + 1,
10784        });
10785    }
10786    if let Some(declaration) = displaced_split_declaration(conditional) {
10787        return Some(CppDisplacedPreprocessorBoundary {
10788            end_byte: declaration.end_byte(),
10789            end_line: declaration.end_position().row + 1,
10790        });
10791    }
10792    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
10793        return Some(CppDisplacedPreprocessorBoundary {
10794            end_byte: terminator.end_byte(),
10795            end_line: terminator.end_position().row + 1,
10796        });
10797    }
10798    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
10799        return Some(CppDisplacedPreprocessorBoundary {
10800            end_byte: terminator.end_byte(),
10801            end_line: terminator.end_position().row + 1,
10802        });
10803    }
10804    None
10805}
10806
10807/// Recover an outer terminator that tree-sitter assigned to a damaged nested
10808/// conditional. This occurs when a split construct such as `extern "C"`
10809/// consumes the nested `#endif` inside an error node: the nested conditional's
10810/// direct terminator is then the outer conditional's real terminator, while
10811/// the outer node ends with a missing token and absorbs later declarations.
10812fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10813    if !conditional.has_error()
10814        || conditional.child_by_field_name("alternative").is_some()
10815        || conditional
10816            .child(conditional.child_count().saturating_sub(1))
10817            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
10818    {
10819        return None;
10820    }
10821    let mut recovered = None;
10822    for nested in named_children_iter(conditional) {
10823        if !matches!(
10824            nested.kind(),
10825            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
10826        ) || nested.child_by_field_name("alternative").is_some()
10827        {
10828            continue;
10829        }
10830        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
10831            continue;
10832        };
10833        if direct.kind() != "#endif" || direct.is_missing() {
10834            continue;
10835        }
10836        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
10837            continue;
10838        };
10839        if displaced.end_byte() >= direct.start_byte() {
10840            continue;
10841        }
10842        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
10843            recovered = Some(direct);
10844        }
10845    }
10846    recovered
10847}
10848
10849fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10850    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
10851        return None;
10852    }
10853    let mut cursor = conditional.walk();
10854    let declarations = conditional
10855        .named_children(&mut cursor)
10856        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
10857        .collect::<Vec<_>>();
10858    let declaration = *declarations.first()?;
10859    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
10860        return None;
10861    }
10862    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
10863    let mut terminator = None;
10864    let mut stack = (0..declaration.child_count())
10865        .filter_map(|index| declaration.child(index))
10866        .filter(|child| child.start_byte() < declarator_start)
10867        .map(|child| (child, false))
10868        .collect::<Vec<_>>();
10869    while let Some((node, inside_error)) = stack.pop() {
10870        let inside_error = inside_error || node.kind() == "ERROR";
10871        if inside_error && node.kind() == "#endif" && !node.is_missing() {
10872            terminator = Some(node);
10873            continue;
10874        }
10875        for index in 0..node.child_count() {
10876            if let Some(child) = node.child(index)
10877                && child.start_byte() < declarator_start
10878            {
10879                stack.push((child, inside_error));
10880            }
10881        }
10882    }
10883    terminator
10884}
10885
10886fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10887    if !conditional.has_error()
10888        || conditional.child_by_field_name("alternative").is_some()
10889        || conditional
10890            .prev_named_sibling()
10891            .filter(|sibling| {
10892                sibling.kind() == "ERROR"
10893                    && sibling.child_count() == 1
10894                    && sibling
10895                        .child(0)
10896                        .is_some_and(|child| child.kind() == "typedef")
10897            })
10898            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
10899            .is_none()
10900    {
10901        return None;
10902    }
10903    let mut cursor = conditional.walk();
10904    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
10905    let declaration_index = children
10906        .iter()
10907        .position(|child| child.kind() == "declaration" && child.has_error())?;
10908    let declaration = children[declaration_index];
10909    if !children
10910        .iter()
10911        .skip(declaration_index + 1)
10912        .any(|child| child.end_byte() > declaration.end_byte())
10913    {
10914        return None;
10915    }
10916    let declarator = declaration.child_by_field_name("declarator")?;
10917    let mut error_end = None;
10918    let mut names = Vec::new();
10919    let mut stack = vec![declarator];
10920    while let Some(node) = stack.pop() {
10921        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
10922            error_end =
10923                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
10924            continue;
10925        }
10926        if matches!(node.kind(), "identifier" | "type_identifier") {
10927            names.push(node.start_byte());
10928        }
10929        push_named_children_reversed(node, &mut stack);
10930    }
10931    let error_end = error_end?;
10932    names
10933        .into_iter()
10934        .any(|start| start >= error_end)
10935        .then_some(declaration)
10936}
10937
10938fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
10939    let Some(error) = parent.child(error_index) else {
10940        return false;
10941    };
10942    if error.kind() != "ERROR"
10943        || error.child_count() != 1
10944        || error.child(0).is_none_or(|child| child.kind() != "}")
10945    {
10946        return false;
10947    }
10948    let Some(semicolon) = parent.child(error_index + 1) else {
10949        return false;
10950    };
10951    semicolon.kind() == "expression_statement"
10952        && semicolon.child_count() == 1
10953        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
10954}
10955
10956/// Locate the real end of a class-like declaration when a macro invocation
10957/// without a source semicolon absorbs the class's `};` into its parsed field.
10958/// The grammar then keeps following namespace declarations as later children
10959/// of the same field list. The direct ERROR-plus-semicolon pair proves the
10960/// boundary structurally; no source-text delimiter scan is needed.
10961fn displaced_macro_class_tail(
10962    declaration_node: Node<'_>,
10963    body: Node<'_>,
10964    source: &str,
10965) -> Option<DisplacedMacroClassTail> {
10966    if !matches!(
10967        declaration_node.kind(),
10968        "class_specifier" | "struct_specifier" | "union_specifier"
10969    ) || body.kind() != "field_declaration_list"
10970    {
10971        return None;
10972    }
10973
10974    let child_count = body.named_child_count();
10975    for index in 0..child_count {
10976        let child = body.named_child(index)?;
10977        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
10978            continue;
10979        };
10980        let split_index = index + 1;
10981        if split_index >= child_count {
10982            return None;
10983        }
10984        let mut cursor = body.walk();
10985        if !body
10986            .named_children(&mut cursor)
10987            .skip(split_index)
10988            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
10989        {
10990            return None;
10991        }
10992        return Some(DisplacedMacroClassTail {
10993            split_index,
10994            class_range: Range {
10995                start_byte: declaration_node.start_byte(),
10996                end_byte: terminator.end_byte(),
10997                start_line: declaration_node.start_position().row + 1,
10998                end_line: terminator.end_position().row + 1,
10999            },
11000        });
11001    }
11002    None
11003}
11004
11005fn displaced_macro_field_terminator<'tree>(
11006    field: Node<'tree>,
11007    source: &str,
11008) -> Option<Node<'tree>> {
11009    if field.kind() != "field_declaration" {
11010        return None;
11011    }
11012    let macro_type = field.child_by_field_name("type")?;
11013    if macro_type.kind() != "type_identifier"
11014        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11015        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
11016    {
11017        return None;
11018    }
11019    for index in 0..field.child_count() {
11020        let error = field.child(index)?;
11021        if error.kind() != "ERROR"
11022            || error.child_count() != 1
11023            || error.child(0).is_none_or(|child| child.kind() != "}")
11024        {
11025            continue;
11026        }
11027        let semicolon = field.child(index + 1)?;
11028        if semicolon.kind() == ";" {
11029            return Some(semicolon);
11030        }
11031    }
11032    None
11033}
11034
11035fn recover_fragmented_partial_specialization<'tree>(
11036    template_node: Node<'tree>,
11037    declaration_child: Node<'tree>,
11038    source: &str,
11039    ancestry: &ParentIndex<'tree>,
11040) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
11041    if declaration_child.kind() != "function_definition" {
11042        return None;
11043    }
11044    let class_node = declaration_child.child_by_field_name("type")?;
11045    if !matches!(
11046        class_node.kind(),
11047        "class_specifier" | "struct_specifier" | "union_specifier"
11048    ) || !class_node
11049        .child_by_field_name("name")
11050        .and_then(|name| direct_identifier_name(name, source))
11051        .is_some_and(|name| cpp_export_macro_token(&name))
11052    {
11053        return None;
11054    }
11055    let declarator = declaration_child.child_by_field_name("declarator")?;
11056    if declarator.kind() != "template_function" {
11057        return None;
11058    }
11059    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
11060    if metadata.specialization_arguments.is_empty() {
11061        return None;
11062    }
11063    let body = declaration_child.child_by_field_name("body")?;
11064    if body.kind() != "compound_statement" {
11065        return None;
11066    }
11067    let complete_prefix = body.named_child(0).filter(|first| {
11068        first.kind() == "labeled_statement"
11069            && first.has_error()
11070            && first
11071                .named_child(first.named_child_count().saturating_sub(1))
11072                .is_some_and(recovered_declaration_has_class_terminator)
11073    });
11074    let complete_body = complete_prefix.is_some();
11075    let mut prefix_members = Vec::new();
11076    if let Some(prefix) = complete_prefix {
11077        prefix_members.push(prefix);
11078    } else {
11079        let mut body_cursor = body.walk();
11080        for child in body.named_children(&mut body_cursor) {
11081            if !is_structurally_valid_fragmented_class_prefix_member(child) {
11082                break;
11083            }
11084            prefix_members.push(child);
11085        }
11086    }
11087    let containing_declarations = template_node.parent()?;
11088    if !matches!(
11089        containing_declarations.kind(),
11090        "declaration_list" | "compound_statement"
11091    ) {
11092        return None;
11093    }
11094    let mut member_siblings = Vec::new();
11095    let mut following_declarations = Vec::new();
11096    let terminator;
11097    if complete_body {
11098        terminator = complete_prefix?;
11099        let mut cursor = body.walk();
11100        let mut after_prefix = false;
11101        for child in body.named_children(&mut cursor) {
11102            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
11103                after_prefix = true;
11104            } else if after_prefix {
11105                following_declarations.push(child);
11106            }
11107        }
11108    } else {
11109        let mut found_template = false;
11110        let mut cursor = containing_declarations.walk();
11111        let mut class_terminator = None;
11112        for child in containing_declarations.children(&mut cursor) {
11113            if same_node(child, template_node) {
11114                found_template = true;
11115                continue;
11116            }
11117            if found_template && child.kind() == "}" {
11118                class_terminator = Some(child);
11119                break;
11120            }
11121            // A namespace can never be a class member: reaching one before the
11122            // terminator proves the class's true close was swallowed upstream
11123            // and this scan has crossed into the enclosing scope, so the
11124            // recovery cannot be bounded -- continuing re-owns the namespace
11125            // block (and its template specializations) as class members under
11126            // a re-appended package, desyncing the fq boundary (#2306).
11127            if found_template && child.kind() == "namespace_definition" {
11128                return None;
11129            }
11130            if found_template && child.is_named() {
11131                member_siblings.push(child);
11132            }
11133        }
11134        terminator = class_terminator?;
11135    }
11136    let name = format!(
11137        "{}<{}>",
11138        metadata.primary_name,
11139        metadata
11140            .specialization_arguments
11141            .iter()
11142            .map(|argument| argument.text.as_str())
11143            .collect::<Vec<_>>()
11144            .join(", ")
11145    );
11146    Some(RecoveredFragmentedPartialSpecialization {
11147        declaration_node: declaration_child,
11148        name,
11149        range: Range {
11150            start_byte: declaration_child.start_byte(),
11151            end_byte: terminator.end_byte(),
11152            start_line: declaration_child.start_position().row + 1,
11153            end_line: terminator.end_position().row + 1,
11154        },
11155        prefix_members,
11156        member_siblings,
11157        following_declarations,
11158    })
11159}
11160
11161/// Whether `node` is the function-shaped parser wrapper whose body was
11162/// recovered as a fragmented partial-specialization class body.
11163///
11164/// Consumers that walk lexical function scopes must use the same complete
11165/// structural proof as declaration extraction. Otherwise member callables in
11166/// the recovered body are mistaken for function-local declarations.
11167pub fn is_recovered_fragmented_partial_specialization_container(
11168    node: Node<'_>,
11169    source: &str,
11170) -> bool {
11171    let Some(template) = node
11172        .parent()
11173        .filter(|parent| parent.kind() == "template_declaration")
11174    else {
11175        return false;
11176    };
11177    let mut root = template;
11178    while let Some(parent) = root.parent() {
11179        root = parent;
11180    }
11181    recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
11182        .is_some()
11183}
11184
11185fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
11186    if declaration.kind() != "declaration" {
11187        return false;
11188    }
11189    // With an export macro between `class` and its name, tree-sitter folds a
11190    // complete class body into a function-shaped declaration. The class's own
11191    // `};` remains structurally identifiable as a direct ERROR child holding
11192    // `}`, immediately followed by the declaration's direct `;` child.
11193    (0..declaration.child_count().saturating_sub(1)).any(|index| {
11194        let Some(error) = declaration.child(index) else {
11195            return false;
11196        };
11197        error.kind() == "ERROR"
11198            && error.child_count() == 1
11199            && error.child(0).is_some_and(|child| child.kind() == "}")
11200            && declaration
11201                .child(index + 1)
11202                .is_some_and(|child| child.kind() == ";")
11203    })
11204}
11205
11206fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
11207    if node.has_error() {
11208        return false;
11209    }
11210    match node.kind() {
11211        "declaration"
11212        | "field_declaration"
11213        | "alias_declaration"
11214        | "type_definition"
11215        | "static_assert_declaration" => true,
11216        "labeled_statement" => node
11217            .named_child(node.named_child_count().saturating_sub(1))
11218            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
11219        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
11220            matches!(
11221                child.kind(),
11222                "declaration"
11223                    | "field_declaration"
11224                    | "alias_declaration"
11225                    | "type_definition"
11226                    | "function_definition"
11227            )
11228        }),
11229        _ => false,
11230    }
11231}
11232
11233fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
11234    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
11235        .then(|| node.child_by_field_name("declarator"))
11236        .flatten()
11237        .and_then(|declarator| extract_variable_name(declarator, source))
11238}
11239
11240fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
11241    while let Some(parent) = node.parent() {
11242        if matches!(parent.kind(), "function_definition" | "lambda_expression") {
11243            return true;
11244        }
11245        node = parent;
11246    }
11247    false
11248}
11249
11250fn cpp_template_metadata<'tree>(
11251    template_node: Node<'tree>,
11252    declaration_child: Node<'tree>,
11253    source: &str,
11254    ancestry: &ParentIndex<'tree>,
11255) -> Option<CppTemplateMetadata> {
11256    let parameters_node = template_node.child_by_field_name("parameters")?;
11257    let name_node = cpp_templated_class_name_node(declaration_child)?;
11258    let primary_node = match name_node.kind() {
11259        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
11260        _ => name_node,
11261    };
11262    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
11263    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
11264        return None;
11265    }
11266
11267    let mut parameter_nodes = Vec::new();
11268    let mut parameter_names = Vec::new();
11269    let mut cursor = parameters_node.walk();
11270    for parameter in parameters_node.named_children(&mut cursor) {
11271        if !matches!(
11272            parameter.kind(),
11273            "type_parameter_declaration"
11274                | "optional_type_parameter_declaration"
11275                | "variadic_type_parameter_declaration"
11276                | "template_template_parameter_declaration"
11277                | "parameter_declaration"
11278                | "optional_parameter_declaration"
11279                | "variadic_parameter_declaration"
11280        ) {
11281            continue;
11282        }
11283        let index = parameter_nodes.len();
11284        // An unnamed parameter still contributes template arity and kind. Use
11285        // an impossible C++ identifier so positional reconciliation can bind
11286        // it without making source expressions refer to a name that was not
11287        // written.
11288        let name = cpp_template_parameter_name(parameter, source)
11289            .unwrap_or_else(|| format!("<anonymous:{index}>"));
11290        parameter_names.push(name);
11291        parameter_nodes.push(parameter);
11292    }
11293    let parameters = parameter_nodes
11294        .into_iter()
11295        .zip(parameter_names.iter().cloned())
11296        .map(|(parameter, name)| CppTemplateParameterMetadata {
11297            name,
11298            kind: cpp_template_parameter_kind(parameter),
11299            variadic: matches!(
11300                parameter.kind(),
11301                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
11302            ),
11303            default: cpp_template_parameter_default_expression(
11304                parameter,
11305                source,
11306                &parameter_names,
11307                ancestry,
11308            ),
11309        })
11310        .collect();
11311    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
11312        Vec::new()
11313    } else {
11314        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
11315            .unwrap_or_default()
11316    };
11317    let alias_target = (declaration_child.kind() == "alias_declaration")
11318        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
11319        .flatten();
11320    Some(CppTemplateMetadata {
11321        primary_name,
11322        primary_fq_name: String::new(),
11323        parameters,
11324        specialization_arguments,
11325        alias_target,
11326    })
11327}
11328
11329fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
11330    match node.kind() {
11331        "class_specifier" | "struct_specifier" | "union_specifier" => {
11332            node.child_by_field_name("name")
11333        }
11334        "function_definition" => {
11335            let declarator = node.child_by_field_name("declarator")?;
11336            if matches!(declarator.kind(), "identifier" | "template_function") {
11337                Some(declarator)
11338            } else {
11339                None
11340            }
11341        }
11342        "alias_declaration" => node.child_by_field_name("name"),
11343        _ => None,
11344    }
11345}
11346
11347fn cpp_template_alias_target<'tree>(
11348    alias: Node<'tree>,
11349    source: &str,
11350    parameter_names: &[String],
11351    ancestry: &ParentIndex<'tree>,
11352) -> Option<CppTemplateAliasTargetMetadata> {
11353    let mut type_node = alias.child_by_field_name("type")?;
11354    while type_node.kind() == "type_descriptor" {
11355        type_node = type_node.child_by_field_name("type")?;
11356    }
11357    let global = type_node.child_by_field_name("scope").is_none()
11358        && type_node.child(0).is_some_and(|child| child.kind() == "::");
11359    let mut components = Vec::new();
11360    cpp_template_target_components(type_node, source, &mut components)?;
11361    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
11362    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
11363        components,
11364        global,
11365        arguments,
11366    })
11367}
11368
11369fn cpp_template_target_components(
11370    node: Node<'_>,
11371    source: &str,
11372    out: &mut Vec<String>,
11373) -> Option<()> {
11374    match node.kind() {
11375        "identifier" | "namespace_identifier" | "type_identifier" => {
11376            out.push(node_text(node, source).to_string());
11377            Some(())
11378        }
11379        "template_type" => {
11380            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11381        }
11382        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11383            if let Some(scope) = node.child_by_field_name("scope") {
11384                cpp_template_target_components(scope, source, out)?;
11385            }
11386            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11387        }
11388        _ => None,
11389    }
11390}
11391
11392fn cpp_template_argument_expressions<'tree>(
11393    mut node: Node<'tree>,
11394    source: &str,
11395    parameter_names: &[String],
11396    ancestry: &ParentIndex<'tree>,
11397) -> Option<Vec<CppTemplateExpression>> {
11398    loop {
11399        match node.kind() {
11400            "template_type" | "template_function" => {
11401                let arguments = node.child_by_field_name("arguments")?;
11402                let mut cursor = arguments.walk();
11403                return Some(
11404                    arguments
11405                        .named_children(&mut cursor)
11406                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
11407                        .map(|argument| {
11408                            cpp_template_expression(argument, source, parameter_names, ancestry)
11409                        })
11410                        .collect(),
11411                );
11412            }
11413            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
11414                node = node
11415                    .child_by_field_name("name")
11416                    .or_else(|| node.child_by_field_name("type"))?;
11417            }
11418            _ => return None,
11419        }
11420    }
11421}
11422
11423fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
11424    let candidate = node
11425        .child_by_field_name("name")
11426        .or_else(|| node.child_by_field_name("declarator"))
11427        .or_else(|| {
11428            let mut cursor = node.walk();
11429            node.named_children(&mut cursor).find(|child| {
11430                matches!(
11431                    child.kind(),
11432                    "identifier" | "type_identifier" | "field_identifier"
11433                )
11434            })
11435        })?;
11436    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
11437    (!name.is_empty()).then_some(name)
11438}
11439
11440fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
11441    match node.kind() {
11442        "type_parameter_declaration"
11443        | "optional_type_parameter_declaration"
11444        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
11445        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
11446        _ => CppTemplateParameterKind::Value,
11447    }
11448}
11449
11450fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
11451    node.child_by_field_name("default_type")
11452        .or_else(|| node.child_by_field_name("default_value"))
11453}
11454
11455fn cpp_template_parameter_default_expression<'tree>(
11456    parameter: Node<'tree>,
11457    source: &str,
11458    parameter_names: &[String],
11459    ancestry: &ParentIndex<'tree>,
11460) -> Option<CppTemplateExpression> {
11461    let default = cpp_template_parameter_default(parameter)?;
11462    let base = cpp_template_expression(default, source, parameter_names, ancestry);
11463    let Some(pointer_error) = parameter.next_named_sibling() else {
11464        return Some(base);
11465    };
11466    let Some(pointer_declarator) =
11467        recovered_abstract_pointer_declarator_term(pointer_error, source)
11468    else {
11469        return Some(base);
11470    };
11471    Some(CppTemplateExpression {
11472        text: format!(
11473            "{}{}",
11474            base.text,
11475            normalize_cpp_whitespace(node_text(pointer_error, source))
11476        ),
11477        term: CppTemplateTerm::Node {
11478            kind: "type_descriptor".to_string(),
11479            children: vec![base.term, pointer_declarator],
11480        },
11481    })
11482}
11483
11484fn recovered_abstract_pointer_declarator_term(
11485    node: Node<'_>,
11486    source: &str,
11487) -> Option<CppTemplateTerm> {
11488    if node.kind() != "ERROR" || node.child_count() == 0 {
11489        return None;
11490    }
11491    let mut children = Vec::new();
11492    for index in 0..node.child_count() {
11493        let child = node.child(index)?;
11494        if child.kind() != "*" {
11495            return None;
11496        }
11497        children.push(CppTemplateTerm::Atom {
11498            kind: "*".to_string(),
11499            text: normalize_cpp_whitespace(node_text(child, source)),
11500        });
11501    }
11502    Some(CppTemplateTerm::Node {
11503        kind: "abstract_pointer_declarator".to_string(),
11504        children,
11505    })
11506}
11507
11508fn cpp_template_expression<'tree>(
11509    node: Node<'tree>,
11510    source: &str,
11511    parameter_names: &[String],
11512    ancestry: &ParentIndex<'tree>,
11513) -> CppTemplateExpression {
11514    let text = normalize_cpp_whitespace(node_text(node, source));
11515    CppTemplateExpression {
11516        text,
11517        term: cpp_template_term(node, source, parameter_names, ancestry),
11518    }
11519}
11520
11521pub fn cpp_template_term<'tree>(
11522    node: Node<'tree>,
11523    source: &str,
11524    parameter_names: &[String],
11525    ancestry: &ParentIndex<'tree>,
11526) -> CppTemplateTerm {
11527    enum Work<'tree> {
11528        Visit(Node<'tree>),
11529        Build { kind: String, child_count: usize },
11530    }
11531
11532    let mut work = vec![Work::Visit(node)];
11533    let mut terms = Vec::new();
11534    while let Some(next) = work.pop() {
11535        match next {
11536            Work::Visit(current) => {
11537                let text = normalize_cpp_whitespace(node_text(current, source));
11538                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
11539                    terms.push(CppTemplateTerm::Parameter(text));
11540                    continue;
11541                }
11542                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
11543                    let mut cursor = current.walk();
11544                    let named = current
11545                        .named_children(&mut cursor)
11546                        .filter(|child| !child.is_extra() && child.kind() != "comment")
11547                        .collect::<Vec<_>>();
11548                    if let [child] = named.as_slice() {
11549                        work.push(Work::Visit(*child));
11550                        continue;
11551                    }
11552                }
11553                if current.child_count() == 0 {
11554                    terms.push(CppTemplateTerm::Atom {
11555                        kind: if matches!(
11556                            current.kind(),
11557                            "identifier"
11558                                | "type_identifier"
11559                                | "field_identifier"
11560                                | "namespace_identifier"
11561                        ) {
11562                            "identifier".to_string()
11563                        } else {
11564                            current.kind().to_string()
11565                        },
11566                        text,
11567                    });
11568                    continue;
11569                }
11570                let children = (0..current.child_count())
11571                    .filter_map(|index| current.child(index))
11572                    .filter(|child| !child.is_extra() && child.kind() != "comment")
11573                    .collect::<Vec<_>>();
11574                work.push(Work::Build {
11575                    kind: current.kind().to_string(),
11576                    child_count: children.len(),
11577                });
11578                work.extend(children.into_iter().rev().map(Work::Visit));
11579            }
11580            Work::Build { kind, child_count } => {
11581                let children = terms.split_off(terms.len() - child_count);
11582                terms.push(CppTemplateTerm::Node { kind, children });
11583            }
11584        }
11585    }
11586    terms.pop().expect("template term traversal emits one root")
11587}
11588
11589fn cpp_template_term_leaf_is_parameter<'tree>(
11590    node: Node<'tree>,
11591    text: &str,
11592    parameter_names: &[String],
11593    ancestry: &ParentIndex<'tree>,
11594) -> bool {
11595    if !parameter_names.iter().any(|parameter| parameter == text) {
11596        return false;
11597    }
11598    !ancestry.parent(node).is_some_and(|parent| {
11599        matches!(
11600            parent.kind(),
11601            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
11602        ) && parent.child_by_field_name("scope").is_some()
11603            && parent.child_by_field_name("name") == Some(node)
11604    })
11605}
11606
11607fn enclosing_cpp_declaration_node<'tree>(
11608    mut node: Node<'tree>,
11609    ancestry: &ParentIndex<'tree>,
11610) -> Option<Node<'tree>> {
11611    loop {
11612        match node.kind() {
11613            "declaration"
11614            | "function_declaration"
11615            | "field_declaration"
11616            | "function_definition" => return Some(node),
11617            _ => node = ancestry.parent(node)?,
11618        }
11619    }
11620}
11621
11622fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
11623    let mut params = Vec::new();
11624    let mut cursor = parameters_node.walk();
11625    for child in parameters_node.children(&mut cursor) {
11626        match child.kind() {
11627            "parameter_declaration" | "optional_parameter_declaration" => {
11628                params.push(cpp_parameter_type(child, source));
11629            }
11630            "variadic_parameter_declaration" => {
11631                params.push(cpp_parameter_type(child, source));
11632            }
11633            "variadic_parameter" | "..." => params.push("...".to_string()),
11634            _ => {}
11635        }
11636    }
11637
11638    if params.is_empty() {
11639        "()".to_string()
11640    } else {
11641        format!("({})", params.join(", "))
11642    }
11643}
11644
11645fn cpp_signature_metadata<'tree>(
11646    signature: String,
11647    function_declarator: Node<'tree>,
11648    source: &str,
11649    ancestry: &ParentIndex<'tree>,
11650) -> SignatureMetadata {
11651    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
11652    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
11653    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
11654    let return_type_identity =
11655        cpp_callable_return_type_identity(function_declarator, source, ancestry);
11656    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
11657        return enrich(
11658            SignatureMetadata::new(signature, Vec::new())
11659                .with_return_type_text(return_type_text)
11660                .with_return_type_identity(return_type_identity),
11661        );
11662    };
11663    let callable_arity = cpp_callable_arity(parameters_node, source);
11664    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
11665    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
11666    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
11667    let Some(relative_start) = signature
11668        .get(search_from..)
11669        .and_then(|suffix| suffix.find(&parameter_text))
11670    else {
11671        return enrich(
11672            SignatureMetadata::new(signature, Vec::new())
11673                .with_callable_arity(callable_arity)
11674                .with_callable_parameter_types(callable_parameter_types)
11675                .with_return_type_text(return_type_text)
11676                .with_return_type_identity(return_type_identity),
11677        );
11678    };
11679    let parameters_start = search_from + relative_start;
11680    let parameters_end = parameters_start + parameter_text.len();
11681    let mut search_start = parameters_start;
11682    let parameters = cpp_parameter_label_nodes(parameters_node)
11683        .into_iter()
11684        .filter_map(|label_node| {
11685            let label = normalize_cpp_whitespace(node_text(label_node, source));
11686            if label.is_empty() || search_start > parameters_end {
11687                return None;
11688            }
11689            let haystack = signature.get(search_start..parameters_end)?;
11690            let relative_start = haystack.find(&label)?;
11691            let start_byte = search_start + relative_start;
11692            let end_byte = start_byte + label.len();
11693            search_start = end_byte;
11694            Some(ParameterMetadata::new(label, start_byte, end_byte))
11695        })
11696        .collect();
11697    enrich(
11698        SignatureMetadata::new(signature, parameters)
11699            .with_callable_arity(callable_arity)
11700            .with_callable_parameter_types(callable_parameter_types)
11701            .with_return_type_text(return_type_text)
11702            .with_return_type_identity(return_type_identity),
11703    )
11704}
11705
11706fn cpp_callable_is_structural_constructor<'tree>(
11707    function_declarator: Node<'tree>,
11708    source: &str,
11709    ancestry: &ParentIndex<'tree>,
11710) -> bool {
11711    let Some(name_node) = function_declarator
11712        .child_by_field_name("declarator")
11713        .or_else(|| function_declarator.child_by_field_name("name"))
11714        .or_else(|| last_named_child(function_declarator))
11715    else {
11716        return false;
11717    };
11718    let Some(callable_name) = direct_identifier_name(name_node, source) else {
11719        return false;
11720    };
11721
11722    let mut current = ancestry.parent(function_declarator);
11723    while let Some(ancestor) = current {
11724        let owner_name = match ancestor.kind() {
11725            "class_specifier" | "struct_specifier" | "union_specifier" => {
11726                class_like_name(ancestor, source, ancestry)
11727            }
11728            "ERROR" => malformed_class_error_owner_name(ancestor, source),
11729            _ => None,
11730        };
11731        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
11732            return true;
11733        }
11734        current = ancestry.parent(ancestor);
11735    }
11736    false
11737}
11738
11739/// Recover the owner name from the direct grammar shape retained when a later
11740/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
11741/// `ERROR` node:
11742///
11743/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
11744///
11745/// Direct-child checks keep this distinct from an unrelated nested class inside
11746/// a broader error region. The closing brace may be displaced past the error
11747/// node, so the opening body token is the available structural boundary.
11748fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
11749    if node.kind() != "ERROR" {
11750        return None;
11751    }
11752    let keyword = node.child(0)?;
11753    if !matches!(keyword.kind(), "class" | "struct" | "union") {
11754        return None;
11755    }
11756    let name_node = node.child(1)?;
11757    let name = direct_identifier_name(name_node, source)?;
11758    let has_body = (2..node.child_count())
11759        .filter_map(|index| node.child(index))
11760        .any(|child| child.kind() == "{");
11761    has_body.then_some(name)
11762}
11763
11764/// The parser-derived return type of one callable declaration.
11765///
11766/// This accepts the enclosing declaration node so consumers do not need to
11767/// duplicate the declarator-unwrapping rules before asking for the structured
11768/// identity.
11769pub fn cpp_callable_declaration_return_type_identity<'tree>(
11770    callable: Node<'tree>,
11771    source: &str,
11772    ancestry: &ParentIndex<'tree>,
11773) -> Option<StructuredTypeIdentity> {
11774    let declarator = callable
11775        .child_by_field_name("declarator")
11776        .and_then(extract_function_declarator)?;
11777    cpp_callable_return_type_identity(declarator, source, ancestry)
11778}
11779
11780pub(crate) fn cpp_callable_return_type_identity<'tree>(
11781    function_declarator: Node<'tree>,
11782    source: &str,
11783    ancestry: &ParentIndex<'tree>,
11784) -> Option<StructuredTypeIdentity> {
11785    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
11786        return None;
11787    }
11788    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
11789    if let Some((return_type, _)) =
11790        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
11791    {
11792        return cpp_structured_type_identity(return_type, source, &lexical_scope);
11793    }
11794    let mut cursor = function_declarator.walk();
11795    if let Some(trailing) = function_declarator
11796        .named_children(&mut cursor)
11797        .find(|child| child.kind() == "trailing_return_type")
11798        && let Some(type_descriptor) = trailing.named_child(0)
11799    {
11800        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
11801    }
11802
11803    let mut current = function_declarator;
11804    let mut wrappers = Vec::new();
11805    while let Some(parent) = ancestry.parent(current) {
11806        if matches!(
11807            parent.kind(),
11808            "function_definition" | "declaration" | "field_declaration"
11809        ) {
11810            let type_node = parent.child_by_field_name("type")?;
11811            if cpp_export_macro_token(node_text(type_node, source))
11812                && (0..parent.named_child_count()).any(|index| {
11813                    parent
11814                        .named_child(index)
11815                        .is_some_and(|child| child.kind() == "ERROR")
11816                })
11817            {
11818                return None;
11819            }
11820            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
11821            for wrapper in wrappers.into_iter().rev() {
11822                identity = cpp_wrap_structured_type(identity, wrapper)?;
11823            }
11824            return Some(identity);
11825        }
11826        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
11827            || (matches!(
11828                parent.kind(),
11829                "pointer_declarator"
11830                    | "reference_declarator"
11831                    | "array_declarator"
11832                    | "parenthesized_declarator"
11833            ) && parent.named_child_count() == 1
11834                && parent.named_child(0) == Some(current));
11835        if !wraps_current_declarator {
11836            return None;
11837        }
11838        match parent.kind() {
11839            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
11840            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
11841            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
11842            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
11843            _ => return None,
11844        }
11845        current = parent;
11846    }
11847    None
11848}
11849
11850fn cpp_structured_type_identity(
11851    node: Node<'_>,
11852    source: &str,
11853    lexical_scope: &[String],
11854) -> Option<StructuredTypeIdentity> {
11855    enum Work<'tree> {
11856        Visit(Node<'tree>),
11857        Wrap(CppStructuredTypeWrapper),
11858        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
11859        BuildGeneric { argument_count: usize },
11860    }
11861
11862    let mut work = vec![Work::Visit(node)];
11863    let mut values = Vec::new();
11864    let mut builder = StructuredTypeIdentityBuilder::default();
11865    while let Some(next) = work.pop() {
11866        match next {
11867            Work::Visit(current) => match current.kind() {
11868                "type_descriptor" => {
11869                    let type_node = current
11870                        .child_by_field_name("type")
11871                        .or_else(|| current.named_child(0))?;
11872                    let mut wrappers = Vec::new();
11873                    let mut cursor = current.walk();
11874                    for child in current.named_children(&mut cursor) {
11875                        if child.id() != type_node.id() {
11876                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
11877                        }
11878                    }
11879                    work.push(Work::ApplyWrappers(wrappers));
11880                    work.push(Work::Visit(type_node));
11881                }
11882                "pointer_declarator" | "abstract_pointer_declarator" => {
11883                    let child = current
11884                        .child_by_field_name("declarator")
11885                        .or_else(|| current.named_child(0))?;
11886                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
11887                    work.push(Work::Visit(child));
11888                }
11889                "reference_declarator" => {
11890                    let child = current
11891                        .child_by_field_name("declarator")
11892                        .or_else(|| current.named_child(0))?;
11893                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
11894                    work.push(Work::Visit(child));
11895                }
11896                "array_declarator" | "abstract_array_declarator" => {
11897                    let child = current
11898                        .child_by_field_name("declarator")
11899                        .or_else(|| current.named_child(0))?;
11900                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
11901                    work.push(Work::Visit(child));
11902                }
11903                "template_type" => {
11904                    let name_node = current.child_by_field_name("name")?;
11905                    let arguments = current
11906                        .child_by_field_name("arguments")
11907                        .map(|arguments_node| {
11908                            let mut cursor = arguments_node.walk();
11909                            arguments_node
11910                                .named_children(&mut cursor)
11911                                .filter(|child| !child.is_extra() && child.kind() != "comment")
11912                                .collect::<Vec<_>>()
11913                        })
11914                        .unwrap_or_default();
11915                    work.push(Work::BuildGeneric {
11916                        argument_count: arguments.len(),
11917                    });
11918                    work.extend(arguments.into_iter().rev().map(Work::Visit));
11919                    work.push(Work::Visit(name_node));
11920                }
11921                "qualified_identifier"
11922                | "scoped_identifier"
11923                | "scoped_type_identifier"
11924                | "type_identifier"
11925                | "field_identifier"
11926                | "identifier"
11927                | "namespace_identifier"
11928                | "primitive_type" => {
11929                    values.push(builder.named(cpp_structured_named_type(
11930                        current,
11931                        source,
11932                        lexical_scope,
11933                    )?)?);
11934                }
11935                _ => {
11936                    let child = current.child_by_field_name("type").or_else(|| {
11937                        (current.named_child_count() == 1)
11938                            .then(|| current.named_child(0))
11939                            .flatten()
11940                    })?;
11941                    work.push(Work::Visit(child));
11942                }
11943            },
11944            Work::Wrap(wrapper) => {
11945                let root = values.pop()?;
11946                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
11947            }
11948            Work::ApplyWrappers(wrappers) => {
11949                let mut root = values.pop()?;
11950                for wrapper in wrappers.into_iter().rev() {
11951                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
11952                }
11953                values.push(root);
11954            }
11955            Work::BuildGeneric { argument_count } => {
11956                let value_count = argument_count.checked_add(1)?;
11957                let start = values.len().checked_sub(value_count)?;
11958                let mut built = values.split_off(start);
11959                let base = built.remove(0);
11960                values.push(builder.generic(base, built)?);
11961            }
11962        }
11963    }
11964    (values.len() == 1)
11965        .then(|| values.pop())
11966        .flatten()
11967        .and_then(|root| builder.finish(root))
11968}
11969
11970fn cpp_structured_named_type(
11971    node: Node<'_>,
11972    source: &str,
11973    lexical_scope: &[String],
11974) -> Option<StructuredTypeName> {
11975    let path = cpp_structured_type_path(node, source)?;
11976    let absolute = node.child_by_field_name("scope").is_none()
11977        && node.child(0).is_some_and(|child| child.kind() == "::");
11978    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
11979}
11980
11981#[derive(Clone, Copy)]
11982enum CppStructuredTypeWrapper {
11983    Pointer,
11984    LvalueReference,
11985    RvalueReference,
11986    Array,
11987}
11988
11989fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
11990    let mut wrappers = Vec::new();
11991    let mut current = node;
11992    loop {
11993        match current.kind() {
11994            "pointer_declarator" | "abstract_pointer_declarator" => {
11995                wrappers.push(CppStructuredTypeWrapper::Pointer)
11996            }
11997            "reference_declarator" | "abstract_reference_declarator" => {
11998                wrappers.push(cpp_reference_wrapper(current)?);
11999            }
12000            "array_declarator" | "abstract_array_declarator" => {
12001                wrappers.push(CppStructuredTypeWrapper::Array)
12002            }
12003            // An initializer and a grouping parenthesis are not type
12004            // structure. Stopping at one of them dropped every wrapper an
12005            // initialized or parenthesized declarator writes, so
12006            // `std::string &alias = value;` reported the unqualified
12007            // `std::string` while `const std::string &value` as a parameter
12008            // reported the reference it shares.
12009            "init_declarator" | "parenthesized_declarator" => {}
12010            _ => break,
12011        }
12012        let Some(child) = current
12013            .child_by_field_name("declarator")
12014            .or_else(|| current.named_child(0))
12015        else {
12016            break;
12017        };
12018        current = child;
12019    }
12020    Some(wrappers)
12021}
12022
12023fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
12024    node.children(&mut node.walk())
12025        .find_map(|child| match child.kind() {
12026            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
12027            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
12028            _ => None,
12029        })
12030}
12031
12032fn cpp_wrap_structured_type(
12033    identity: StructuredTypeIdentity,
12034    wrapper: CppStructuredTypeWrapper,
12035) -> Option<StructuredTypeIdentity> {
12036    match wrapper {
12037        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
12038        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
12039        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
12040        CppStructuredTypeWrapper::Array => identity.wrap_array(),
12041    }
12042}
12043
12044fn cpp_wrap_structured_type_node(
12045    builder: &mut StructuredTypeIdentityBuilder,
12046    inner: StructuredTypeNodeId,
12047    wrapper: CppStructuredTypeWrapper,
12048) -> Option<StructuredTypeNodeId> {
12049    match wrapper {
12050        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
12051        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
12052        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
12053        CppStructuredTypeWrapper::Array => builder.array(inner),
12054    }
12055}
12056
12057/// The dot-free name components a qualified C++ name node writes, outermost
12058/// first.
12059///
12060/// The walk reads the grammar's `scope` and `name` fields rather than cutting
12061/// a rendered spelling on `::`, so `std::move` yields `["std", "move"]` and a
12062/// shape the grammar does not name yields nothing at all.
12063pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
12064    let mut path = Vec::new();
12065    let mut stack = vec![node];
12066    while let Some(current) = stack.pop() {
12067        match current.kind() {
12068            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
12069                let component = node_text(current, source).to_string();
12070                if component.is_empty() {
12071                    return None;
12072                }
12073                path.push(component);
12074            }
12075            "template_type" | "dependent_type" => {
12076                stack.push(current.child_by_field_name("name")?);
12077            }
12078            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12079                stack.push(current.child_by_field_name("name")?);
12080                if let Some(scope) = current.child_by_field_name("scope") {
12081                    stack.push(scope);
12082                }
12083            }
12084            _ => return None,
12085        }
12086    }
12087    (!path.is_empty()).then_some(path)
12088}
12089
12090fn cpp_callable_lexical_scope<'tree>(
12091    node: Node<'tree>,
12092    source: &str,
12093    ancestry: &ParentIndex<'tree>,
12094) -> Vec<String> {
12095    let mut groups = Vec::new();
12096    let mut current = ancestry.parent(node);
12097    while let Some(parent) = current {
12098        if matches!(
12099            parent.kind(),
12100            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
12101        ) && let Some(name_node) = parent.child_by_field_name("name")
12102            && let Some(components) = cpp_structured_type_path(name_node, source)
12103            && !components.is_empty()
12104        {
12105            groups.push(components);
12106        }
12107        current = ancestry.parent(parent);
12108    }
12109    groups.reverse();
12110    groups.into_iter().flatten().collect()
12111}
12112
12113fn cpp_callable_dispatch_extensibility<'tree>(
12114    function_declarator: Node<'tree>,
12115    ancestry: &ParentIndex<'tree>,
12116) -> DispatchExtensibility {
12117    let mut declaration = None;
12118    let mut current = Some(function_declarator);
12119    while let Some(node) = current {
12120        match node.kind() {
12121            "template_declaration"
12122            | "preproc_if"
12123            | "preproc_ifdef"
12124            | "preproc_else"
12125            | "preproc_elif"
12126            | "preproc_call"
12127            | "ERROR" => return DispatchExtensibility::Open,
12128            "declaration" | "field_declaration" | "function_definition" => {
12129                declaration.get_or_insert(node);
12130            }
12131            "translation_unit" => break,
12132            _ => {}
12133        }
12134        current = ancestry.parent(node);
12135    }
12136    let Some(declaration) = declaration else {
12137        return DispatchExtensibility::Open;
12138    };
12139
12140    let mut saw_virtual_boundary = false;
12141    let mut stack = vec![declaration];
12142    while let Some(node) = stack.pop() {
12143        match node.kind() {
12144            "compound_statement" | "field_declaration_list" => continue,
12145            "final" | "final_specifier" => return DispatchExtensibility::Closed,
12146            "virtual"
12147            | "override"
12148            | "virtual_specifier"
12149            | "pure_virtual_clause"
12150            | "template_parameter_list"
12151            | "template_method"
12152            | "template_function"
12153            | "ERROR" => saw_virtual_boundary = true,
12154            _ => {}
12155        }
12156        let mut cursor = node.walk();
12157        stack.extend(node.children(&mut cursor));
12158    }
12159
12160    if saw_virtual_boundary {
12161        DispatchExtensibility::Open
12162    } else {
12163        DispatchExtensibility::Closed
12164    }
12165}
12166
12167fn cpp_callable_linkage<'tree>(
12168    declaration: Node<'tree>,
12169    source: &str,
12170    ancestry: &ParentIndex<'tree>,
12171) -> CallableLinkage {
12172    let mut enclosed_by_class = false;
12173    let mut current = ancestry.parent(declaration);
12174    while let Some(node) = current {
12175        if node.kind() == "namespace_definition"
12176            && node
12177                .child_by_field_name("name")
12178                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12179        {
12180            return CallableLinkage::Internal;
12181        }
12182        if matches!(
12183            node.kind(),
12184            "class_specifier" | "struct_specifier" | "union_specifier"
12185        ) {
12186            if node
12187                .child_by_field_name("name")
12188                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12189            {
12190                return CallableLinkage::Internal;
12191            }
12192            enclosed_by_class = true;
12193        }
12194        // An export macro between `class` and the class name can make
12195        // tree-sitter parse the entire class as a function definition. The
12196        // structured recovery proves that this container is a named class
12197        // scope, not a local callable scope.
12198        if node.kind() == "lambda_expression"
12199            || node.kind() == "function_definition"
12200                && !is_recovered_exported_class_container(node, source)
12201        {
12202            return CallableLinkage::Internal;
12203        }
12204        current = ancestry.parent(node);
12205    }
12206
12207    if enclosed_by_class {
12208        return CallableLinkage::External;
12209    }
12210
12211    let mut cursor = declaration.walk();
12212    if declaration.named_children(&mut cursor).any(|child| {
12213        child.kind() == "storage_class_specifier"
12214            && normalize_cpp_whitespace(node_text(child, source)) == "static"
12215    }) {
12216        CallableLinkage::Internal
12217    } else {
12218        CallableLinkage::External
12219    }
12220}
12221
12222fn cpp_callable_return_type_text<'tree>(
12223    function_declarator: Node<'tree>,
12224    source: &str,
12225    ancestry: &ParentIndex<'tree>,
12226) -> Option<String> {
12227    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
12228        return None;
12229    }
12230    if let Some((return_type, _)) =
12231        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
12232    {
12233        let text = normalize_cpp_whitespace(node_text(return_type, source));
12234        return (!text.is_empty()).then_some(text);
12235    }
12236    let mut cursor = function_declarator.walk();
12237    if let Some(trailing) = function_declarator
12238        .named_children(&mut cursor)
12239        .find(|child| child.kind() == "trailing_return_type")
12240        && let Some(type_descriptor) = trailing.named_child(0)
12241    {
12242        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
12243        if !text.is_empty() {
12244            return Some(text);
12245        }
12246    }
12247
12248    let mut current = function_declarator;
12249    let mut indirection = String::new();
12250    while let Some(parent) = ancestry.parent(current) {
12251        if matches!(
12252            parent.kind(),
12253            "function_definition" | "declaration" | "field_declaration"
12254        ) {
12255            let type_node = parent.child_by_field_name("type")?;
12256            if cpp_export_macro_token(node_text(type_node, source))
12257                && (0..parent.named_child_count()).any(|index| {
12258                    parent
12259                        .named_child(index)
12260                        .is_some_and(|child| child.kind() == "ERROR")
12261                })
12262            {
12263                // Export/decorator macros commonly occupy the grammar's `type`
12264                // field and leave the semantic return type in an ERROR sibling.
12265                // Do not persist the macro token as a return type. The malformed
12266                // declaration does not carry enough structured evidence here.
12267                return None;
12268            }
12269            let base = normalize_cpp_whitespace(node_text(type_node, source));
12270            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
12271        }
12272        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
12273            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
12274                && parent.named_child_count() == 1
12275                && parent.named_child(0) == Some(current));
12276        if wraps_current_declarator {
12277            match parent.kind() {
12278                "pointer_declarator" => indirection.push('*'),
12279                "reference_declarator" => {
12280                    let reference = parent
12281                        .children(&mut parent.walk())
12282                        .find(|child| !child.is_named())
12283                        .map(|child| node_text(child, source))
12284                        .unwrap_or("&");
12285                    indirection.push_str(reference);
12286                }
12287                "init_declarator" | "parenthesized_declarator" => {}
12288                _ => return None,
12289            }
12290            current = parent;
12291            continue;
12292        }
12293        return None;
12294    }
12295    None
12296}
12297
12298fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
12299    let mut required = 0;
12300    let mut total = 0;
12301    let mut repeated = false;
12302    let mut cursor = parameters_node.walk();
12303    for child in parameters_node.children(&mut cursor) {
12304        match child.kind() {
12305            "parameter_declaration" => {
12306                if cpp_parameter_is_explicit_object(child, source) {
12307                    continue;
12308                }
12309                if child.child_by_field_name("declarator").is_none()
12310                    && child
12311                        .child_by_field_name("type")
12312                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
12313                {
12314                    continue;
12315                }
12316                required += 1;
12317                total += 1;
12318            }
12319            "optional_parameter_declaration" => total += 1,
12320            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12321                repeated = true;
12322            }
12323            _ => {}
12324        }
12325    }
12326    CallableArity::new(required, total, repeated)
12327}
12328
12329fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
12330    parameter
12331        .child_by_field_name("type")
12332        .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
12333        .and_then(|type_node| type_node.child_by_field_name("constraint"))
12334        .is_some_and(|constraint| {
12335            constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
12336        })
12337}
12338
12339/// One entry of a callable's invocation parameter list.
12340///
12341/// The list excludes an explicit object parameter and a lone `void`, so its
12342/// length is the callable's invocation arity. Every derivation of a parameter
12343/// type - the rendered spelling used for overload discrimination and the
12344/// structured identity used by dependency-pack production - starts from this
12345/// same sequence, so the two can never disagree about which parameters exist.
12346#[derive(Clone, Copy)]
12347enum CppParameterSlot<'tree> {
12348    Declared(Node<'tree>),
12349    Ellipsis,
12350}
12351
12352fn cpp_callable_parameter_slots<'tree>(
12353    parameters_node: Node<'tree>,
12354    source: &str,
12355) -> Vec<CppParameterSlot<'tree>> {
12356    let mut slots = Vec::new();
12357    let mut cursor = parameters_node.walk();
12358    for parameter in parameters_node.children(&mut cursor) {
12359        match parameter.kind() {
12360            "parameter_declaration" | "optional_parameter_declaration" => {
12361                if cpp_parameter_is_explicit_object(parameter, source)
12362                    || (parameter.child_by_field_name("declarator").is_none()
12363                        && parameter
12364                            .child_by_field_name("type")
12365                            .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
12366                {
12367                    continue;
12368                }
12369                slots.push(CppParameterSlot::Declared(parameter));
12370            }
12371            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12372                slots.push(CppParameterSlot::Ellipsis);
12373            }
12374            _ => {}
12375        }
12376    }
12377    slots
12378}
12379
12380fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
12381    cpp_callable_parameter_slots(parameters_node, source)
12382        .into_iter()
12383        .map(|slot| match slot {
12384            CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
12385            CppParameterSlot::Ellipsis => "...".to_string(),
12386        })
12387        .collect()
12388}
12389
12390/// One callable parameter's parser-derived type.
12391///
12392/// A rendered spelling such as `const T&` is a source text, not a type name. A
12393/// consumer that must publish a type into a structured model - a semantic-pack
12394/// type reference, for example - reads this instead.
12395#[derive(Debug, Clone, PartialEq, Eq)]
12396pub enum CppParameterType {
12397    /// The written type reduced to a structured identity. C++ cv-qualifiers
12398    /// have no place in that model and are not represented.
12399    Structured(StructuredTypeIdentity),
12400    /// A `...` pack, which declares no parameter type at all.
12401    Ellipsis,
12402    /// A written type with no structured reduction, such as a macro-obscured,
12403    /// `decltype`-computed, or function-pointer parameter.
12404    Unstructured,
12405}
12406
12407/// The structured type of each invocation parameter, in declaration order.
12408///
12409/// The result is index-parallel with the rendered
12410/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
12411/// callable.
12412pub fn cpp_callable_parameter_type_identities<'tree>(
12413    function_declarator: Node<'tree>,
12414    source: &str,
12415    ancestry: &ParentIndex<'tree>,
12416) -> Vec<CppParameterType> {
12417    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12418        return Vec::new();
12419    };
12420    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12421    cpp_callable_parameter_slots(parameters_node, source)
12422        .into_iter()
12423        .map(|slot| match slot {
12424            CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
12425            CppParameterSlot::Declared(parameter) => {
12426                cpp_parameter_type_identity(parameter, source, &lexical_scope)
12427                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
12428            }
12429        })
12430        .collect()
12431}
12432
12433/// The parser-derived type of one declared object or parameter.
12434///
12435/// The declaration owns the base `type` field while the individual declarator
12436/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
12437/// lets callers distinguish several declarators in one declaration without
12438/// reparsing a rendered signature.
12439pub fn cpp_declaration_type_identity<'tree>(
12440    declaration: Node<'tree>,
12441    declarator: Node<'tree>,
12442    source: &str,
12443    ancestry: &ParentIndex<'tree>,
12444) -> Option<StructuredTypeIdentity> {
12445    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
12446    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
12447}
12448
12449fn cpp_parameter_type_identity(
12450    parameter: Node<'_>,
12451    source: &str,
12452    lexical_scope: &[String],
12453) -> Option<StructuredTypeIdentity> {
12454    cpp_declaration_type_identity_in_scope(
12455        parameter,
12456        cpp_parameter_declarator(parameter),
12457        source,
12458        lexical_scope,
12459    )
12460}
12461
12462fn cpp_declaration_type_identity_in_scope(
12463    declaration: Node<'_>,
12464    declarator: Option<Node<'_>>,
12465    source: &str,
12466    lexical_scope: &[String],
12467) -> Option<StructuredTypeIdentity> {
12468    let type_node = declaration.child_by_field_name("type")?;
12469    let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
12470    if let Some(declarator) = declarator {
12471        for wrapper in cpp_structured_declarator_wrappers(declarator)?
12472            .into_iter()
12473            .rev()
12474        {
12475            identity = cpp_wrap_structured_type(identity, wrapper)?;
12476        }
12477    }
12478    Some(identity)
12479}
12480
12481/// One callable parameter's comparable shape.
12482///
12483/// [`CppParameterType`] above answers "which type is written here" for a
12484/// structured model and deliberately records no cv-qualifiers, so it reports
12485/// the same value for `f(char *)` and `f(const char *)`. Deciding whether two
12486/// callable declarations declare one function needs the opposite trade: every
12487/// cv-qualifier that C++ counts as part of the parameter type must survive,
12488/// while the two declarations may spell the same type through different
12489/// qualifications. This slot carries that comparand.
12490///
12491/// The result is index-parallel with [`cpp_callable_parameter_type_identities`]
12492/// and with the rendered parameter spellings of the same callable.
12493#[derive(Debug, Clone, PartialEq, Eq)]
12494pub enum CppComparableSlot {
12495    /// A declared parameter reduced to its comparable shape.
12496    Shape(CppComparableParameter),
12497    /// A `...` pack, which declares no parameter type at all.
12498    Ellipsis,
12499    /// A parameter with no comparable reduction, such as a macro-obscured,
12500    /// `decltype`-computed, or function-pointer parameter.
12501    Unstructured,
12502}
12503
12504/// A parameter type as a flat arena of nodes plus a root index.
12505///
12506/// The arena carries the same rationale as [`StructuredTypeIdentity`]: source
12507/// can nest types very deeply, and cloning, comparing or dropping the value
12508/// must not consume the Rust call stack. Nodes are appended in post-order, so
12509/// every child index is smaller than its parent's and the last appended node is
12510/// the root.
12511///
12512/// That post-order append is also what makes the derived `PartialEq` a correct
12513/// structural equality: the builder below is deterministic, so one type shape
12514/// has exactly one arena layout no matter which spelling produced it. Two
12515/// shapes are equal as values iff they are equal as type trees.
12516#[derive(Debug, Clone, PartialEq, Eq)]
12517pub struct CppComparableParameter {
12518    nodes: Vec<CppComparableNode>,
12519    root: usize,
12520}
12521
12522/// One node of a [`CppComparableParameter`] arena.
12523///
12524/// `Reference` and `Array` carry no qualifiers because the grammar writes none
12525/// on them: a reference cannot be cv-qualified in C++, and an array's
12526/// qualifiers belong to its element type. A cv-qualifier written on a generic
12527/// type (`const std::vector<int>`) is recorded on the generic's base leaf,
12528/// which is the only Named node the whole spelling produces.
12529#[derive(Debug, Clone, PartialEq, Eq)]
12530pub enum CppComparableNode {
12531    Named {
12532        name: StructuredTypeName,
12533        primitive: bool,
12534        konst: bool,
12535        volatil: bool,
12536    },
12537    Pointer {
12538        inner: usize,
12539        konst: bool,
12540        volatil: bool,
12541    },
12542    Reference {
12543        inner: usize,
12544    },
12545    Array {
12546        inner: usize,
12547    },
12548    Generic {
12549        base: usize,
12550        arguments: Vec<usize>,
12551    },
12552}
12553
12554impl CppComparableParameter {
12555    pub fn root(&self) -> usize {
12556        self.root
12557    }
12558
12559    pub fn node(&self, index: usize) -> &CppComparableNode {
12560        &self.nodes[index]
12561    }
12562
12563    /// Apply the [dcl.fct]/5 parameter-type adjustments, which hold at the
12564    /// parameter's top level only.
12565    ///
12566    /// A top-level cv-qualifier is discarded, so `f(const int)` and `f(int)`
12567    /// declare one function, and a top-level array type becomes a pointer to
12568    /// its element type, so `f(int[3])` and `f(int *)` do too. The outermost
12569    /// type constructor is this arena's root, which is why both adjustments
12570    /// are one match on it: cv on an inner pointer level, on a pointee, or on
12571    /// an array element keeps distinguishing the type, and an array behind a
12572    /// pointer or reference is not a top-level array.
12573    fn adjust_parameter_top_level(&mut self) {
12574        let root = self.root;
12575        match &mut self.nodes[root] {
12576            CppComparableNode::Named { konst, volatil, .. }
12577            | CppComparableNode::Pointer { konst, volatil, .. } => {
12578                *konst = false;
12579                *volatil = false;
12580            }
12581            CppComparableNode::Array { inner } => {
12582                let inner = *inner;
12583                self.nodes[root] = CppComparableNode::Pointer {
12584                    inner,
12585                    konst: false,
12586                    volatil: false,
12587                };
12588            }
12589            CppComparableNode::Generic { base, .. } => {
12590                let base = *base;
12591                let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
12592                    unreachable!("a comparable generic's base is always a named leaf");
12593                };
12594                *konst = false;
12595                *volatil = false;
12596            }
12597            CppComparableNode::Reference { .. } => {}
12598        }
12599    }
12600}
12601
12602/// The comparable shape of each invocation parameter, in declaration order.
12603///
12604/// The result is index-parallel with
12605/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
12606/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
12607/// must treat as evidence of nothing rather than as agreement.
12608pub fn cpp_comparable_parameter_shapes<'tree>(
12609    function_declarator: Node<'tree>,
12610    source: &str,
12611    ancestry: &ParentIndex<'tree>,
12612) -> Vec<CppComparableSlot> {
12613    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12614        return Vec::new();
12615    };
12616    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12617    cpp_callable_parameter_slots(parameters_node, source)
12618        .into_iter()
12619        .map(|slot| match slot {
12620            CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
12621            CppParameterSlot::Declared(parameter) => {
12622                cpp_comparable_parameter(parameter, source, &lexical_scope)
12623                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
12624            }
12625        })
12626        .collect()
12627}
12628
12629fn cpp_comparable_parameter(
12630    parameter: Node<'_>,
12631    source: &str,
12632    lexical_scope: &[String],
12633) -> Option<CppComparableParameter> {
12634    let type_node = parameter.child_by_field_name("type")?;
12635    let levels = match cpp_parameter_declarator(parameter) {
12636        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12637        None => Vec::new(),
12638    };
12639    let mut shape = cpp_comparable_type_shape(
12640        type_node,
12641        cpp_cv_qualifiers(parameter, source),
12642        levels,
12643        source,
12644        lexical_scope,
12645    )?;
12646    shape.adjust_parameter_top_level();
12647    Some(shape)
12648}
12649
12650/// The `const` and `volatile` qualifiers written as direct named children of
12651/// `node`.
12652///
12653/// The grammar exposes `type_qualifier` as a non-field named child in exactly
12654/// the three places a parameter's qualifiers can be written: on the
12655/// `parameter_declaration` itself (the base type), on a `type_descriptor`
12656/// (inside a template argument list), and on each `pointer_declarator` level
12657/// (the pointer object). Every other qualifier the grammar admits - `restrict`
12658/// and friends - takes no part in C++ type identity, the same filter
12659/// `cpp_parameter_type` applies to the rendered spelling (#1827).
12660fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
12661    let mut qualifiers = CppCvQualifiers::default();
12662    let mut cursor = node.walk();
12663    for child in node.named_children(&mut cursor) {
12664        if child.kind() != "type_qualifier" {
12665            continue;
12666        }
12667        match node_text(child, source) {
12668            "const" => qualifiers.konst = true,
12669            "volatile" => qualifiers.volatil = true,
12670            _ => {}
12671        }
12672    }
12673    qualifiers
12674}
12675
12676#[derive(Clone, Copy, Default)]
12677struct CppCvQualifiers {
12678    konst: bool,
12679    volatil: bool,
12680}
12681
12682impl CppCvQualifiers {
12683    fn union(self, other: Self) -> Self {
12684        Self {
12685            konst: self.konst || other.konst,
12686            volatil: self.volatil || other.volatil,
12687        }
12688    }
12689}
12690
12691/// One pointer, reference or array level a declarator chain adds.
12692#[derive(Clone, Copy)]
12693enum CppComparableLevel {
12694    Pointer { konst: bool, volatil: bool },
12695    Reference,
12696    Array,
12697}
12698
12699/// The levels `declarator` adds, outermost written level first.
12700///
12701/// C++ declarator syntax binds inside out: the level written closest to the
12702/// declared name is the outermost type constructor, and tree-sitter nests it
12703/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
12704/// applies in order to reach "array of pointer to int", and the qualifier of
12705/// `int * const *p` is read on the level it was written next to, the inner
12706/// pointer of the resulting type.
12707///
12708/// A declarator chain that names a function type - a function-pointer
12709/// parameter - has no comparable shape and reports `None`, matching the
12710/// structured identity channel.
12711fn cpp_comparable_declarator_levels(
12712    declarator: Node<'_>,
12713    source: &str,
12714) -> Option<Vec<CppComparableLevel>> {
12715    let mut levels = Vec::new();
12716    let mut current = declarator;
12717    loop {
12718        match current.kind() {
12719            "pointer_declarator" | "abstract_pointer_declarator" => {
12720                let qualifiers = cpp_cv_qualifiers(current, source);
12721                levels.push(CppComparableLevel::Pointer {
12722                    konst: qualifiers.konst,
12723                    volatil: qualifiers.volatil,
12724                });
12725            }
12726            "reference_declarator" | "abstract_reference_declarator" => {
12727                levels.push(CppComparableLevel::Reference);
12728            }
12729            "array_declarator" | "abstract_array_declarator" => {
12730                levels.push(CppComparableLevel::Array);
12731            }
12732            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
12733            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
12734            _ => return None,
12735        }
12736        let Some(next) = cpp_nested_declarator(current) else {
12737            return Some(levels);
12738        };
12739        current = next;
12740    }
12741}
12742
12743/// Reduce one written type to a comparable arena.
12744///
12745/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
12746/// two additions: each visited type node carries the cv-qualifiers written on
12747/// it, and declarator levels arrive as a prepared list rather than being
12748/// rediscovered inside the walk.
12749fn cpp_comparable_type_shape(
12750    type_node: Node<'_>,
12751    qualifiers: CppCvQualifiers,
12752    levels: Vec<CppComparableLevel>,
12753    source: &str,
12754    lexical_scope: &[String],
12755) -> Option<CppComparableParameter> {
12756    enum Work<'tree> {
12757        Visit {
12758            node: Node<'tree>,
12759            qualifiers: CppCvQualifiers,
12760        },
12761        ApplyLevels(Vec<CppComparableLevel>),
12762        BuildGeneric {
12763            argument_count: usize,
12764        },
12765    }
12766
12767    let mut nodes: Vec<CppComparableNode> = Vec::new();
12768    let mut values: Vec<usize> = Vec::new();
12769    let mut work = vec![
12770        Work::ApplyLevels(levels),
12771        Work::Visit {
12772            node: type_node,
12773            qualifiers,
12774        },
12775    ];
12776    while let Some(next) = work.pop() {
12777        match next {
12778            Work::Visit { node, qualifiers } => match node.kind() {
12779                "type_descriptor" => {
12780                    let inner_type = node
12781                        .child_by_field_name("type")
12782                        .or_else(|| node.named_child(0))?;
12783                    let mut cursor = node.walk();
12784                    let declarator = node.child_by_field_name("declarator").or_else(|| {
12785                        node.named_children(&mut cursor).find(|child| {
12786                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
12787                        })
12788                    });
12789                    let levels = match declarator {
12790                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12791                        None => Vec::new(),
12792                    };
12793                    work.push(Work::ApplyLevels(levels));
12794                    work.push(Work::Visit {
12795                        node: inner_type,
12796                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
12797                    });
12798                }
12799                "sized_type_specifier" => {
12800                    // `unsigned char` is one primitive type whose components are
12801                    // partly unnamed tokens, so the whole specifier is its own
12802                    // name component. Reducing it to the `type` child would make
12803                    // `f(unsigned char)` and `f(char)` compare equal.
12804                    let name = StructuredTypeName::new(
12805                        vec![normalize_cpp_whitespace(node_text(node, source))],
12806                        lexical_scope.to_vec(),
12807                        false,
12808                    )?;
12809                    values.push(cpp_push_comparable_node(
12810                        &mut nodes,
12811                        CppComparableNode::Named {
12812                            name,
12813                            primitive: true,
12814                            konst: qualifiers.konst,
12815                            volatil: qualifiers.volatil,
12816                        },
12817                    ));
12818                }
12819                "qualified_identifier"
12820                | "scoped_identifier"
12821                | "scoped_type_identifier"
12822                | "type_identifier"
12823                | "field_identifier"
12824                | "identifier"
12825                | "namespace_identifier"
12826                | "primitive_type"
12827                | "template_type" => {
12828                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
12829                    values.push(cpp_push_comparable_node(
12830                        &mut nodes,
12831                        CppComparableNode::Named {
12832                            name,
12833                            primitive: node.kind() == "primitive_type",
12834                            konst: qualifiers.konst,
12835                            volatil: qualifiers.volatil,
12836                        },
12837                    ));
12838                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
12839                        let mut cursor = arguments_node.walk();
12840                        let arguments = arguments_node
12841                            .named_children(&mut cursor)
12842                            .filter(|child| !child.is_extra() && child.kind() != "comment")
12843                            .collect::<Vec<_>>();
12844                        work.push(Work::BuildGeneric {
12845                            argument_count: arguments.len(),
12846                        });
12847                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
12848                            node: argument,
12849                            qualifiers: CppCvQualifiers::default(),
12850                        }));
12851                    }
12852                }
12853                _ => {
12854                    let inner = node.child_by_field_name("type").or_else(|| {
12855                        (node.named_child_count() == 1)
12856                            .then(|| node.named_child(0))
12857                            .flatten()
12858                    })?;
12859                    work.push(Work::Visit {
12860                        node: inner,
12861                        qualifiers,
12862                    });
12863                }
12864            },
12865            Work::ApplyLevels(levels) => {
12866                let mut root = values.pop()?;
12867                for level in levels {
12868                    let node = match level {
12869                        CppComparableLevel::Pointer { konst, volatil } => {
12870                            CppComparableNode::Pointer {
12871                                inner: root,
12872                                konst,
12873                                volatil,
12874                            }
12875                        }
12876                        CppComparableLevel::Reference => {
12877                            CppComparableNode::Reference { inner: root }
12878                        }
12879                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
12880                    };
12881                    root = cpp_push_comparable_node(&mut nodes, node);
12882                }
12883                values.push(root);
12884            }
12885            Work::BuildGeneric { argument_count } => {
12886                let value_count = argument_count.checked_add(1)?;
12887                let start = values.len().checked_sub(value_count)?;
12888                let mut built = values.split_off(start);
12889                let base = built.remove(0);
12890                values.push(cpp_push_comparable_node(
12891                    &mut nodes,
12892                    CppComparableNode::Generic {
12893                        base,
12894                        arguments: built,
12895                    },
12896                ));
12897            }
12898        }
12899    }
12900    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
12901    debug_assert_eq!(
12902        root,
12903        nodes.len().saturating_sub(1),
12904        "comparable nodes are appended in post-order, so the root is the last one"
12905    );
12906    Some(CppComparableParameter { nodes, root })
12907}
12908
12909fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
12910    nodes.push(node);
12911    nodes.len() - 1
12912}
12913
12914/// The template argument list of the name `node` terminates in, if any.
12915///
12916/// `std::vector<int>` writes its arguments on the `name` of a qualified
12917/// identifier, so a walk that stopped at the qualified node would reduce
12918/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
12919fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
12920    let mut current = node;
12921    loop {
12922        match current.kind() {
12923            "template_type" => return current.child_by_field_name("arguments"),
12924            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12925                current = current.child_by_field_name("name")?;
12926            }
12927            _ => return None,
12928        }
12929    }
12930}
12931
12932/// The callable declarator of the declaration that covers `start_byte`.
12933///
12934/// A consumer that holds a declaration's recorded byte position rather than its
12935/// syntax node - external header extraction, for instance - uses this to reach
12936/// the same `function_declarator` the declaration walk read.
12937pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
12938    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
12939    loop {
12940        if matches!(
12941            current.kind(),
12942            "declaration" | "field_declaration" | "function_definition"
12943        ) && let Some(declarator) = current
12944            .child_by_field_name("declarator")
12945            .and_then(extract_function_declarator)
12946        {
12947            return Some(declarator);
12948        }
12949        current = current.parent()?;
12950    }
12951}
12952
12953fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
12954    let mut labels = Vec::new();
12955    let mut cursor = parameters_node.walk();
12956    for child in parameters_node.children(&mut cursor) {
12957        match child.kind() {
12958            "parameter_declaration" | "optional_parameter_declaration" => {
12959                if let Some(name_node) = child
12960                    .child_by_field_name("declarator")
12961                    .and_then(cpp_declarator_label_node)
12962                {
12963                    labels.push(name_node);
12964                } else {
12965                    labels.push(child);
12966                }
12967            }
12968            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12969                labels.push(child);
12970            }
12971            _ => {}
12972        }
12973    }
12974    labels
12975}
12976
12977fn cpp_signature_search_start<'tree>(
12978    signature: &str,
12979    function_declarator: Node<'tree>,
12980    source: &str,
12981    ancestry: &ParentIndex<'tree>,
12982) -> usize {
12983    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
12984        return 0;
12985    };
12986    let raw = node_text(enclosing, source);
12987    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
12988    let offset = function_declarator
12989        .start_byte()
12990        .saturating_sub(enclosing.start_byte())
12991        .saturating_sub(leading_trim_bytes);
12992    offset.min(signature.len())
12993}
12994
12995fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
12996    match node.kind() {
12997        "identifier" | "field_identifier" => Some(node),
12998        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
12999            .child_by_field_name("declarator")
13000            .or_else(|| last_named_child(node))
13001            .and_then(cpp_declarator_label_node),
13002        "array_declarator" => node
13003            .child_by_field_name("declarator")
13004            .and_then(cpp_declarator_label_node),
13005        "function_declarator" => node
13006            .child_by_field_name("declarator")
13007            .or_else(|| node.child_by_field_name("name"))
13008            .or_else(|| last_named_child(node))
13009            .and_then(cpp_declarator_label_node),
13010        _ => None,
13011    }
13012}
13013
13014fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
13015    let base_type = parameter
13016        .child_by_field_name("type")
13017        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
13018        .unwrap_or_default();
13019    let declarator = cpp_parameter_declarator(parameter);
13020    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
13021    // are discarded, so `f(const int)` and `f(int)` declare one function. A
13022    // qualifier written next to the parameter's type is only top-level when
13023    // the declarator adds no indirection; behind a pointer, reference or array
13024    // declarator the same qualifier belongs to the pointee, referent or
13025    // element and keeps distinguishing the type (#1827).
13026    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
13027    let mut cursor = parameter.walk();
13028    let qualifiers = parameter
13029        .named_children(&mut cursor)
13030        .filter(|child| child.kind() == "type_qualifier")
13031        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13032        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
13033        .collect::<Vec<_>>()
13034        .join(" ");
13035    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
13036        (true, _) => base_type,
13037        (_, true) => qualifiers,
13038        (false, false) => format!("{qualifiers} {base_type}"),
13039    };
13040    let declarator_suffix = declarator
13041        .map(|node| cpp_declarator_suffix_without_name(node, source))
13042        .unwrap_or_default();
13043
13044    let combined = if type_text.is_empty() {
13045        declarator_suffix
13046    } else if declarator_suffix.is_empty() {
13047        type_text
13048    } else {
13049        format!("{type_text} {declarator_suffix}")
13050    };
13051    normalize_cpp_type_text(&combined)
13052}
13053
13054fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
13055    parameter.child_by_field_name("declarator").or_else(|| {
13056        // Some unnamed prototype parameters expose their abstract declarator
13057        // as a direct named child without the grammar's `declarator` field.
13058        // Recover only the structured abstract-declarator node; the parameter's
13059        // type and qualifiers are distinct children and must not be guessed from
13060        // source text.
13061        let mut cursor = parameter.walk();
13062        parameter
13063            .named_children(&mut cursor)
13064            .find(|child| is_cpp_abstract_declarator(child.kind()))
13065    })
13066}
13067
13068/// Whether a parameter's declarator chain adds indirection - a pointer,
13069/// reference, array or function declarator - to the parameter's written type.
13070pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
13071    let mut current = Some(declarator);
13072    while let Some(node) = current {
13073        if matches!(
13074            node.kind(),
13075            "pointer_declarator"
13076                | "abstract_pointer_declarator"
13077                | "reference_declarator"
13078                | "abstract_reference_declarator"
13079                | "array_declarator"
13080                | "abstract_array_declarator"
13081                | "function_declarator"
13082                | "abstract_function_declarator"
13083        ) {
13084            return true;
13085        }
13086        current = cpp_nested_declarator(node);
13087    }
13088    false
13089}
13090
13091fn is_cpp_abstract_declarator(kind: &str) -> bool {
13092    matches!(
13093        kind,
13094        "abstract_pointer_declarator"
13095            | "abstract_reference_declarator"
13096            | "abstract_array_declarator"
13097            | "abstract_function_declarator"
13098            | "abstract_parenthesized_declarator"
13099    )
13100}
13101
13102fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
13103    node.child_by_field_name("declarator").or_else(|| {
13104        if is_cpp_abstract_declarator(node.kind()) {
13105            let mut cursor = node.walk();
13106            node.named_children(&mut cursor)
13107                .find(|child| is_cpp_abstract_declarator(child.kind()))
13108        } else {
13109            // Named declarators historically use their last named child when
13110            // tree-sitter omits the field. Keep that broad fallback for
13111            // attributed, variadic, and recovered named shapes.
13112            last_named_child(node)
13113        }
13114    })
13115}
13116
13117fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
13118    match node.kind() {
13119        "identifier" | "field_identifier" => String::new(),
13120        "pointer_declarator" | "abstract_pointer_declarator" => {
13121            let inner = cpp_nested_declarator(node)
13122                .map(|child| cpp_declarator_suffix_without_name(child, source))
13123                .unwrap_or_default();
13124            format!("*{inner}")
13125        }
13126        "reference_declarator" | "abstract_reference_declarator" => {
13127            let inner = cpp_nested_declarator(node)
13128                .map(|child| cpp_declarator_suffix_without_name(child, source))
13129                .unwrap_or_default();
13130            let reference = node
13131                .children(&mut node.walk())
13132                .find(|child| matches!(child.kind(), "&" | "&&"))
13133                .map(|child| node_text(child, source))
13134                .unwrap_or("&");
13135            format!("{reference}{inner}")
13136        }
13137        "array_declarator" | "abstract_array_declarator" => {
13138            let inner = cpp_nested_declarator(node)
13139                .map(|child| cpp_declarator_suffix_without_name(child, source))
13140                .unwrap_or_default();
13141            let size = node
13142                .child_by_field_name("size")
13143                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13144                .unwrap_or_default();
13145            format!("{inner}[{size}]")
13146        }
13147        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
13148            let inner = cpp_nested_declarator(node);
13149            inner
13150                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
13151                .unwrap_or_default()
13152        }
13153        "function_declarator" | "abstract_function_declarator" => {
13154            let inner = cpp_nested_declarator(node)
13155                .map(|child| cpp_declarator_suffix_without_name(child, source))
13156                .unwrap_or_default();
13157            let params = node
13158                .child_by_field_name("parameters")
13159                .map(|child| cpp_parameter_signature(child, source))
13160                .unwrap_or_else(|| "()".to_string());
13161            format!("{inner}{params}")
13162        }
13163        _ => {
13164            let text = normalize_cpp_whitespace(node_text(node, source));
13165            let name = extract_declarator_name(node, source);
13166            if name.is_empty() {
13167                text
13168            } else {
13169                text.replace(&name, "").trim().to_string()
13170            }
13171        }
13172    }
13173}
13174
13175fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
13176    collapse_cpp_whitespace(
13177        suffix
13178            .trim()
13179            .trim_start_matches("->")
13180            .trim_start_matches('{')
13181            .trim_end_matches(';'),
13182    )
13183}
13184
13185pub fn normalize_cpp_whitespace(value: &str) -> String {
13186    collapse_cpp_whitespace(value)
13187}
13188
13189fn normalize_cpp_type_text(value: &str) -> String {
13190    collapse_cpp_whitespace(value)
13191        .replace(", ", ",")
13192        .replace(" <", "<")
13193        .replace("< ", "<")
13194        .replace(" >", ">")
13195}
13196
13197fn collapse_cpp_whitespace(value: &str) -> String {
13198    let mut result = String::new();
13199    let mut prev_space = false;
13200    for ch in value.chars() {
13201        if ch.is_whitespace() {
13202            if !prev_space {
13203                result.push(' ');
13204            }
13205            prev_space = true;
13206        } else {
13207            result.push(ch);
13208            prev_space = false;
13209        }
13210    }
13211    result.trim().to_string()
13212}
13213
13214pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
13215    node_source_text(node, source)
13216}
13217
13218pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
13219    walk_named_tree_preorder(node, true, |node| {
13220        match node.kind() {
13221            "type_identifier" | "identifier" | "qualified_identifier" => {
13222                let text = node_text(node, source).trim();
13223                if !text.is_empty() {
13224                    identifiers.insert(text.to_string());
13225                }
13226            }
13227            _ => {}
13228        }
13229        WalkControl::Continue
13230    });
13231}
13232
13233fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
13234    node.child_by_field_name("body").or_else(|| {
13235        let mut cursor = node.walk();
13236        node.named_children(&mut cursor).find(|child| {
13237            matches!(
13238                child.kind(),
13239                "declaration_list" | "field_declaration_list" | "enumerator_list"
13240            )
13241        })
13242    })
13243}
13244
13245/// Return a class body's actual closing brace when the parser supplied one.
13246///
13247/// A malformed namespace sentinel can leave a class node carrying unrelated
13248/// parser errors even though its own class body is complete.  `has_error()` is
13249/// therefore too coarse an admission predicate for sentinel ownership.  The
13250/// body list, however, exposes the opening and closing punctuation directly;
13251/// a real (non-missing) final `}` proves that the class did not borrow the
13252/// enclosing namespace's close.  Requiring the body to end before its parent
13253/// container also rejects a recovered node whose body swallowed that outer
13254/// boundary.
13255fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
13256    if !matches!(
13257        node.kind(),
13258        "class_specifier" | "struct_specifier" | "union_specifier"
13259    ) {
13260        return None;
13261    }
13262    let body = cpp_body_node(node)?;
13263    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
13264        return None;
13265    }
13266    let open = body.child(0)?;
13267    let close = body.child(body.child_count().checked_sub(1)?)?;
13268    if open.kind() != "{"
13269        || open.is_missing()
13270        || close.kind() != "}"
13271        || close.is_missing()
13272        || close.end_byte() != body.end_byte()
13273        || body.end_byte() > node.end_byte()
13274        || node
13275            .parent()
13276            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
13277    {
13278        return None;
13279    }
13280    Some(close)
13281}
13282
13283fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
13284    if node.kind() == "namespace_definition" {
13285        return true;
13286    }
13287    let mut cursor = node.walk();
13288    node.named_children(&mut cursor)
13289        .any(cpp_contains_namespace_definition)
13290}
13291
13292struct CppNestedNamespaceSentinel<'tree> {
13293    function: Node<'tree>,
13294    body: Node<'tree>,
13295    namespace_components: Vec<String>,
13296}
13297
13298/// Owned structural recovery metadata for a namespace-sentinel region.
13299///
13300/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
13301/// instead of the namespace/class scopes that the declaration visitor restores.
13302/// The inverted usage walk has the original CST, so it needs the same ownership
13303/// evidence without borrowing parser nodes across its file scan.  Keep this
13304/// descriptor deliberately source-range based: callers can match a reference
13305/// node by containment and then resolve its structured type spelling in the
13306/// recovered class scope.
13307#[derive(Debug, Clone)]
13308pub struct CppSentinelRecoveredOwner {
13309    pub range: Range,
13310    /// Start of the qualified owner name (`btree<P>::method`).  A leading
13311    /// return type before this byte is looked up from the namespace; parameters,
13312    /// trailing returns, and the body use the member owner scope.
13313    pub owner_name_start_byte: usize,
13314    /// Number of leading components belonging to the namespace rather than
13315    /// the qualified class owner.  A leading return type is looked up before
13316    /// every owner component, not merely before the innermost class.
13317    pub namespace_component_count: usize,
13318    pub scope_components: Vec<String>,
13319}
13320
13321#[derive(Debug, Clone)]
13322pub struct CppSentinelRecoveredClass {
13323    pub namespace_range: Range,
13324    pub namespace_scope_components: Vec<String>,
13325    pub class_range: Range,
13326    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
13327    pub scope_components: Vec<String>,
13328    /// Qualified out-of-line member definitions owned by this class.  Their
13329    /// ranges may extend beyond `class_range` when the malformed sentinel
13330    /// swallowed the namespace close and left definitions as function siblings.
13331    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
13332}
13333
13334/// Resolve the lexical scope restored for a node in a malformed
13335/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
13336/// outrank class spans, which in turn outrank the surviving namespace body.
13337/// The class ancestor suffix is recovered from the original CST so nested
13338/// members keep their complete `Outer::Inner` owner chain.
13339pub fn cpp_sentinel_recovered_scope_for_node(
13340    node: Node<'_>,
13341    source: &str,
13342    recovered_classes: &[CppSentinelRecoveredClass],
13343) -> Option<Vec<String>> {
13344    let contains =
13345        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
13346    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
13347    for recovered in recovered_classes {
13348        for owner in recovered
13349            .owner_ranges
13350            .iter()
13351            .filter(|owner| contains(owner.range))
13352        {
13353            let replace = best_owner.is_none_or(|existing| {
13354                owner.range.end_byte.saturating_sub(owner.range.start_byte)
13355                    < existing
13356                        .range
13357                        .end_byte
13358                        .saturating_sub(existing.range.start_byte)
13359            });
13360            if replace {
13361                best_owner = Some(owner);
13362            }
13363        }
13364    }
13365    if let Some(owner) = best_owner {
13366        let mut scope = owner.scope_components.clone();
13367        if node.start_byte() < owner.owner_name_start_byte {
13368            scope.truncate(owner.namespace_component_count);
13369        }
13370        return Some(scope);
13371    }
13372
13373    let class = recovered_classes
13374        .iter()
13375        .filter(|recovered| contains(recovered.class_range))
13376        .min_by_key(|recovered| {
13377            recovered
13378                .class_range
13379                .end_byte
13380                .saturating_sub(recovered.class_range.start_byte)
13381        });
13382    let class_scope = class.is_some();
13383    let mut scope = if let Some(class) = class {
13384        class.scope_components.clone()
13385    } else {
13386        let namespace = recovered_classes
13387            .iter()
13388            .filter(|recovered| contains(recovered.namespace_range))
13389            .min_by_key(|recovered| {
13390                recovered
13391                    .namespace_range
13392                    .end_byte
13393                    .saturating_sub(recovered.namespace_range.start_byte)
13394            })?;
13395        let mut scope = namespace.namespace_scope_components.clone();
13396        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
13397        let common_prefix = scope
13398            .iter()
13399            .zip(&parser_namespace)
13400            .take_while(|(recovered, parser)| recovered == parser)
13401            .count();
13402        scope.extend(parser_namespace.into_iter().skip(common_prefix));
13403        scope
13404    };
13405    if class_scope {
13406        let mut ancestor_components = Vec::new();
13407        let mut ancestor = node.parent();
13408        while let Some(current) = ancestor {
13409            if matches!(
13410                current.kind(),
13411                "class_specifier" | "struct_specifier" | "union_specifier"
13412            ) && let Some(name) = current.child_by_field_name("name")
13413                && let Some(name_components) = cpp_name_components(name, source)
13414            {
13415                ancestor_components.push(
13416                    name_components
13417                        .into_iter()
13418                        .map(|component| component.name)
13419                        .collect::<Vec<_>>(),
13420                );
13421            }
13422            ancestor = current.parent();
13423        }
13424        ancestor_components.reverse();
13425        let base_len = scope.len();
13426        for component in ancestor_components.into_iter().flatten() {
13427            if scope.len() >= base_len && scope.last() == Some(&component) {
13428                continue;
13429            }
13430            scope.push(component);
13431        }
13432    }
13433    Some(scope)
13434}
13435
13436struct CppSentinelFragmentedClassTail<'tree> {
13437    class_node: Node<'tree>,
13438    template_node: Option<Node<'tree>>,
13439    name: String,
13440    raw_supertypes: Option<Vec<String>>,
13441    fragmented: FragmentedExportBody,
13442    consumed_start: usize,
13443}
13444
13445struct CppSentinelFragmentedClassErrorPrefix<'tree> {
13446    name: String,
13447    open: Node<'tree>,
13448    raw_supertypes: Option<Vec<String>>,
13449}
13450
13451struct CppSentinelDirectBodyClassRegion {
13452    namespace_components: Vec<String>,
13453    class_start: usize,
13454    class_start_line: usize,
13455    class_close_end: usize,
13456    class_close_line: usize,
13457    name: String,
13458}
13459
13460fn cpp_sentinel_body_class_candidate<'tree>(
13461    child: Node<'tree>,
13462) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13463    if matches!(
13464        child.kind(),
13465        "class_specifier" | "struct_specifier" | "union_specifier"
13466    ) {
13467        return Some((child, None));
13468    }
13469    if child.kind() != "template_declaration" {
13470        if child.kind() == "declaration" {
13471            return Some((first_class_like_child(child)?, None));
13472        }
13473        return None;
13474    }
13475    let mut cursor = child.walk();
13476    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
13477        if matches!(
13478            candidate.kind(),
13479            "class_specifier" | "struct_specifier" | "union_specifier"
13480        ) {
13481            Some(candidate)
13482        } else if candidate.kind() == "declaration" {
13483            first_class_like_child(candidate)
13484        } else {
13485            None
13486        }
13487    })?;
13488    Some((class_node, Some(child)))
13489}
13490
13491/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
13492/// namespace-sentinel body when a later member macro ends the bogus sentinel
13493/// function before the real class close. The anonymous class/open tokens and
13494/// direct identifier are the structural proof; a retained direct close would
13495/// be an ordinary malformed class rather than the fragmented tail handled here.
13496fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
13497    node: Node<'tree>,
13498    source: &str,
13499) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
13500    let name = malformed_class_error_owner_name(node, source)?;
13501    let mut cursor = node.walk();
13502    let children = node.children(&mut cursor).collect::<Vec<_>>();
13503    let keyword = children.first()?;
13504    let open_index = children.iter().position(|child| child.kind() == "{")?;
13505    if children[open_index + 1..]
13506        .iter()
13507        .any(|child| child.kind() == "}")
13508    {
13509        return None;
13510    }
13511    let raw_supertypes =
13512        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
13513    Some(CppSentinelFragmentedClassErrorPrefix {
13514        name,
13515        open: children[open_index],
13516        raw_supertypes,
13517    })
13518}
13519
13520fn cpp_sentinel_direct_body_class_candidate<'tree>(
13521    child: Node<'tree>,
13522) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13523    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
13524        return Some(candidate);
13525    }
13526    if child.kind() != "template_declaration" {
13527        return None;
13528    }
13529    let mut cursor = child.walk();
13530    let wrapper = child
13531        .named_children(&mut cursor)
13532        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
13533    Some((first_class_like_child(wrapper)?, Some(child)))
13534}
13535
13536fn cpp_sentinel_direct_namespace_components(
13537    function: Node<'_>,
13538    body: Node<'_>,
13539    source: &str,
13540) -> Option<Vec<String>> {
13541    let mut cursor = function.walk();
13542    let children = function
13543        .named_children(&mut cursor)
13544        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
13545        .collect::<Vec<_>>();
13546    let sentinel_index = children.iter().rposition(|child| {
13547        direct_identifier_name(*child, source)
13548            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
13549    })?;
13550    let mut identifiers = Vec::new();
13551    let mut stack = children[sentinel_index + 1..]
13552        .iter()
13553        .rev()
13554        .copied()
13555        .collect::<Vec<_>>();
13556    while let Some(current) = stack.pop() {
13557        if let Some(name) = direct_identifier_name(current, source) {
13558            identifiers.push(name);
13559            continue;
13560        }
13561        let mut cursor = current.walk();
13562        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
13563        stack.extend(children.into_iter().rev());
13564    }
13565    let [keyword, namespace] = identifiers.as_slice() else {
13566        return None;
13567    };
13568    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
13569        .then(|| vec![namespace.clone()])
13570}
13571
13572fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
13573    let mut sibling = class_semicolon.next_named_sibling();
13574    let namespace_close = loop {
13575        let Some(current) = sibling else {
13576            return false;
13577        };
13578        sibling = current.next_named_sibling();
13579        if current.kind() != "comment" {
13580            break current;
13581        }
13582    };
13583    if !cpp_is_stray_close_brace(namespace_close, source) {
13584        return false;
13585    }
13586    loop {
13587        let Some(current) = sibling else {
13588            return false;
13589        };
13590        sibling = current.next_named_sibling();
13591        if current.kind() == "comment" {
13592            continue;
13593        }
13594        return direct_identifier_name(current, source)
13595            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
13596    }
13597}
13598
13599fn cpp_sentinel_macro_body_class_region<'tree>(
13600    node: Node<'tree>,
13601    source: &str,
13602    ancestry: &ParentIndex<'tree>,
13603) -> Option<CppSentinelDirectBodyClassRegion> {
13604    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
13605        return None;
13606    };
13607    if node.kind() != "function_definition" || !node.has_error() {
13608        return None;
13609    }
13610    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
13611    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
13612    let mut cursor = body.walk();
13613    let candidates = body
13614        .named_children(&mut cursor)
13615        .filter_map(cpp_sentinel_direct_body_class_candidate)
13616        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
13617        .collect::<Vec<_>>();
13618    let [(class_node, template_node)] = candidates.as_slice() else {
13619        return None;
13620    };
13621    let original_body = cpp_body_node(*class_node)?;
13622    let name = class_like_name(*class_node, source, ancestry)?;
13623    if name.is_empty() || cpp_export_macro_token(&name) {
13624        return None;
13625    }
13626
13627    let mut sibling = node.next_named_sibling();
13628    let (class_close_start, class_close_end, class_close_line) = loop {
13629        let current = sibling?;
13630        let next = current.next_named_sibling();
13631        if cpp_is_stray_close_brace(current, source)
13632            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
13633        {
13634            let semicolon = next.expect("checked above");
13635            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
13636                return None;
13637            }
13638            break (
13639                current.start_byte(),
13640                semicolon.end_byte(),
13641                semicolon.end_position().row + 1,
13642            );
13643        }
13644        sibling = next;
13645    };
13646    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
13647    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
13648    let root = tree.root_node();
13649    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
13650    // The region reparse is its own tree, so it needs its own parent index;
13651    // the caller's index answers nothing about these nodes.
13652    let reparsed_ancestry = ParentIndex::new(root);
13653    let reparsed =
13654        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
13655    if reparsed.name != name
13656        || reparsed.declaration_node.start_byte() != class_node.start_byte()
13657        || reparsed.body.start_byte() != original_body.start_byte()
13658        || class_close_start <= reparsed.body.end_byte()
13659        || class_close_end <= class_node.end_byte()
13660    {
13661        return None;
13662    }
13663    Some(CppSentinelDirectBodyClassRegion {
13664        namespace_components,
13665        class_start: reparse_start,
13666        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
13667            node.start_position().row + 1
13668        }),
13669        class_close_end,
13670        class_close_line,
13671        name,
13672    })
13673}
13674
13675/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
13676/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
13677///
13678/// The parser puts the namespace opener and the malformed function in one root
13679/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
13680/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
13681/// the malformed function must begin with an all-caps type, then an ERROR whose
13682/// sole identifier is `namespace`, followed by the inner namespace identifier
13683/// and a compound body; and that body must contain a complete named class or a
13684/// structurally fragmented class prefix. A text reparse cannot prove any of
13685/// those ownership boundaries.
13686fn cpp_nested_namespace_sentinel<'tree>(
13687    node: Node<'tree>,
13688    source: &str,
13689    ancestry: &ParentIndex<'tree>,
13690) -> Option<CppNestedNamespaceSentinel<'tree>> {
13691    if !node.has_error() {
13692        return None;
13693    }
13694
13695    let (function, mut namespace_components) = if node.kind() == "ERROR" {
13696        let mut cursor = node.walk();
13697        let functions = node
13698            .named_children(&mut cursor)
13699            .filter(|child| child.kind() == "function_definition")
13700            .collect::<Vec<_>>();
13701        let [function] = functions.as_slice() else {
13702            return None;
13703        };
13704        if !function.has_error() {
13705            return None;
13706        }
13707        let mut cursor = node.walk();
13708        let children = node.children(&mut cursor).collect::<Vec<_>>();
13709        let function_index = children
13710            .iter()
13711            .position(|child| same_node(*child, *function))?;
13712        let [outer_keyword, outer_name, outer_open] =
13713            children.get(function_index.checked_sub(3)?..function_index)?
13714        else {
13715            return None;
13716        };
13717        if outer_keyword.kind() != "namespace"
13718            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
13719            || outer_open.kind() != "{"
13720        {
13721            return None;
13722        }
13723        (
13724            *function,
13725            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
13726        )
13727    } else if node.kind() == "function_definition" {
13728        let declaration_list = node.parent()?;
13729        let namespace = declaration_list.parent()?;
13730        if declaration_list.kind() != "declaration_list"
13731            || namespace.kind() != "namespace_definition"
13732            || namespace.child_by_field_name("body") != Some(declaration_list)
13733        {
13734            return None;
13735        }
13736        (node, Vec::new())
13737    } else {
13738        return None;
13739    };
13740
13741    let mut cursor = function.walk();
13742    let named = function
13743        .named_children(&mut cursor)
13744        .filter(|child| child.kind() != "comment")
13745        .collect::<Vec<_>>();
13746    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
13747        return None;
13748    };
13749    if first_type.kind() != "type_identifier" {
13750        return None;
13751    }
13752    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
13753    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
13754        return None;
13755    }
13756    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
13757        return None;
13758    }
13759    let inner_keyword = inner_error.named_child(0)?;
13760    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
13761        return None;
13762    }
13763    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
13764        return None;
13765    }
13766    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
13767    if inner_name.is_empty() || body.kind() != "compound_statement" {
13768        return None;
13769    }
13770    namespace_components.push(inner_name);
13771
13772    let mut cursor = body.walk();
13773    let has_complete_class = body.named_children(&mut cursor).any(|child| {
13774        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13775            cpp_body_node(class_node).is_some()
13776                && class_like_name(class_node, source, ancestry)
13777                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13778        })
13779    });
13780    if !has_complete_class
13781        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
13782    {
13783        return None;
13784    }
13785
13786    Some(CppNestedNamespaceSentinel {
13787        function,
13788        body: *body,
13789        namespace_components,
13790    })
13791}
13792
13793/// Recognize a namespace-begin sentinel directly beneath the translation unit.
13794///
13795/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
13796/// function whose type is the sentinel, whose declarator is the structured
13797/// qualified name `namespace::a::b`, and whose body contains the namespace
13798/// items. Declaration indexing already reparses this bounded region. The
13799/// inverse scanner retains the original tree, so recover the same namespace
13800/// components from the declarator fields for its lexical-scope metadata.
13801fn cpp_root_namespace_sentinel<'tree>(
13802    node: Node<'tree>,
13803    source: &str,
13804    ancestry: &ParentIndex<'tree>,
13805) -> Option<CppNestedNamespaceSentinel<'tree>> {
13806    if node.kind() != "function_definition"
13807        || !node.has_error()
13808        || node.parent()?.kind() != "translation_unit"
13809    {
13810        return None;
13811    }
13812    let first_type = node.child_by_field_name("type")?;
13813    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
13814    if first_type.kind() != "type_identifier"
13815        || sentinel.is_empty()
13816        || !cpp_export_macro_token(&sentinel)
13817    {
13818        return None;
13819    }
13820    let declarator = node.child_by_field_name("declarator")?;
13821    let body = node.child_by_field_name("body")?;
13822    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
13823        return None;
13824    }
13825    let mut cursor = node.walk();
13826    let named = node
13827        .named_children(&mut cursor)
13828        .filter(|child| child.kind() != "comment")
13829        .collect::<Vec<_>>();
13830    let [named_type, named_declarator, named_body] = named.as_slice() else {
13831        return None;
13832    };
13833    if !same_node(*named_type, first_type)
13834        || !same_node(*named_declarator, declarator)
13835        || !same_node(*named_body, body)
13836    {
13837        return None;
13838    }
13839    let mut declarator_components = Vec::new();
13840    let mut valid_components = true;
13841    walk_named_tree_preorder(declarator, true, |component| {
13842        if !matches!(
13843            component.kind(),
13844            "identifier" | "namespace_identifier" | "type_identifier"
13845        ) {
13846            return WalkControl::Continue;
13847        }
13848        let Some(component) = canonical_cpp_qualified_component(component, source) else {
13849            valid_components = false;
13850            return WalkControl::Break;
13851        };
13852        declarator_components.push(component.name);
13853        WalkControl::SkipChildren
13854    });
13855    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
13856        return None;
13857    }
13858    declarator_components.remove(0);
13859    let namespace_components = declarator_components;
13860    if namespace_components.is_empty()
13861        || namespace_components
13862            .iter()
13863            .any(|component| component.is_empty() || cpp_export_macro_token(component))
13864    {
13865        return None;
13866    }
13867
13868    let mut cursor = body.walk();
13869    let has_complete_class = body.named_children(&mut cursor).any(|child| {
13870        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13871            cpp_body_node(class_node).is_some()
13872                && class_like_name(class_node, source, ancestry)
13873                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13874        })
13875    });
13876    if !has_complete_class
13877        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
13878    {
13879        return None;
13880    }
13881
13882    Some(CppNestedNamespaceSentinel {
13883        function: node,
13884        body,
13885        namespace_components,
13886    })
13887}
13888
13889/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
13890/// malformed namespace-sentinel function.  The recovery is deliberately
13891/// structural: the class must be a direct body item, its own class node must be
13892/// erroneous and end before a unique anonymous `}` in the enclosing
13893/// declaration-list, and that namespace's next sibling must be a standalone
13894/// `;`.  The complete interior must pass the existing member-shaped reparse
13895/// gate. This avoids source brace scans and does not borrow a close from an
13896/// unrelated later declaration.
13897fn cpp_sentinel_fragmented_class_tail<'tree>(
13898    function: Node<'tree>,
13899    body: Node<'tree>,
13900    source: &str,
13901    ancestry: &ParentIndex<'tree>,
13902) -> Option<CppSentinelFragmentedClassTail<'tree>> {
13903    let mut cursor = body.walk();
13904    let candidates = body
13905        .named_children(&mut cursor)
13906        .filter_map(|child| {
13907            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
13908                let class_body = cpp_body_node(class_node)?;
13909                if !class_node.has_error() {
13910                    return None;
13911                }
13912                let name = class_like_name(class_node, source, ancestry)?;
13913                let raw_supertypes =
13914                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
13915                        .then(|| extract_cpp_supertypes(class_node, source));
13916                return Some((
13917                    class_node,
13918                    template_node,
13919                    name,
13920                    class_body,
13921                    class_body.start_byte().checked_add(1)?,
13922                    raw_supertypes,
13923                ));
13924            }
13925            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
13926            Some((
13927                child,
13928                None,
13929                prefix.name,
13930                prefix.open,
13931                prefix.open.end_byte(),
13932                prefix.raw_supertypes,
13933            ))
13934        })
13935        .collect::<Vec<_>>();
13936    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
13937        candidates.as_slice()
13938    else {
13939        return None;
13940    };
13941    if name.is_empty() || cpp_export_macro_token(name) {
13942        return None;
13943    }
13944
13945    let (close, semicolon) =
13946        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
13947
13948    let reparse_end = close.start_byte();
13949    if *reparse_start >= reparse_end {
13950        return None;
13951    }
13952    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
13953    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
13954        return None;
13955    }
13956    let class_range = Range {
13957        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13958        end_byte: semicolon.end_byte(),
13959        start_line: template_node.map_or(class_node.start_position().row, |node| {
13960            node.start_position().row
13961        }) + 1,
13962        end_line: semicolon.end_position().row + 1,
13963    };
13964    Some(CppSentinelFragmentedClassTail {
13965        class_node: *class_node,
13966        template_node: *template_node,
13967        name: name.clone(),
13968        raw_supertypes: raw_supertypes.clone(),
13969        fragmented: FragmentedExportBody {
13970            reparse_start: *reparse_start,
13971            reparse_end,
13972            class_range,
13973        },
13974        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13975    })
13976}
13977
13978/// Recover the class and out-of-line owner scopes from every malformed
13979/// namespace-sentinel region in `root`.
13980///
13981/// This is the shared structural counterpart to
13982/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
13983/// reuses the visitor's sentinel/class admission predicates instead of parsing
13984/// source text a second time.  The returned values own only ranges and names, so
13985/// they can be retained by an inverted usage scan after the tree borrow ends.
13986pub fn cpp_sentinel_recovered_classes(
13987    root: Node<'_>,
13988    source: &str,
13989) -> Vec<CppSentinelRecoveredClass> {
13990    if !root.has_error() {
13991        return Vec::new();
13992    }
13993    // This scan owns its walk of `root`, so it owns the parent index that walk
13994    // asks its ancestor questions through. Built after the error gate: a clean
13995    // tree returns without paying for one.
13996    let ancestry = ParentIndex::new(root);
13997    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
13998    let mut stack = vec![root];
13999    while let Some(current) = stack.pop() {
14000        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
14001            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
14002        {
14003            let namespace_components = cpp_sentinel_recovered_namespace_components(
14004                recovered.function,
14005                &recovered.namespace_components,
14006                source,
14007            );
14008            let fragmented = cpp_sentinel_fragmented_class_tail(
14009                recovered.function,
14010                recovered.body,
14011                source,
14012                &ancestry,
14013            );
14014            let mut class_candidates = Vec::new();
14015            let mut cursor = recovered.body.walk();
14016            for (class_node, template_node) in recovered
14017                .body
14018                .named_children(&mut cursor)
14019                .filter_map(cpp_sentinel_body_class_candidate)
14020            {
14021                let Some(name) = class_like_name(class_node, source, &ancestry) else {
14022                    continue;
14023                };
14024                if name.is_empty() || cpp_export_macro_token(&name) {
14025                    continue;
14026                }
14027                let is_fragmented = fragmented
14028                    .as_ref()
14029                    .is_some_and(|tail| same_node(tail.class_node, class_node));
14030                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
14031                    continue;
14032                }
14033                let class_range = if is_fragmented {
14034                    fragmented
14035                        .as_ref()
14036                        .map(|tail| tail.fragmented.class_range)
14037                        .expect("fragmented class range is present when class matches")
14038                } else {
14039                    cpp_declaration_range(template_node.unwrap_or(class_node))
14040                };
14041                class_candidates.push((class_range, name));
14042            }
14043            if let Some(fragmented) = fragmented
14044                .as_ref()
14045                .filter(|tail| tail.class_node.kind() == "ERROR")
14046            {
14047                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
14048            }
14049
14050            let mut owner_ranges =
14051                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
14052            cpp_sentinel_extend_unique_owner_ranges(
14053                &mut owner_ranges,
14054                cpp_sentinel_recovered_sibling_owner_ranges(
14055                    recovered.function,
14056                    &namespace_components,
14057                    source,
14058                ),
14059            );
14060            for (class_range, name) in class_candidates {
14061                push_cpp_sentinel_recovered_class(
14062                    &mut recovered_classes,
14063                    cpp_declaration_range(recovered.body),
14064                    &namespace_components,
14065                    class_range,
14066                    name,
14067                    &owner_ranges,
14068                );
14069            }
14070
14071            if let Some(declaration_list) = recovered
14072                .function
14073                .parent()
14074                .filter(|parent| parent.kind() == "declaration_list")
14075            {
14076                let outer_namespace =
14077                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
14078                push_cpp_sentinel_sibling_classes(
14079                    &mut recovered_classes,
14080                    declaration_list,
14081                    recovered.function,
14082                    &outer_namespace,
14083                    source,
14084                    &ancestry,
14085                );
14086            }
14087        } else if let Some(region) =
14088            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
14089        {
14090            let namespace_components = cpp_sentinel_recovered_namespace_components(
14091                current,
14092                &region.namespace_components,
14093                source,
14094            );
14095            let owner_container = current
14096                .parent()
14097                .filter(|parent| parent.kind() == "declaration_list")
14098                .unwrap_or(current);
14099            let owner_ranges =
14100                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14101            push_cpp_sentinel_recovered_class(
14102                &mut recovered_classes,
14103                cpp_declaration_range(owner_container),
14104                &namespace_components,
14105                Range {
14106                    start_byte: region.class_start,
14107                    end_byte: region.class_close_end,
14108                    start_line: region.class_start_line,
14109                    end_line: region.class_close_line,
14110                },
14111                region.name,
14112                &owner_ranges,
14113            );
14114        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
14115            // A generic sentinel-prefixed class can be reduced as a malformed
14116            // function/ERROR without the explicit `namespace X` token pair.
14117            // Reuse the declaration visitor's bounded reparse and retain only
14118            // the recovered class identity/range here.
14119            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
14120                region;
14121            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14122                continue;
14123            };
14124            let root = tree.root_node();
14125            let template_node = cpp_sentinel_reparsed_leading_template(root);
14126            // A region reparse is its own tree and needs its own parent index.
14127            let reparsed_ancestry = ParentIndex::new(root);
14128            let Some(reparsed_class) =
14129                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
14130            else {
14131                continue;
14132            };
14133            let class_node = reparsed_class.declaration_node;
14134            let name = reparsed_class.name;
14135            let namespace_components =
14136                cpp_sentinel_recovered_namespace_components(current, &[], source);
14137            let owner_container = current
14138                .parent()
14139                .filter(|parent| parent.kind() == "declaration_list")
14140                .unwrap_or(current);
14141            let mut owner_ranges =
14142                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14143            cpp_sentinel_extend_unique_owner_ranges(
14144                &mut owner_ranges,
14145                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
14146            );
14147            push_cpp_sentinel_recovered_class(
14148                &mut recovered_classes,
14149                cpp_declaration_range(owner_container),
14150                &namespace_components,
14151                Range {
14152                    start_byte: class_start,
14153                    end_byte: close_end,
14154                    start_line: class_node.start_position().row + 1,
14155                    end_line: class_node.end_position().row + 1,
14156                },
14157                name,
14158                &owner_ranges,
14159            );
14160            if owner_container.kind() == "declaration_list" {
14161                push_cpp_sentinel_sibling_classes(
14162                    &mut recovered_classes,
14163                    owner_container,
14164                    current,
14165                    &namespace_components,
14166                    source,
14167                    &ancestry,
14168                );
14169            }
14170        }
14171
14172        let mut cursor = current.walk();
14173        stack.extend(current.named_children(&mut cursor));
14174    }
14175    // A shallower sentinel can expose nested classes as apparent namespace
14176    // siblings even after a deeper sentinel proves that a containing class
14177    // owns their ranges. Drop those shadow descriptors; scope recovery starts
14178    // from the proven containing class and appends parser-visible class
14179    // ancestors, preserving the full `Outer::Inner` chain.
14180    let shadowed = recovered_classes
14181        .iter()
14182        .map(|candidate| {
14183            recovered_classes.iter().any(|container| {
14184                container.class_range.start_byte <= candidate.class_range.start_byte
14185                    && container.class_range.end_byte >= candidate.class_range.end_byte
14186                    && container.class_range != candidate.class_range
14187                    && container.namespace_scope_components.len()
14188                        > candidate.namespace_scope_components.len()
14189                    && container
14190                        .namespace_scope_components
14191                        .starts_with(&candidate.namespace_scope_components)
14192            })
14193        })
14194        .collect::<Vec<_>>();
14195    let mut index = 0usize;
14196    recovered_classes.retain(|_| {
14197        let keep = !shadowed[index];
14198        index += 1;
14199        keep
14200    });
14201    recovered_classes
14202}
14203
14204/// A flat sentinel can swallow the first class while leaving later classes and
14205/// their out-of-line definitions as ordinary declaration-list siblings.  Once
14206/// the malformed class proves the sentinel envelope, retain those structurally
14207/// complete sibling classes under the same surviving namespace so every member
14208/// owner in the region uses one recovery contract.
14209fn push_cpp_sentinel_sibling_classes<'tree>(
14210    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14211    declaration_list: Node<'tree>,
14212    sentinel_node: Node<'tree>,
14213    namespace_components: &[String],
14214    source: &str,
14215    ancestry: &ParentIndex<'tree>,
14216) {
14217    let owner_ranges =
14218        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
14219    let namespace_range = cpp_declaration_range(declaration_list);
14220    let mut cursor = declaration_list.walk();
14221    for (class_node, template_node) in declaration_list
14222        .named_children(&mut cursor)
14223        .filter(|child| !same_node(*child, sentinel_node))
14224        .filter_map(cpp_sentinel_body_class_candidate)
14225    {
14226        let Some(name) = class_like_name(class_node, source, ancestry) else {
14227            continue;
14228        };
14229        if name.is_empty()
14230            || cpp_export_macro_token(&name)
14231            || cpp_complete_class_body_close(class_node).is_none()
14232        {
14233            continue;
14234        }
14235        push_cpp_sentinel_recovered_class(
14236            recovered_classes,
14237            namespace_range,
14238            namespace_components,
14239            cpp_declaration_range(template_node.unwrap_or(class_node)),
14240            name,
14241            &owner_ranges,
14242        );
14243    }
14244}
14245
14246fn push_cpp_sentinel_recovered_class(
14247    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14248    namespace_range: Range,
14249    namespace_components: &[String],
14250    class_range: Range,
14251    name: String,
14252    owner_ranges: &[CppSentinelRecoveredOwner],
14253) {
14254    let mut scope_components = namespace_components.to_vec();
14255    scope_components.push(name);
14256    let owner_ranges = owner_ranges
14257        .iter()
14258        .filter(|owner| owner.scope_components.starts_with(&scope_components))
14259        .cloned()
14260        .collect::<Vec<_>>();
14261    if recovered_classes.iter().any(|existing| {
14262        existing.class_range == class_range && existing.scope_components == scope_components
14263    }) {
14264        return;
14265    }
14266    recovered_classes.push(CppSentinelRecoveredClass {
14267        namespace_range,
14268        namespace_scope_components: namespace_components.to_vec(),
14269        class_range,
14270        scope_components,
14271        owner_ranges,
14272    });
14273}
14274
14275fn cpp_sentinel_recovered_namespace_components(
14276    function: Node<'_>,
14277    recovered_components: &[String],
14278    source: &str,
14279) -> Vec<String> {
14280    let mut ancestor_components = Vec::new();
14281    let mut ancestor = function.parent();
14282    while let Some(current) = ancestor {
14283        if current.kind() == "namespace_definition"
14284            && let Some(name_node) = current.child_by_field_name("name")
14285            && let Some(components) = cpp_name_components(name_node, source)
14286        {
14287            ancestor_components.push(
14288                components
14289                    .into_iter()
14290                    .map(|component| component.name)
14291                    .collect::<Vec<_>>(),
14292            );
14293        }
14294        ancestor = current.parent();
14295    }
14296    ancestor_components.reverse();
14297    let mut ancestors = ancestor_components
14298        .into_iter()
14299        .flatten()
14300        .collect::<Vec<_>>();
14301
14302    let overlap = (0..=ancestors.len().min(recovered_components.len()))
14303        .rev()
14304        .find(|length| {
14305            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
14306        })
14307        .unwrap_or(0);
14308    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
14309    ancestors
14310}
14311
14312fn cpp_sentinel_recovered_owner_ranges(
14313    body: Node<'_>,
14314    namespace_components: &[String],
14315    source: &str,
14316) -> Vec<CppSentinelRecoveredOwner> {
14317    let mut owners = Vec::new();
14318    walk_named_tree_preorder(body, true, |node| {
14319        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14320    });
14321    owners
14322}
14323
14324fn cpp_sentinel_collect_owner_range(
14325    node: Node<'_>,
14326    namespace_components: &[String],
14327    source: &str,
14328    owners: &mut Vec<CppSentinelRecoveredOwner>,
14329) -> WalkControl {
14330    if node.kind() != "function_definition" {
14331        return WalkControl::Continue;
14332    }
14333    let Some(function_declarator) = extract_function_declarator(node) else {
14334        return WalkControl::Continue;
14335    };
14336    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
14337        return WalkControl::Continue;
14338    };
14339    let Some(mut components) = cpp_name_components(name_node, source) else {
14340        return WalkControl::Continue;
14341    };
14342    if components.len() <= 1 {
14343        return WalkControl::Continue;
14344    }
14345    components.pop();
14346    let mut owner_components = components
14347        .into_iter()
14348        .map(|component| component.name)
14349        .collect::<Vec<_>>();
14350    let overlap = (0..=namespace_components.len().min(owner_components.len()))
14351        .rev()
14352        .find(|length| {
14353            owner_components[..*length]
14354                == namespace_components[namespace_components.len().saturating_sub(*length)..]
14355        })
14356        .unwrap_or(0);
14357    let mut scope_components = namespace_components.to_vec();
14358    scope_components.extend(owner_components.drain(overlap..));
14359    if scope_components.len() <= namespace_components.len() {
14360        return WalkControl::Continue;
14361    }
14362    let range = cpp_declaration_range(node);
14363    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
14364        existing.range == range && existing.scope_components == scope_components
14365    }) {
14366        owners.push(CppSentinelRecoveredOwner {
14367            range,
14368            owner_name_start_byte: name_node.start_byte(),
14369            namespace_component_count: namespace_components.len(),
14370            scope_components,
14371        });
14372    }
14373    WalkControl::Continue
14374}
14375
14376fn cpp_sentinel_extend_unique_owner_ranges(
14377    owners: &mut Vec<CppSentinelRecoveredOwner>,
14378    additional: Vec<CppSentinelRecoveredOwner>,
14379) {
14380    for owner in additional {
14381        if !owners.iter().any(|existing| {
14382            existing.range == owner.range && existing.scope_components == owner.scope_components
14383        }) {
14384            owners.push(owner);
14385        }
14386    }
14387}
14388
14389fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
14390    if node.kind() != "ERROR" || node.named_child_count() != 1 {
14391        return false;
14392    }
14393    let Some(end_name) = node.named_child(0) else {
14394        return false;
14395    };
14396    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
14397        return false;
14398    }
14399    let mut cursor = node.walk();
14400    node.children(&mut cursor)
14401        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
14402}
14403
14404/// Collect owner definitions that the malformed sentinel left as later
14405/// declaration-list siblings. Parser-visible namespace siblings are a hard
14406/// boundary: their declarations must keep their own lexical namespace.
14407fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14408    parent: Node<'_>,
14409    sentinel_node: Node<'_>,
14410    namespace_components: &[String],
14411    source: &str,
14412) -> Vec<CppSentinelRecoveredOwner> {
14413    let mut owners = Vec::new();
14414    let mut after_sentinel = false;
14415    let mut cursor = parent.walk();
14416    for child in parent.named_children(&mut cursor) {
14417        if !after_sentinel {
14418            if same_node(child, sentinel_node) {
14419                after_sentinel = true;
14420            }
14421            continue;
14422        }
14423        walk_named_tree_preorder(child, true, |node| {
14424            if node.kind() == "namespace_definition" {
14425                return WalkControl::SkipChildren;
14426            }
14427            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14428        });
14429    }
14430    owners
14431}
14432
14433/// Collect owner definitions after a malformed namespace, stopping only at
14434/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
14435/// enclosing container is not trusted to belong to the recovered namespace.
14436fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14437    parent: Node<'_>,
14438    sentinel_node: Node<'_>,
14439    namespace_components: &[String],
14440    source: &str,
14441) -> Option<Vec<CppSentinelRecoveredOwner>> {
14442    let mut owners = Vec::new();
14443    let mut after_namespace = false;
14444    let mut cursor = parent.walk();
14445    for child in parent.named_children(&mut cursor) {
14446        if !after_namespace {
14447            if same_node(child, sentinel_node) {
14448                after_namespace = true;
14449            }
14450            continue;
14451        }
14452        if cpp_sentinel_namespace_end(child, source) {
14453            return Some(owners);
14454        }
14455        walk_named_tree_preorder(child, true, |node| {
14456            if node.kind() == "namespace_definition" {
14457                return WalkControl::SkipChildren;
14458            }
14459            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14460        });
14461    }
14462    None
14463}
14464
14465fn cpp_sentinel_recovered_sibling_owner_ranges(
14466    sentinel_node: Node<'_>,
14467    namespace_components: &[String],
14468    source: &str,
14469) -> Vec<CppSentinelRecoveredOwner> {
14470    let Some(declaration_list) = sentinel_node
14471        .parent()
14472        .filter(|parent| parent.kind() == "declaration_list")
14473    else {
14474        return Vec::new();
14475    };
14476    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14477        declaration_list,
14478        sentinel_node,
14479        namespace_components,
14480        source,
14481    );
14482
14483    let Some(namespace) = declaration_list
14484        .parent()
14485        .filter(|parent| parent.kind() == "namespace_definition")
14486    else {
14487        return owners;
14488    };
14489    let Some(outer_parent) = namespace.parent() else {
14490        return owners;
14491    };
14492    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14493        outer_parent,
14494        namespace,
14495        namespace_components,
14496        source,
14497    ) {
14498        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
14499    }
14500    owners
14501}
14502
14503fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
14504    let mut current = function_declarator.child_by_field_name("declarator")?;
14505    loop {
14506        if let Some(name) = macro_decorated_unqualified_name(current) {
14507            current = name;
14508            continue;
14509        }
14510        if matches!(
14511            current.kind(),
14512            "qualified_identifier"
14513                | "scoped_identifier"
14514                | "scoped_type_identifier"
14515                | "identifier"
14516                | "field_identifier"
14517                | "operator_name"
14518                | "destructor_name"
14519                | "literal_operator_name"
14520        ) {
14521            return Some(current);
14522        }
14523        current = current
14524            .child_by_field_name("declarator")
14525            .or_else(|| current.child_by_field_name("name"))
14526            .or_else(|| last_named_child(current))?;
14527    }
14528}
14529
14530/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
14531/// `::` between the halves. The scope is an attribute-like macro token, not
14532/// a namespace; `Name` is the declared name.
14533///
14534/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
14535/// tree-sitter joins the attribute macro and the constructor name into one
14536/// `qualified_identifier` and marks the separator it had to invent as MISSING.
14537/// That flag is the grammar's own record that no separator is in the source,
14538/// so read it instead of looking for `::` in the node text. A genuine
14539/// `Outer::Inner` keeps a present separator and is declined here, and so is
14540/// the explicit-global `::name`, whose `::` is present and whose scope is
14541/// absent.
14542fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
14543    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
14544        return None;
14545    }
14546    let mut cursor = node.walk();
14547    if node
14548        .children(&mut cursor)
14549        .any(|child| child.kind() == "::" && !child.is_missing())
14550    {
14551        return None;
14552    }
14553    node.child_by_field_name("name")
14554}
14555
14556fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
14557    if let Some(name) = macro_decorated_unqualified_name(node) {
14558        return cpp_name_components(name, source);
14559    }
14560    match node.kind() {
14561        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14562            let mut components = match node.child_by_field_name("scope") {
14563                Some(scope) => cpp_name_components(scope, source)?,
14564                None => Vec::new(),
14565            };
14566            let name = node.child_by_field_name("name")?;
14567            components.push(canonical_cpp_qualified_component(name, source)?);
14568            Some(components)
14569        }
14570        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
14571    }
14572}
14573
14574fn cpp_sentinel_fragment_boundary<'tree>(
14575    function: Node<'tree>,
14576    class_node: Node<'tree>,
14577    class_body: Node<'tree>,
14578    source: &str,
14579) -> Option<(Node<'tree>, Node<'tree>)> {
14580    let declaration_list = function.parent()?;
14581    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
14582        return None;
14583    }
14584    let namespace = declaration_list.parent()?;
14585    if namespace.kind() != "namespace_definition"
14586        || namespace.child_by_field_name("body") != Some(declaration_list)
14587    {
14588        return None;
14589    }
14590    let mut cursor = declaration_list.walk();
14591    let closes = declaration_list
14592        .children(&mut cursor)
14593        .filter(|child| {
14594            !child.is_named()
14595                && child.kind() == "}"
14596                && child.start_byte() >= function.end_byte()
14597                && child.start_byte() > class_node.end_byte()
14598                && child.start_byte() > class_body.start_byte()
14599        })
14600        .collect::<Vec<_>>();
14601    let [close] = closes.as_slice() else {
14602        return None;
14603    };
14604    let semicolon = namespace.next_named_sibling()?;
14605    if !cpp_is_stray_semicolon(semicolon, source)
14606        || close.end_byte() != namespace.end_byte()
14607        || semicolon.start_byte() < namespace.end_byte()
14608    {
14609        return None;
14610    }
14611    Some((*close, semicolon))
14612}
14613
14614/// Detect the bogus declaration/function tree that tree-sitter recovers for a
14615/// region prefixed by an object-like macro sentinel the parser cannot see
14616/// (issue #941), and return the byte range `[start, end)` of the swallowed
14617/// declaration interior to reparse.
14618///
14619/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
14620/// `function_definition` whose first non-comment named child is the sentinel
14621/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
14622/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
14623/// the real items.
14624/// `start` is the end of the sentinel identifier -- everything after it is the
14625/// genuine source. `end` is the node's end, extended across any trailing empty
14626/// `;` statement the mis-parse displaced past the node (the class/struct closing
14627/// semicolon), so the reparse sees a complete, brace-balanced item.
14628///
14629/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
14630/// node (`has_error`). Unknown annotation/export macros can make a real callable
14631/// error-recovered even though tree-sitter still preserves its declarator, so a
14632/// preserved callable is admitted only when a displaced class keyword precedes
14633/// that declarator. The clean-reparse-to-items gate in
14634/// `cpp_reparsed_items_are_indexable` is the final arbiter.
14635/// Return the reparse start and, when present, the structurally recovered class
14636/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
14637/// separately from the reparse start because an opaque template-declaration
14638/// macro may precede it.
14639fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
14640    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
14641    {
14642        return None;
14643    }
14644    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
14645    // retain a valid function declarator despite the unknown export macro making
14646    // the outer node erroneous. Remember that declarator for the ordering gate
14647    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
14648    // class keyword precedes a spurious callable assembled from a later member.
14649    let mut declarator_cursor = node.walk();
14650    let preserved_callable = node
14651        .children_by_field_name("declarator", &mut declarator_cursor)
14652        .find_map(extract_function_declarator);
14653    // Leading documentation comments are attached to the malformed
14654    // `function_definition` as named children.  They are not part of the
14655    // sentinel prefix, so select the first non-comment child structurally
14656    // rather than requiring the sentinel to be child zero.  This is the shape
14657    // emitted for nlohmann/json's `basic_json`: its class documentation comment
14658    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
14659    // envelope otherwise ends at the first nested union.
14660    let mut cursor = node.walk();
14661    let first = node
14662        .named_children(&mut cursor)
14663        .find(|child| child.kind() != "comment")?;
14664    if first.kind() != "type_identifier" {
14665        return None;
14666    }
14667    let sentinel = normalize_cpp_whitespace(node_text(first, source));
14668    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
14669        return None;
14670    }
14671    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
14672    // makes the trailing sentinel of one region and the leading sentinel of the
14673    // next both land as bare macro-token identifiers ahead of the real content.
14674    // Advance past every leading macro-token identifier so the reparse begins at
14675    // genuine source rather than another sentinel that would re-form the bogus
14676    // shape and fail the reparse gate.
14677    let mut start = first.end_byte();
14678    let mut after_first = false;
14679    let mut cursor = node.walk();
14680    for child in node.named_children(&mut cursor) {
14681        if !after_first {
14682            if same_node(child, first) {
14683                after_first = true;
14684            }
14685            continue;
14686        }
14687        if matches!(child.kind(), "identifier" | "type_identifier")
14688            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
14689        {
14690            start = child.end_byte();
14691        } else {
14692            break;
14693        }
14694    }
14695    // An additional opaque template-declaration macro before a class can be
14696    // folded into the bogus function's qualified declarator.  In that shape
14697    // the macro is not a direct sibling we can skip above; tree-sitter exposes
14698    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
14699    // Reparse from that keyword (or a real preceding `template` keyword) so the
14700    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
14701    // a class nested in a genuine sentinel-wrapped namespace lies after the
14702    // body opening and must not change the established region start.
14703    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
14704    let mut class_start = None;
14705    let mut template_start = None;
14706    let mut stack = vec![node];
14707    while let Some(current) = stack.pop() {
14708        if current.start_byte() >= prefix_end {
14709            continue;
14710        }
14711        if matches!(
14712            current.kind(),
14713            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
14714        ) {
14715            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
14716                "class" | "struct" | "union" | "enum" => {
14717                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
14718                        seen.min(current.start_byte())
14719                    }));
14720                }
14721                "template" => {
14722                    template_start =
14723                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
14724                            seen.min(current.start_byte())
14725                        }));
14726                }
14727                _ => {}
14728            }
14729        }
14730        let mut cursor = current.walk();
14731        stack.extend(current.children(&mut cursor));
14732    }
14733    if preserved_callable.is_some_and(|callable| {
14734        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
14735    }) {
14736        return None;
14737    }
14738    if let Some(class_start) = class_start {
14739        start = template_start
14740            .filter(|template_start| *template_start < class_start)
14741            .unwrap_or(class_start);
14742    }
14743    Some((start, class_start))
14744}
14745
14746/// Locate a sentinel-prefixed class whose malformed declaration was split across
14747/// root-level siblings. The true class close is represented structurally as a
14748/// lone `}` error followed by the class's displaced `;`; nested method/body
14749/// errors are not direct siblings of the sentinel node and therefore cannot
14750/// satisfy this pair.
14751fn cpp_sentinel_macro_class_region<'tree>(
14752    node: Node<'tree>,
14753    source: &str,
14754) -> Option<(usize, usize, usize, usize, usize, usize)> {
14755    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
14756        return None;
14757    };
14758    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
14759        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
14760        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
14761    if class_start >= body_open_start {
14762        return None;
14763    }
14764    let sibling_close = {
14765        let mut sibling = node.next_named_sibling();
14766        let mut found = None;
14767        while let Some(current) = sibling {
14768            let next = current.next_named_sibling();
14769            if cpp_is_stray_close_brace(current, source)
14770                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
14771            {
14772                let semicolon = next.expect("checked above");
14773                found = Some((
14774                    current.start_byte(),
14775                    semicolon.end_byte(),
14776                    semicolon.end_position().row + 1,
14777                ));
14778                break;
14779            }
14780            sibling = next;
14781        }
14782        found
14783    };
14784    // A stray `};` sibling is this class's close only when the bounded reparse
14785    // agrees the first body-bearing class ENDS there. When the malformed
14786    // envelope swallowed the class's true close, the scan can promote a much
14787    // later scope's close instead -- in protobuf-generated headers
14788    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
14789    // `struct TableStruct_*` paired with the first message class's `};`, making
14790    // the recovered "class body" span whole `namespace {}` blocks and minting
14791    // namespace-scope classes as nested members of the recovered class, which
14792    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
14793    // (#2275). On disagreement, fall through to the suffix-reparse boundary
14794    // below, which derives the close from the class node's own balanced body
14795    // range.
14796    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
14797        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14798            return false;
14799        };
14800        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14801        // A region reparse is its own tree and needs its own parent index.
14802        let reparsed_ancestry = ParentIndex::new(tree.root_node());
14803        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
14804            tree.root_node(),
14805            template_node,
14806            source,
14807            &reparsed_ancestry,
14808        ) else {
14809            return false;
14810        };
14811        let body = reparsed_class.body;
14812        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
14813    });
14814    let (class_close_start, class_close_end, class_close_line) =
14815        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
14816            (class_close_start, class_close_end, class_close_line)
14817        } else {
14818            // When the malformed envelope itself is an ERROR, tree-sitter can
14819            // leave the class's balanced close in the source while promoting
14820            // all following members to siblings. Reparse the complete suffix
14821            // and use the first body-bearing class node's own field range as
14822            // the partition boundary. This keeps balancing in tree-sitter and
14823            // preserves the source's original byte offsets.
14824            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
14825            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14826            // A region reparse is its own tree and needs its own parent index.
14827            let reparsed_ancestry = ParentIndex::new(tree.root_node());
14828            let reparsed_class = cpp_sentinel_reparsed_class(
14829                tree.root_node(),
14830                template_node,
14831                source,
14832                &reparsed_ancestry,
14833            )?;
14834            let body = reparsed_class.body;
14835            let class_close_end = body.end_byte();
14836            let class_close_start = class_close_end.checked_sub(1)?;
14837            let class_close_line = body.end_position().row + 1;
14838            (class_close_start, class_close_end, class_close_line)
14839        };
14840    if class_close_start <= class_start {
14841        return None;
14842    }
14843
14844    // Reparse only far enough to expose the class body opening. This is a
14845    // structured check that the candidate really begins with a body-bearing
14846    // class-like item; the original malformed tree cannot provide that node.
14847    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
14848    let class_root = tree.root_node();
14849    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
14850    // A region reparse is its own tree and needs its own parent index.
14851    let reparsed_ancestry = ParentIndex::new(class_root);
14852    let reparsed_class =
14853        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
14854    let body = reparsed_class.body;
14855    // The class body opening must agree with the malformed wrapper's structured
14856    // body field. This rejects an inner nested class while permitting later
14857    // members to remain fragmented as root-level siblings in the bounded parse.
14858    if body.start_byte() != body_open_start {
14859        return None;
14860    }
14861    let body_start = body.start_byte().checked_add(1)?;
14862    (body_start < class_close_start).then_some((
14863        reparse_start,
14864        class_start,
14865        body_start,
14866        class_close_start,
14867        class_close_end,
14868        class_close_line,
14869    ))
14870}
14871
14872/// Find the `{` token immediately following the class/struct/union/enum token
14873/// at `class_start` in the malformed tree. The token is anonymous in the C++
14874/// grammar, so this deliberately walks all children (not only named children)
14875/// and relies on sibling structure rather than source-text searching.
14876fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
14877    let mut stack = vec![node];
14878    while let Some(current) = stack.pop() {
14879        if current.start_byte() == class_start
14880            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
14881        {
14882            let mut sibling = current.next_sibling();
14883            while let Some(candidate) = sibling {
14884                if candidate.kind() == "{" {
14885                    return Some(candidate.start_byte());
14886                }
14887                sibling = candidate.next_sibling();
14888            }
14889        }
14890        let mut cursor = current.walk();
14891        stack.extend(current.children(&mut cursor));
14892    }
14893    None
14894}
14895
14896/// The class body that tree-sitter displaced out of a sentinel-prefixed
14897/// declaration and left as the malformed node's next sibling.
14898///
14899/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
14900/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
14901/// the last child of that `ERROR` and its `{` opens a sibling
14902/// `compound_statement` instead. The body is still the malformed tree's own
14903/// structured token, which is what the caller's `body.start_byte() !=
14904/// body_open_start` agreement check needs; it just is not reachable by walking
14905/// forward from the class token inside the node.
14906fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
14907    node.next_named_sibling()
14908        .filter(|sibling| sibling.kind() == "compound_statement")
14909}
14910
14911fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
14912    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
14913    let mut end = if class_start.is_some() {
14914        cpp_macro_prefixed_class_end(source, start)?
14915    } else {
14916        node.end_byte()
14917    };
14918    if class_start.is_none()
14919        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
14920    {
14921        end = end.max(namespace_end);
14922    }
14923    let mut sibling = node.next_named_sibling();
14924    while let Some(current) = sibling {
14925        if !cpp_is_stray_semicolon(current, source) {
14926            break;
14927        }
14928        end = current.end_byte();
14929        sibling = current.next_named_sibling();
14930    }
14931    (start < end).then_some((start, end))
14932}
14933
14934/// Extend a sentinel reparse through a following namespace that tree-sitter
14935/// flattened into the sentinel node's sibling list.
14936///
14937/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
14938/// unknown macro becomes a false function return type and consumes the first
14939/// namespace body. A second `namespace detail` then loses its enclosing node:
14940/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
14941/// attaches its declarations to the surrounding error tree. Reparse from that
14942/// structured keyword so tree-sitter, rather than a source-text brace scan,
14943/// supplies the complete namespace boundary.
14944fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
14945    let mut sibling = node.next_sibling();
14946    let keyword = loop {
14947        let candidate = sibling?;
14948        sibling = candidate.next_sibling();
14949        if candidate.kind() != "comment" {
14950            break candidate;
14951        }
14952    };
14953    if keyword.kind() != "namespace" {
14954        return None;
14955    }
14956    let name = loop {
14957        let candidate = sibling?;
14958        sibling = candidate.next_sibling();
14959        if candidate.kind() != "comment" {
14960            break candidate;
14961        }
14962    };
14963    if cpp_namespace_name_components(name, source).is_empty() {
14964        return None;
14965    }
14966    let open = loop {
14967        let candidate = sibling?;
14968        sibling = candidate.next_sibling();
14969        if candidate.kind() != "comment" {
14970            break candidate;
14971        }
14972    };
14973    if open.kind() != "{" {
14974        return None;
14975    }
14976
14977    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
14978    let root = tree.root_node();
14979    let mut cursor = root.walk();
14980    let namespace = root
14981        .named_children(&mut cursor)
14982        .find(|candidate| candidate.kind() != "comment")?;
14983    (namespace.kind() == "namespace_definition"
14984        && namespace.start_byte() == keyword.start_byte()
14985        && namespace.child_by_field_name("body").is_some())
14986    .then_some(namespace.end_byte())
14987}
14988
14989/// Parse the source suffix beginning at a structurally recovered class/template
14990/// keyword and return the end of its first body-bearing class item.  The parser,
14991/// rather than a brace scanner, owns nested-body balancing.  This is needed when
14992/// the original error tree truncates the class and scatters later members as
14993/// top-level siblings.
14994fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
14995    let tree = cpp_reparse_region_items(source, start, source.len())?;
14996    let root = tree.root_node();
14997    let mut cursor = root.walk();
14998    for item in root.named_children(&mut cursor) {
14999        if item.end_byte() <= start || item.kind() == "comment" {
15000            continue;
15001        }
15002        let mut stack = vec![item];
15003        while let Some(current) = stack.pop() {
15004            if matches!(
15005                current.kind(),
15006                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
15007            ) && cpp_body_node(current).is_some()
15008            {
15009                return Some(current.end_byte());
15010            }
15011            let mut cursor = current.walk();
15012            stack.extend(current.named_children(&mut cursor));
15013        }
15014        // The recovered prefix is required to begin with the class item.  If
15015        // the first real item is something else, fail closed rather than skip
15016        // arbitrary source looking for a later class.
15017        return None;
15018    }
15019    None
15020}
15021
15022/// An empty `;` statement: the displaced closing semicolon of a struct/class that
15023/// the sentinel mis-parse split off past the bogus function node.
15024fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
15025    node.kind() == "expression_statement"
15026        && node.named_child_count() == 0
15027        && node_text(node, source).trim() == ";"
15028}
15029
15030/// Recover the member that follows CPython's object header macro when
15031/// tree-sitter folds both declarations into one malformed field.
15032///
15033/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
15034/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
15035/// sole child is the real field declarator. The tail may wrap that identifier
15036/// in pointer declarators, as in `PyObject_HEAD ImagingObject *image;`; other
15037/// malformed wrappers do not provide the same evidence that this is the
15038/// CPython macro boundary.
15039#[derive(Clone, Copy)]
15040pub(crate) struct RecoveredPyObjectHeadField<'tree> {
15041    pub(crate) type_node: Node<'tree>,
15042    pub(crate) name: Node<'tree>,
15043    pub(crate) declarator: Node<'tree>,
15044}
15045
15046impl RecoveredPyObjectHeadField<'_> {
15047    pub(crate) fn pointer_depth(self) -> i32 {
15048        let mut depth = 0;
15049        let mut current = self.declarator;
15050        while current != self.name {
15051            debug_assert_eq!(current.kind(), "pointer_declarator");
15052            depth += 1;
15053            current = current
15054                .child_by_field_name("declarator")
15055                .expect("recovered PyObject field pointer has an inner declarator");
15056        }
15057        depth
15058    }
15059}
15060
15061pub(crate) fn recovered_pyobject_head_field<'tree>(
15062    node: Node<'tree>,
15063    source: &str,
15064) -> Option<RecoveredPyObjectHeadField<'tree>> {
15065    if node.kind() != "field_declaration" {
15066        return None;
15067    }
15068    let type_node = node.child_by_field_name("type")?;
15069    if type_node.kind() != "type_identifier"
15070        || node_text(type_node, source).trim() != "PyObject_HEAD"
15071    {
15072        return None;
15073    }
15074    let pseudo_declarator = node.child_by_field_name("declarator")?;
15075    let mut cursor = node.walk();
15076    let errors = node
15077        .named_children(&mut cursor)
15078        .filter(|child| child.kind() == "ERROR")
15079        .collect::<Vec<_>>();
15080    let [error] = errors.as_slice() else {
15081        return None;
15082    };
15083    if error.named_child_count() != 1 {
15084        return None;
15085    }
15086    let error_child = error.named_child(0)?;
15087    if pseudo_declarator.kind() == "field_identifier"
15088        && error.start_byte() >= pseudo_declarator.end_byte()
15089        && error_child.kind() == "identifier"
15090    {
15091        return Some(RecoveredPyObjectHeadField {
15092            type_node: pseudo_declarator,
15093            name: error_child,
15094            declarator: error_child,
15095        });
15096    }
15097    if pseudo_declarator.kind() != "pointer_declarator"
15098        || error.end_byte() > pseudo_declarator.start_byte()
15099        || error_child.kind() != "identifier"
15100    {
15101        return None;
15102    }
15103    let mut name = pseudo_declarator;
15104    while name.kind() == "pointer_declarator" {
15105        name = name.child_by_field_name("declarator")?;
15106    }
15107    (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
15108        type_node: error_child,
15109        name,
15110        declarator: pseudo_declarator,
15111    })
15112}
15113
15114/// Recover the real field name when a leading object-like annotation macro
15115/// displaces a qualified type into tree-sitter's bit-field recovery shape.
15116///
15117/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
15118/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
15119/// `bitfield_clause` containing an error plus an assignment.  The assignment's
15120/// left field is the only structured declaration name in that malformed tail.
15121/// A real bit-field is excluded by the all-caps macro type and required error.
15122fn recovered_macro_qualified_field_declarators<'tree>(
15123    node: Node<'tree>,
15124    source: &str,
15125) -> Option<Vec<Node<'tree>>> {
15126    if node.kind() != "field_declaration" {
15127        return None;
15128    }
15129    let macro_type = node.child_by_field_name("type")?;
15130    if macro_type.kind() != "type_identifier"
15131        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15132    {
15133        return None;
15134    }
15135    let pseudo_declarator = node.child_by_field_name("declarator")?;
15136    if pseudo_declarator.kind() != "field_identifier" {
15137        return None;
15138    }
15139    let mut cursor = node.walk();
15140    let clause = node
15141        .named_children(&mut cursor)
15142        .find(|child| child.kind() == "bitfield_clause")?;
15143    if !(0..clause.named_child_count()).any(|index| {
15144        clause
15145            .named_child(index)
15146            .is_some_and(|child| child.kind() == "ERROR")
15147    }) {
15148        return None;
15149    }
15150    let mut recovered = Vec::new();
15151    let mut stack = vec![clause];
15152    while let Some(current) = stack.pop() {
15153        if current.kind() == "assignment_expression"
15154            && let Some(left) = current.child_by_field_name("left")
15155            && extract_variable_name(left, source).is_some()
15156        {
15157            recovered.push(left);
15158            break;
15159        }
15160        let mut cursor = current.walk();
15161        stack.extend(current.named_children(&mut cursor));
15162    }
15163    if recovered.is_empty() {
15164        return None;
15165    }
15166    let mut cursor = node.walk();
15167    recovered.extend(
15168        node.children_by_field_name("declarator", &mut cursor)
15169            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
15170    );
15171    Some(recovered)
15172}
15173
15174/// Recover a macro-qualified constructor that tree-sitter represents as one
15175/// field declaration. The constructor call remains inside the direct recovery
15176/// error, while each member initializer becomes a false function declarator.
15177/// The class owner proves the constructor name and lets the caller ignore those
15178/// initializer declarators.
15179fn recovered_macro_qualified_constructor_call<'tree>(
15180    node: Node<'tree>,
15181    class_name: &str,
15182    source: &str,
15183) -> Option<Node<'tree>> {
15184    if node.kind() != "field_declaration" {
15185        return None;
15186    }
15187    let macro_type = node.child_by_field_name("type")?;
15188    if macro_type.kind() != "type_identifier"
15189        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15190    {
15191        return None;
15192    }
15193    let mut cursor = node.walk();
15194    let bitfield = node
15195        .named_children(&mut cursor)
15196        .find(|child| child.kind() == "bitfield_clause")?;
15197    let error = bitfield
15198        .named_child(0)
15199        .filter(|child| child.kind() == "ERROR")?;
15200    let mut stack = vec![error];
15201    while let Some(current) = stack.pop() {
15202        if current.kind() == "call_expression"
15203            && current
15204                .child_by_field_name("function")
15205                .is_some_and(|function| node_text(function, source) == class_name)
15206            && current
15207                .child_by_field_name("arguments")
15208                .is_some_and(|arguments| arguments.kind() == "argument_list")
15209        {
15210            return Some(current);
15211        }
15212        let mut cursor = current.walk();
15213        stack.extend(current.named_children(&mut cursor));
15214    }
15215    None
15216}
15217
15218/// Recover a macro-qualified member function declaration that tree-sitter
15219/// represents as a pseudo-field. An object-like export macro before a qualified
15220/// return type can displace the namespace and type into an ERROR/bitfield
15221/// recovery, leaving the callable as a structured `call_expression`.
15222///
15223/// The caller must route this shape before ordinary declarator classification;
15224/// otherwise the displaced namespace identifier is published as a field.
15225fn recovered_macro_qualified_function_call<'tree>(
15226    node: Node<'tree>,
15227    source: &str,
15228) -> Option<Node<'tree>> {
15229    if node.kind() != "field_declaration" {
15230        return None;
15231    }
15232    let macro_type = node.child_by_field_name("type")?;
15233    if macro_type.kind() != "type_identifier"
15234        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15235    {
15236        return None;
15237    }
15238    let declarator = node.child_by_field_name("declarator")?;
15239    if declarator.kind() != "field_identifier" {
15240        return None;
15241    }
15242    let mut cursor = node.walk();
15243    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15244    if !named.iter().any(|child| {
15245        child.kind() == "storage_class_specifier"
15246            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
15247    }) {
15248        return None;
15249    }
15250    let bitfield = named
15251        .iter()
15252        .find(|child| child.kind() == "bitfield_clause")?;
15253    let mut bitfield_cursor = bitfield.walk();
15254    let payload = bitfield
15255        .named_children(&mut bitfield_cursor)
15256        .collect::<Vec<_>>();
15257    let [displaced_error, call] = payload.as_slice() else {
15258        return None;
15259    };
15260    if displaced_error.kind() != "ERROR"
15261        || displaced_error.named_child_count() != 1
15262        || displaced_error
15263            .named_child(0)
15264            .is_none_or(|child| child.kind() != "identifier")
15265        || call.kind() != "call_expression"
15266        || call
15267            .child_by_field_name("function")
15268            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
15269        || call
15270            .child_by_field_name("arguments")
15271            .is_none_or(|arguments| arguments.kind() != "argument_list")
15272    {
15273        return None;
15274    }
15275    Some(*call)
15276}
15277
15278fn recovered_macro_qualified_function_parameters(
15279    arguments: Node<'_>,
15280    source: &str,
15281) -> Option<(String, Vec<String>)> {
15282    if arguments.kind() != "argument_list" {
15283        return None;
15284    }
15285    let mut cursor = arguments.walk();
15286    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
15287    if named.is_empty() {
15288        return Some(("()".to_string(), Vec::new()));
15289    }
15290    let mut types = Vec::new();
15291    let mut labels = Vec::new();
15292    let mut index = 0;
15293    while index < named.len() {
15294        let parameter_type = named[index];
15295        let parameter_name = named.get(index + 1).copied()?;
15296        if !matches!(
15297            parameter_type.kind(),
15298            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
15299        ) || parameter_name.kind() != "ERROR"
15300            || parameter_name.named_child_count() != 1
15301            || parameter_name
15302                .named_child(0)
15303                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
15304        {
15305            return None;
15306        }
15307        let parameter_name = parameter_name.named_child(0)?;
15308        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
15309        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
15310        index += 2;
15311    }
15312    Some((format!("({})", types.join(", ")), labels))
15313}
15314
15315/// Recognize the phantom field tree-sitter emits for a macro-qualified
15316/// function return type.  For example,
15317/// `static API result_type ThresholdForSmallA() { ... }` can become a
15318/// `field_declaration` (`API` as the type and `result_type` as a field name)
15319/// followed by a clean `function_definition` for `ThresholdForSmallA`.
15320///
15321/// Keep this predicate entirely tied to the CST envelope: the type must be an
15322/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
15323/// the declaration must carry a missing semicolon rather than a real one, and
15324/// the immediate named sibling must expose a function declarator.  A real
15325/// macro-decorated field with an explicit semicolon therefore remains a field.
15326pub fn recovered_macro_return_type_node<'tree>(
15327    node: Node<'tree>,
15328    source: &str,
15329) -> Option<Node<'tree>> {
15330    if node.kind() != "field_declaration" {
15331        return None;
15332    }
15333    let macro_type = node.child_by_field_name("type")?;
15334    if macro_type.kind() != "type_identifier"
15335        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15336    {
15337        return None;
15338    }
15339    let declarator = node.child_by_field_name("declarator")?;
15340    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
15341        return None;
15342    }
15343    let mut has_missing_semicolon = false;
15344    let mut has_real_semicolon = false;
15345    for child in children_iter(node) {
15346        if child.kind() != ";" {
15347            continue;
15348        }
15349        if child.is_missing() {
15350            has_missing_semicolon = true;
15351        } else {
15352            has_real_semicolon = true;
15353        }
15354    }
15355    if !has_missing_semicolon || has_real_semicolon {
15356        return None;
15357    }
15358    let mut next = node.next_named_sibling();
15359    while next.is_some_and(|sibling| sibling.kind() == "comment") {
15360        next = next.and_then(|sibling| sibling.next_named_sibling());
15361    }
15362    let next = next?;
15363    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
15364        return None;
15365    }
15366    let function_declarator = next.child_by_field_name("declarator")?;
15367    extract_function_declarator(function_declarator).map(|_| declarator)
15368}
15369
15370/// Whether `name` is a type parameter of a template declaration that lexically
15371/// encloses `node`. The malformed macro-return field uses the parameter name as
15372/// its pseudo-declarator; preserving that field is necessary to publish a
15373/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
15374/// ancestors instead of interpreting source text so nested templates and
15375/// parser-recovered regions retain their real lexical scopes.
15376pub(crate) fn cpp_active_template_type_parameter<'tree>(
15377    node: Node<'tree>,
15378    name: &str,
15379    source: &str,
15380    ancestry: &ParentIndex<'tree>,
15381) -> bool {
15382    let mut ancestor = ancestry.parent(node);
15383    while let Some(current) = ancestor {
15384        if current.kind() == "template_declaration"
15385            && let Some(parameters) = current.child_by_field_name("parameters")
15386        {
15387            let mut cursor = parameters.walk();
15388            if parameters.named_children(&mut cursor).any(|parameter| {
15389                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
15390                    && cpp_template_parameter_name(parameter, source)
15391                        .is_some_and(|parameter_name| parameter_name == name)
15392            }) {
15393                return true;
15394            }
15395        }
15396        ancestor = ancestry.parent(current);
15397    }
15398    false
15399}
15400
15401/// Reparse the region `[start, end)` of `source` as C++, confined to the region
15402/// via included ranges so every reparsed node keeps its original byte offset and
15403/// line number. The existing visitors read node text from the original source,
15404/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
15405/// `parse_rust_region_tree` technique.
15406fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
15407    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
15408}
15409
15410fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
15411    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
15412        return None;
15413    }
15414    let semicolon = node.next_sibling()?;
15415    if semicolon.kind() != ";" || semicolon.is_missing() {
15416        return None;
15417    }
15418    let row = node.start_position().row;
15419    let mut start = node.start_byte();
15420    let mut sibling = node.prev_sibling();
15421    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
15422        if previous.kind() == ";" {
15423            break;
15424        }
15425        start = previous.start_byte();
15426        sibling = previous.prev_sibling();
15427    }
15428    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
15429}
15430
15431/// One `RET name _(( args ));`-style K&R prototype-macro invocation
15432/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
15433/// byte spans a clean reparse needs, in original-source order.
15434struct PrototypeMacroCandidate {
15435    /// Where the return type and declared name begin.
15436    run_start: usize,
15437    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
15438    /// exclusive end of the first reparse span.
15439    identifier_start: usize,
15440    /// The inner `(`'s start byte: the start of the second reparse span.
15441    /// This paren is kept so the reparse sees exactly one parameter-list
15442    /// paren pair.
15443    inner_open_start: usize,
15444    /// Byte just past the inner `)`: the end of the second reparse span.
15445    inner_close_end: usize,
15446    /// Byte just past the outer `)`: the start of the third reparse span.
15447    outer_close_end: usize,
15448    /// Byte just past the terminating `;`: the end of the third reparse
15449    /// span, and of the whole candidate.
15450    semicolon_end: usize,
15451}
15452
15453impl PrototypeMacroCandidate {
15454    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
15455    /// one included-range tree: the return type and name, the parenthesized
15456    /// argument list, and the terminating `;`. The macro token and its outer
15457    /// wrapping parenthesis are omitted -- deleting them is exactly what the
15458    /// (absent) preprocessor macro expansion would do.
15459    fn ranges(&self) -> [(usize, usize); 3] {
15460        [
15461            (self.run_start, self.identifier_start),
15462            (self.inner_open_start, self.inner_close_end),
15463            (self.outer_close_end, self.semicolon_end),
15464        ]
15465    }
15466}
15467
15468/// A live terminating semicolon owned directly by `node`.
15469fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
15470    node.child(node.child_count().checked_sub(1)?)
15471        .filter(|child| child.kind() == ";" && !child.is_missing())
15472}
15473
15474/// The known pre-ANSI prototype macros whose expansion is exactly their one
15475/// argument. The malformed CST cannot prove that an arbitrary identifier has
15476/// that definition, so unknown spellings fail closed rather than turning an
15477/// ordinary broken declaration into a Function (issue #2932).
15478fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
15479    matches!(
15480        node.kind(),
15481        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
15482    ) && matches!(
15483        normalize_cpp_whitespace(node_text(node, source)).as_str(),
15484        "_" | "__P" | "OF" | "PROTO"
15485    )
15486}
15487
15488/// Read tree-sitter's recovery for `declared_name MACRO`: a
15489/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
15490/// declared name, and whose `name` is a known prototype macro.
15491fn cpp_prototype_macro_qualified_parts<'tree>(
15492    node: Node<'tree>,
15493    source: &str,
15494) -> Option<(Node<'tree>, Node<'tree>)> {
15495    if node.kind() != "qualified_identifier" {
15496        return None;
15497    }
15498    let declared_name = node
15499        .child_by_field_name("scope")
15500        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
15501    let macro_name = macro_decorated_unqualified_name(node)?;
15502    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
15503}
15504
15505/// The inner parenthesized node of tree-sitter's structured
15506/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
15507/// the macro invocation's outer parentheses while retaining the one pair an
15508/// ordinary function declarator needs. No delimiter search is involved: both
15509/// pairs and their ownership come from the CST.
15510fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
15511    if arguments.kind() != "argument_list"
15512        || arguments.named_child_count() != 1
15513        || arguments.child_count() != 3
15514        || arguments
15515            .child(0)
15516            .is_none_or(|open| open.kind() != "(" || open.is_missing())
15517        || arguments
15518            .child(2)
15519            .is_none_or(|close| close.kind() != ")" || close.is_missing())
15520    {
15521        return None;
15522    }
15523    let inner = arguments.named_child(0)?;
15524    let close_index = match inner.kind() {
15525        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
15526        // tree-sitter completes the C++ cast production with a zero-width
15527        // value after the live close parenthesis.
15528        "cast_expression" => inner.child_count().checked_sub(2)?,
15529        _ => return None,
15530    };
15531    (inner
15532        .child(0)
15533        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
15534        && inner
15535            .child(close_index)
15536            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
15537    .then_some(inner)
15538}
15539
15540fn cpp_prototype_macro_candidate_from_init_declaration(
15541    declaration: Node<'_>,
15542    source: &str,
15543) -> Option<PrototypeMacroCandidate> {
15544    let init = declaration
15545        .child_by_field_name("declarator")
15546        .filter(|declarator| declarator.kind() == "init_declarator")?;
15547    let malformed_declarator = init.child_by_field_name("declarator")?;
15548    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
15549        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
15550    } else {
15551        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
15552            return None;
15553        }
15554        let declared_name_error = init
15555            .prev_named_sibling()
15556            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
15557        let declared_name = declared_name_error
15558            .named_child(0)
15559            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
15560        (declared_name, malformed_declarator)
15561    };
15562    let arguments = init
15563        .child_by_field_name("value")
15564        .filter(|value| value.kind() == "argument_list")?;
15565    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15566    let semicolon = cpp_direct_semicolon(declaration)?;
15567    let return_type = declaration.child_by_field_name("type")?;
15568    if return_type.end_byte() > declared_name.start_byte()
15569        || declared_name.end_byte() > macro_name.start_byte()
15570        || macro_name.end_byte() > arguments.start_byte()
15571        || arguments.end_byte() > semicolon.start_byte()
15572    {
15573        return None;
15574    }
15575    Some(PrototypeMacroCandidate {
15576        run_start: declaration.start_byte(),
15577        identifier_start: macro_name.start_byte(),
15578        inner_open_start: inner.start_byte(),
15579        inner_close_end: inner.end_byte(),
15580        outer_close_end: arguments.end_byte(),
15581        semicolon_end: semicolon.end_byte(),
15582    })
15583}
15584
15585fn cpp_prototype_macro_candidate_from_qualified_declaration(
15586    declaration: Node<'_>,
15587    source: &str,
15588) -> Option<PrototypeMacroCandidate> {
15589    let qualified = declaration
15590        .child_by_field_name("declarator")
15591        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
15592    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15593    let open_error = qualified
15594        .next_named_sibling()
15595        .filter(|next| next.kind() == "ERROR")?;
15596    let close_error = last_named_child(declaration)
15597        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
15598    if open_error.child_count() < 3
15599        || open_error
15600            .child(0)
15601            .is_none_or(|open| open.kind() != "(" || open.is_missing())
15602        || open_error
15603            .child(1)
15604            .is_none_or(|open| open.kind() != "(" || open.is_missing())
15605        || close_error.child_count() != 2
15606        || close_error
15607            .child(0)
15608            .is_none_or(|close| close.kind() != ")" || close.is_missing())
15609        || close_error
15610            .child(1)
15611            .is_none_or(|close| close.kind() != ")" || close.is_missing())
15612    {
15613        return None;
15614    }
15615    let inner_open = open_error.child(1)?;
15616    let inner_close = close_error.child(0)?;
15617    let outer_close = close_error.child(1)?;
15618    let semicolon = cpp_direct_semicolon(declaration)?;
15619    let return_type = declaration.child_by_field_name("type")?;
15620    if return_type.end_byte() > qualified.start_byte()
15621        || macro_name.end_byte() > open_error.start_byte()
15622        || inner_open.start_byte() > inner_close.end_byte()
15623        || inner_close.end_byte() > outer_close.start_byte()
15624        || outer_close.end_byte() > semicolon.start_byte()
15625    {
15626        return None;
15627    }
15628    Some(PrototypeMacroCandidate {
15629        run_start: declaration.start_byte(),
15630        identifier_start: macro_name.start_byte(),
15631        inner_open_start: inner_open.start_byte(),
15632        inner_close_end: inner_close.end_byte(),
15633        outer_close_end: outer_close.end_byte(),
15634        semicolon_end: semicolon.end_byte(),
15635    })
15636}
15637
15638fn cpp_prototype_macro_candidate_from_pointer_expression(
15639    statement: Node<'_>,
15640    source: &str,
15641) -> Option<PrototypeMacroCandidate> {
15642    if statement.kind() != "expression_statement"
15643        || statement.named_child_count() != 1
15644        || !statement.has_error()
15645    {
15646        return None;
15647    }
15648    let expansion = statement
15649        .named_child(0)
15650        .filter(|child| child.kind() == "parameter_pack_expansion")?;
15651    let binary = expansion
15652        .child_by_field_name("pattern")
15653        .filter(|pattern| pattern.kind() == "binary_expression")?;
15654    if binary.child_count() != 3
15655        || binary
15656            .child(1)
15657            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
15658        || expansion
15659            .child(expansion.child_count().checked_sub(1)?)
15660            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
15661    {
15662        return None;
15663    }
15664    let return_type = binary.child_by_field_name("left")?;
15665    let call = binary
15666        .child_by_field_name("right")
15667        .filter(|right| right.kind() == "call_expression")?;
15668    let qualified = call.child_by_field_name("function")?;
15669    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15670    let arguments = call
15671        .child_by_field_name("arguments")
15672        .filter(|arguments| arguments.kind() == "argument_list")?;
15673    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15674    let semicolon = cpp_direct_semicolon(statement)?;
15675    if return_type.end_byte() > declared_name.start_byte()
15676        || macro_name.end_byte() > arguments.start_byte()
15677        || arguments.end_byte() > semicolon.start_byte()
15678    {
15679        return None;
15680    }
15681    Some(PrototypeMacroCandidate {
15682        run_start: statement.start_byte(),
15683        identifier_start: macro_name.start_byte(),
15684        inner_open_start: inner.start_byte(),
15685        inner_close_end: inner.end_byte(),
15686        outer_close_end: arguments.end_byte(),
15687        semicolon_end: semicolon.end_byte(),
15688    })
15689}
15690
15691/// Recover only CST shapes whose fields preserve the declaration name, macro
15692/// invocation, nested argument list, and terminator. Arbitrary flattened
15693/// `ERROR` wreckage stays unsupported rather than being interpreted through a
15694/// token or delimiter scan (issue #2932).
15695fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
15696    let candidate = match node.kind() {
15697        "declaration" if node.has_error() => {
15698            cpp_prototype_macro_candidate_from_init_declaration(node, source)
15699                .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
15700        }
15701        "expression_statement" => {
15702            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
15703        }
15704        _ => None,
15705    };
15706    candidate.into_iter().collect()
15707}
15708
15709fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
15710    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
15711        return false;
15712    }
15713    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
15714        return false;
15715    }
15716    let Some(declarator) = (if node.kind() == "function_definition" {
15717        node.child_by_field_name("declarator")
15718            .and_then(extract_function_declarator)
15719    } else {
15720        node.named_child(0)
15721            .filter(|child| child.kind() == "function_declarator")
15722    }) else {
15723        return false;
15724    };
15725    let Some(name) = cpp_function_declarator_name_node(declarator) else {
15726        return false;
15727    };
15728    declarator.start_byte() == node.start_byte()
15729        && name.kind() == "identifier"
15730        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
15731}
15732
15733/// Reparse a fragmented class-body interior while preserving its original byte
15734/// and line offsets, confined to the region by tree-sitter included ranges.
15735///
15736/// This used to materialize the region's whole file prefix as whitespace and
15737/// make the lexer walk it, the technique #1309 replaced on the other reparse
15738/// path: O(file) per fragmented-class recovery, on files that are already
15739/// error-recovered and already slow (#2788). Included ranges give the parser
15740/// the same view -- the region's bytes, at their original offsets and
15741/// line/column positions -- without materializing or lexing anything before it.
15742///
15743/// The equality of the two views is the claim, so a debug build parses both and
15744/// asserts the trees agree node for node.
15745fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15746    let region = cpp_reparse_region_items(source, start, end);
15747
15748    #[cfg(debug_assertions)]
15749    assert_eq!(
15750        region.as_ref().map(cpp_tree_shape),
15751        cpp_reparse_padded_class_body(source, start, end)
15752            .as_ref()
15753            .map(cpp_tree_shape),
15754        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
15755         prefix produces"
15756    );
15757
15758    region
15759}
15760
15761/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
15762/// replaces, kept as the oracle a debug build asserts every region reparse
15763/// against and as the release-mode parity tests' reference (#2788).
15764#[cfg(any(debug_assertions, test))]
15765fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15766    if start >= end {
15767        // The included-range parser refuses an empty region; the padded one
15768        // returned a tree holding nothing, which every caller read as "no
15769        // members here".
15770        return None;
15771    }
15772    let bytes = source.as_bytes();
15773    let prefix = bytes.get(..start)?;
15774    let interior = bytes.get(start..end)?;
15775    let mut padded = Vec::with_capacity(end);
15776    padded.extend(
15777        prefix
15778            .iter()
15779            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
15780    );
15781    padded.extend_from_slice(interior);
15782    let padded = String::from_utf8(padded).ok()?;
15783    let mut parser = Parser::new();
15784    parser
15785        .set_language(&tree_sitter_cpp::LANGUAGE.into())
15786        .ok()?;
15787    parser.parse(&padded, None)
15788}
15789
15790/// Every node of `tree` in preorder, by kind, span and position, which is what
15791/// two reparses of one region have to agree on for their callers to read the
15792/// same declarations out of either (#2788).
15793#[cfg(any(debug_assertions, test))]
15794fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
15795    let mut shape = Vec::new();
15796    let mut cursor = tree.root_node().walk();
15797    let mut stack = vec![tree.root_node()];
15798    while let Some(node) = stack.pop() {
15799        shape.push((
15800            node.kind(),
15801            node.start_byte(),
15802            node.end_byte(),
15803            node.start_position().row,
15804            node.start_position().column,
15805            node.is_named(),
15806            node.is_missing(),
15807        ));
15808        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
15809        stack.extend(children.into_iter().rev());
15810    }
15811    shape
15812}
15813
15814/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
15815/// reparsed interior is indexed only when every top-level named node is a
15816/// well-formed C++ item (or a comment) and at least one real item is present.
15817/// Expression/statement soup surfaces as a top-level `ERROR` or
15818/// `expression_statement`, neither of which is an item kind, so it is rejected.
15819///
15820/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
15821/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
15822/// swallowed by a preceding dangling sentinel) reparses to a real
15823/// `namespace_definition` whose body still holds a bogus `function_definition`,
15824/// so the subtree legitimately carries an error. Container items are admitted
15825/// even with an internal error; the inner bogus function is recovered recursively
15826/// when `visit_function_definition` walks it. Each recursion strips at least one
15827/// leading sentinel, so the region strictly shrinks and recovery terminates.
15828///
15829/// A top-level `function_definition` is the one place we stay strict: it is
15830/// admitted only when it is clean or is itself a sentinel candidate. A function
15831/// that has an error and is not a sentinel is a real callable with a broken body,
15832/// so we refuse the whole reparse and let the ordinary path handle it (preserving
15833/// its real return type rather than re-deriving an implicit one).
15834fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
15835    let mut cursor = root.walk();
15836    let mut saw_item = false;
15837    for child in root.named_children(&mut cursor) {
15838        match child.kind() {
15839            "comment" => {}
15840            "function_definition" => {
15841                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
15842                    return false;
15843                }
15844                saw_item = true;
15845            }
15846            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
15847            _ => return false,
15848        }
15849    }
15850    saw_item
15851}
15852
15853/// Robustness gate for a reparsed fragmented multiple-base export class body
15854/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
15855/// kinds a class body produces when reparsed at translation-unit scope: the
15856/// access-specifier label preceding the first member surfaces as a
15857/// `labeled_statement` wrapping that member, and members surface as
15858/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
15859/// Statement or expression soup surfaces as other top-level kinds and is rejected,
15860/// so only a genuinely member-shaped body is ever re-owned as members; anything
15861/// ambiguous falls back to indexing the class alone.
15862fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
15863    if node.kind() != "ERROR" {
15864        return false;
15865    }
15866    let mut stack = Vec::new();
15867    let mut saw_function_declarator = false;
15868    let mut cursor = node.walk();
15869    for child in node.named_children(&mut cursor) {
15870        stack.push(child);
15871    }
15872    while let Some(current) = stack.pop() {
15873        match current.kind() {
15874            // Tree-sitter may wrap adjacent copy-control declarations in a
15875            // nested ERROR. Keep descending only through ERROR wrappers; the
15876            // actual declaration payload must be a function_declarator.
15877            "ERROR" => {
15878                let mut cursor = current.walk();
15879                stack.extend(current.named_children(&mut cursor));
15880            }
15881            "function_declarator" => saw_function_declarator = true,
15882            _ => return false,
15883        }
15884    }
15885    saw_function_declarator
15886}
15887
15888fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
15889    if node.kind() != "ERROR" {
15890        return false;
15891    }
15892    let mut cursor = node.walk();
15893    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15894    let [explicit, constructor_error, destructor] = named.as_slice() else {
15895        return false;
15896    };
15897    let Some(constructor) = constructor_error.named_child(0) else {
15898        return false;
15899    };
15900    let Some(constructor_name) =
15901        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
15902    else {
15903        return false;
15904    };
15905    let Some(destructor_name) =
15906        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
15907    else {
15908        return false;
15909    };
15910    let Some(destroyed_type) = destructor_name.named_child(0) else {
15911        return false;
15912    };
15913    explicit.kind() == "explicit_function_specifier"
15914        && constructor_error.kind() == "ERROR"
15915        && constructor_error.named_child_count() == 1
15916        && constructor.kind() == "function_declarator"
15917        && constructor_name.kind() == "identifier"
15918        && destructor.kind() == "function_declarator"
15919        && destructor_name.kind() == "destructor_name"
15920        && destroyed_type.kind() == "identifier"
15921        && node_text(constructor_name, source) == node_text(destroyed_type, source)
15922}
15923
15924fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
15925    if node.kind() != "compound_statement" {
15926        return false;
15927    }
15928    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
15929        return false;
15930    };
15931    if prefix.kind() == "labeled_statement"
15932        && prefix.named_child(0).is_some_and(|label| {
15933            matches!(
15934                node_text(label, source).trim(),
15935                "public" | "private" | "protected"
15936            )
15937        })
15938    {
15939        return prefix.named_children(&mut prefix.walk()).any(|child| {
15940            child.kind() == "declaration"
15941                && child.has_error()
15942                && child
15943                    .named_children(&mut child.walk())
15944                    .any(cpp_reparsed_member_error_is_indexable)
15945        });
15946    }
15947    // A malformed constructor initializer can be split into a declaration
15948    // followed by its compound body when the class prefix already contains
15949    // realistic members. Keep this admission tied to that exact structured
15950    // declaration/error/body chain rather than accepting arbitrary blocks.
15951    prefix.kind() == "declaration"
15952        && prefix.has_error()
15953        && prefix
15954            .named_children(&mut prefix.walk())
15955            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
15956}
15957
15958fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
15959    if !cpp_reparsed_member_error_is_indexable(node) {
15960        return false;
15961    }
15962    let Some(preproc) = node.next_named_sibling() else {
15963        return false;
15964    };
15965    preproc.kind() == "preproc_if"
15966        && preproc.has_error()
15967        && preproc
15968            .named_children(&mut preproc.walk())
15969            .any(|child| child.kind() == "expression_statement" && child.has_error())
15970        && preproc
15971            .next_named_sibling()
15972            .is_some_and(|body| body.kind() == "compound_statement")
15973}
15974
15975/// Return a function body whose braces and ownership are explicit in the
15976/// reparsed class-member tree. An error below a real function envelope is
15977/// recoverable by the ordinary function visitor; a missing/deferred body is
15978/// not, because accepting it would let statement soup masquerade as a member.
15979fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
15980    if node.kind() != "function_definition" {
15981        return None;
15982    }
15983    let body = node.child_by_field_name("body")?;
15984    if body.kind() != "compound_statement" {
15985        return None;
15986    }
15987    let open = body.child(0)?;
15988    let close = body.child(body.child_count().checked_sub(1)?)?;
15989    if open.kind() != "{"
15990        || open.is_missing()
15991        || close.kind() != "}"
15992        || close.is_missing()
15993        || close.end_byte() != body.end_byte()
15994        || body.end_byte() != node.end_byte()
15995    {
15996        return None;
15997    }
15998    Some(body)
15999}
16000
16001fn cpp_reparsed_member_function_errors_are_in_body(
16002    node: Node<'_>,
16003    body: Node<'_>,
16004    source: &str,
16005) -> bool {
16006    let mut cursor = node.walk();
16007    node.children(&mut cursor).all(|child| {
16008        same_node(child, body)
16009            || cpp_reparsed_member_attribute_error(child, source)
16010            || cpp_reparsed_member_signature_identifier_errors(child)
16011            || (!child.has_error() && !child.is_error() && !child.is_missing())
16012    })
16013}
16014
16015/// A complete callable can still carry parser errors in its signature when a
16016/// project annotation is not part of the C++ grammar (`nonneg int`,
16017/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
16018/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
16019/// leaves inside the already-proven callable envelope; structured statements,
16020/// literals, missing tokens, and other malformed signature payload remain
16021/// rejected.
16022fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
16023    if !node.has_error() && !node.is_error() && !node.is_missing() {
16024        return false;
16025    }
16026    let mut stack = vec![node];
16027    let mut saw_error = false;
16028    while let Some(current) = stack.pop() {
16029        if current.is_missing() {
16030            return false;
16031        }
16032        if current.kind() == "ERROR" {
16033            saw_error = true;
16034            let mut cursor = current.walk();
16035            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
16036            if children
16037                .iter()
16038                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
16039            {
16040                return false;
16041            }
16042            stack.extend(children);
16043            continue;
16044        }
16045        let mut cursor = current.walk();
16046        stack.extend(current.children(&mut cursor));
16047    }
16048    saw_error
16049}
16050
16051fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
16052    node.kind() == "ERROR"
16053        && node.named_child_count() == 1
16054        && node.named_child(0).is_some_and(|attribute| {
16055            attribute.kind() == "identifier"
16056                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
16057        })
16058}
16059
16060/// A C++ attribute placed between a member's declarator and body can make
16061/// tree-sitter expose the callable as
16062/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
16063/// Keep this admission tied to that exact node geometry. In particular, an
16064/// arbitrary ERROR or identifier before a compound statement is not enough.
16065fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
16066    let Some(body) = cpp_reparsed_member_function_body(node) else {
16067        return false;
16068    };
16069    let mut cursor = node.walk();
16070    let named = node
16071        .named_children(&mut cursor)
16072        .filter(|child| child.kind() != "comment")
16073        .collect::<Vec<_>>();
16074    let [type_node, error, attribute, body_node] = named.as_slice() else {
16075        return false;
16076    };
16077    if !same_node(*body_node, body)
16078        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16079        || attribute.kind() != "identifier"
16080        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16081        || error.kind() != "ERROR"
16082        || error.named_child_count() != 1
16083    {
16084        return false;
16085    }
16086    error
16087        .named_child(0)
16088        .is_some_and(cpp_reparsed_attribute_callable_declarator)
16089}
16090
16091fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
16092    cpp_structured_type_path(node, source).is_some()
16093        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
16094}
16095
16096fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16097    let Some(body) = cpp_reparsed_member_function_body(node) else {
16098        return false;
16099    };
16100    let mut cursor = node.walk();
16101    let named = node
16102        .named_children(&mut cursor)
16103        .filter(|child| child.kind() != "comment")
16104        .collect::<Vec<_>>();
16105    let [friend, return_error, declarator, body_node] = named.as_slice() else {
16106        return false;
16107    };
16108    let Some(return_type) = return_error.named_child(0) else {
16109        return false;
16110    };
16111    same_node(*body_node, body)
16112        && friend.kind() == "type_identifier"
16113        && node_text(*friend, source) == "friend"
16114        && return_error.kind() == "ERROR"
16115        && return_error.named_child_count() == 1
16116        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16117        && extract_function_declarator(*declarator)
16118            .and_then(cpp_function_declarator_name_node)
16119            .is_some()
16120}
16121
16122fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16123    let Some(body) = cpp_reparsed_member_function_body(node) else {
16124        return false;
16125    };
16126    let mut cursor = node.walk();
16127    let named = node
16128        .named_children(&mut cursor)
16129        .filter(|child| child.kind() != "comment")
16130        .collect::<Vec<_>>();
16131    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
16132        return false;
16133    };
16134    let Some(return_type) = return_error.named_child(0) else {
16135        return false;
16136    };
16137    same_node(*body_node, body)
16138        && prefix
16139            .iter()
16140            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
16141        && attribute.kind() == "type_identifier"
16142        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16143        && return_error.kind() == "ERROR"
16144        && return_error.named_child_count() == 1
16145        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16146        && extract_function_declarator(*declarator)
16147            .and_then(cpp_function_declarator_name_node)
16148            .is_some()
16149}
16150
16151/// An included-range reparse that begins inside a malformed class can merge an
16152/// access label and following template member. Tree-sitter then emits the label
16153/// as the `template_type` name, the template parameter list as its arguments,
16154/// an ERROR-wrapped return type, the callable declarator, and its complete
16155/// body. Admit only that exact structured displacement.
16156fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16157    let Some(body) = cpp_reparsed_member_function_body(node) else {
16158        return false;
16159    };
16160    let mut cursor = node.walk();
16161    let named = node
16162        .named_children(&mut cursor)
16163        .filter(|child| child.kind() != "comment")
16164        .collect::<Vec<_>>();
16165    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
16166        return false;
16167    };
16168    let Some(template_name) = template_type.child_by_field_name("name") else {
16169        return false;
16170    };
16171    let Some(arguments) = template_type.child_by_field_name("arguments") else {
16172        return false;
16173    };
16174    let Some(return_type) = return_error.named_child(0) else {
16175        return false;
16176    };
16177    let mut cursor = template_type.walk();
16178    let template_errors = template_type
16179        .named_children(&mut cursor)
16180        .filter(|child| child.kind() == "ERROR")
16181        .collect::<Vec<_>>();
16182    let [comment_error] = template_errors.as_slice() else {
16183        return false;
16184    };
16185    let mut cursor = comment_error.walk();
16186    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
16187    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
16188        return false;
16189    };
16190    same_node(*body_node, body)
16191        && template_type.kind() == "template_type"
16192        && template_name.kind() == "type_identifier"
16193        && matches!(
16194            node_text(template_name, source).trim(),
16195            "public" | "private" | "protected"
16196        )
16197        && arguments.kind() == "template_argument_list"
16198        && arguments.named_child_count() > 0
16199        && !arguments.has_error()
16200        && !colon.is_named()
16201        && colon.kind() == ":"
16202        && comments.iter().all(|child| child.kind() == "comment")
16203        && !template_keyword.is_named()
16204        && template_keyword.kind() == "template"
16205        && return_error.kind() == "ERROR"
16206        && return_error.named_child_count() == 1
16207        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16208        && extract_function_declarator(*declarator)
16209            .and_then(cpp_function_declarator_name_node)
16210            .is_some()
16211}
16212
16213/// Return the constructor declaration tree-sitter can merge into an access
16214/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
16215/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
16216/// declaration's apparent type; the callable name must still exactly match the
16217/// recovered class, so unrelated labeled statements are never re-owned.
16218fn cpp_reparsed_preprocessor_constructor<'tree>(
16219    node: Node<'tree>,
16220    class_name: &str,
16221    source: &str,
16222) -> Option<Node<'tree>> {
16223    if node.kind() != "labeled_statement" {
16224        return None;
16225    }
16226    let mut cursor = node.walk();
16227    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16228    let [label, directive_error, declaration] = named.as_slice() else {
16229        return None;
16230    };
16231    if label.kind() != "statement_identifier"
16232        || !matches!(
16233            node_text(*label, source),
16234            "public" | "private" | "protected"
16235        )
16236        || directive_error.kind() != "ERROR"
16237        || directive_error.child_count() != 1
16238        || directive_error
16239            .child(0)
16240            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
16241        || declaration.kind() != "declaration"
16242        || declaration.named_child_count() != 2
16243    {
16244        return None;
16245    }
16246    let apparent_type = declaration.child_by_field_name("type")?;
16247    if apparent_type.kind() != "type_identifier"
16248        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
16249    {
16250        return None;
16251    }
16252    let declarator = declaration.child_by_field_name("declarator")?;
16253    let function = extract_function_declarator(declarator)?;
16254    let name = cpp_function_declarator_name_node(function)?;
16255    (node_text(name, source) == class_name).then_some(*declaration)
16256}
16257
16258fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
16259    if extract_function_declarator(node)
16260        .and_then(cpp_function_declarator_name_node)
16261        .is_some()
16262    {
16263        return true;
16264    }
16265    node.kind() == "init_declarator"
16266        && node
16267            .child_by_field_name("declarator")
16268            .is_some_and(|declarator| declarator.kind() == "identifier")
16269        && node
16270            .child_by_field_name("value")
16271            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
16272}
16273
16274/// Return true for the constrained/attribute form that tree-sitter splits into
16275/// an ERROR declaration, a preprocessor `requires` clause, and a following
16276/// compound statement. The three nodes must remain immediate named siblings;
16277/// this deliberately does not search source text or skip unrelated statements.
16278fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
16279    if node.kind() != "ERROR" || node.named_child_count() != 3 {
16280        return false;
16281    }
16282    let mut cursor = node.walk();
16283    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16284    let [type_node, function_declarator, attribute] = named.as_slice() else {
16285        return false;
16286    };
16287    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16288        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
16289        || attribute.kind() != "identifier"
16290        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16291    {
16292        return false;
16293    }
16294    let Some(preproc) =
16295        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16296    else {
16297        return false;
16298    };
16299    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
16300        .filter(|sibling| sibling.kind() == "compound_statement")
16301    else {
16302        return false;
16303    };
16304    let Some(open) = body.child(0) else {
16305        return false;
16306    };
16307    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
16308        return false;
16309    };
16310    let Some(condition) = preproc.child_by_field_name("condition") else {
16311        return false;
16312    };
16313    let mut cursor = preproc.walk();
16314    let payload = preproc
16315        .named_children(&mut cursor)
16316        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
16317        .collect::<Vec<_>>();
16318    let [requires_statement] = payload.as_slice() else {
16319        return false;
16320    };
16321    let requires_clause = requires_statement.named_child(0);
16322
16323    open.kind() == "{"
16324        && !open.is_missing()
16325        && close.kind() == "}"
16326        && !close.is_missing()
16327        && close.end_byte() == body.end_byte()
16328        && requires_statement.kind() == "expression_statement"
16329        && requires_statement.named_child_count() == 1
16330        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
16331}
16332
16333fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16334    let mut sibling = node.next_named_sibling();
16335    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16336        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
16337    }
16338    sibling
16339}
16340
16341fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16342    let mut sibling = node.prev_named_sibling();
16343    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16344        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
16345    }
16346    sibling
16347}
16348
16349fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
16350    let Some(preproc) =
16351        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16352    else {
16353        return false;
16354    };
16355    let Some(error) =
16356        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
16357    else {
16358        return false;
16359    };
16360    cpp_reparsed_attribute_requires_error(error, source)
16361}
16362
16363fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
16364    node: Node<'tree>,
16365    source: &str,
16366) -> Option<Node<'tree>> {
16367    if node.kind() != "ERROR" {
16368        return None;
16369    }
16370    let mut cursor = node.walk();
16371    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16372    let [parameter, macro_name, message] = named.as_slice() else {
16373        return None;
16374    };
16375    let parameter_name = parameter.named_child(0)?;
16376    (parameter.kind() == "type_parameter_declaration"
16377        && parameter_name.kind() == "type_identifier"
16378        && macro_name.kind() == "type_identifier"
16379        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16380        && message.kind() == "string_literal")
16381        .then_some(parameter_name)
16382}
16383
16384/// Recognize the alternate constraint-macro prefix where tree-sitter retains
16385/// the complete qualified constraint as a fourth child instead of moving it
16386/// into the following function. Keep the gate tied to a two-type template
16387/// constraint that names the declared type parameter.
16388fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
16389    node: Node<'tree>,
16390    source: &str,
16391) -> Option<Node<'tree>> {
16392    if node.kind() != "ERROR" {
16393        return None;
16394    }
16395    let mut cursor = node.walk();
16396    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16397    let [parameter, macro_name, message, constraint] = named.as_slice() else {
16398        return None;
16399    };
16400    let parameter_name = parameter.named_child(0)?;
16401    let constraint_scope = constraint.child_by_field_name("scope")?;
16402    let constraint_template = constraint.child_by_field_name("name")?;
16403    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16404    let mut argument_cursor = constraint_arguments.walk();
16405    let constraint_types = constraint_arguments
16406        .named_children(&mut argument_cursor)
16407        .collect::<Vec<_>>();
16408    if parameter.kind() != "type_parameter_declaration"
16409        || parameter_name.kind() != "type_identifier"
16410        || macro_name.kind() != "type_identifier"
16411        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16412        || message.kind() != "string_literal"
16413        || constraint.kind() != "qualified_identifier"
16414        || constraint_scope.kind() != "namespace_identifier"
16415        || !matches!(
16416            constraint_template.kind(),
16417            "template_function" | "template_type"
16418        )
16419        || !matches!(constraint_types.as_slice(), [left, right]
16420            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16421        || constraint_arguments.has_error()
16422    {
16423        return None;
16424    }
16425    let parameter_text = node_text(parameter_name, source);
16426    let mut stack = constraint_types;
16427    while let Some(current) = stack.pop() {
16428        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
16429            return Some(parameter_name);
16430        }
16431        let mut cursor = current.walk();
16432        stack.extend(current.named_children(&mut cursor));
16433    }
16434    None
16435}
16436
16437fn cpp_reparsed_template_macro_companion_is_indexable(
16438    node: Node<'_>,
16439    parameter_name: Node<'_>,
16440    source: &str,
16441) -> bool {
16442    let Some(body) = cpp_reparsed_member_function_body(node) else {
16443        return false;
16444    };
16445    let mut cursor = node.walk();
16446    let named = node
16447        .named_children(&mut cursor)
16448        .filter(|child| child.kind() != "comment")
16449        .collect::<Vec<_>>();
16450    let [
16451        constraint,
16452        close_error,
16453        storage,
16454        return_error,
16455        declarator,
16456        body_node,
16457    ] = named.as_slice()
16458    else {
16459        return false;
16460    };
16461    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
16462        return false;
16463    };
16464    let Some(constraint_template) = constraint.child_by_field_name("name") else {
16465        return false;
16466    };
16467    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
16468        return false;
16469    };
16470    let Some(return_type) = return_error.named_child(0) else {
16471        return false;
16472    };
16473    let mut cursor = constraint_arguments.walk();
16474    let constraint_types = constraint_arguments
16475        .named_children(&mut cursor)
16476        .collect::<Vec<_>>();
16477    same_node(*body_node, body)
16478        && constraint.kind() == "qualified_identifier"
16479        && constraint_scope.kind() == "namespace_identifier"
16480        && constraint_template.kind() == "template_type"
16481        && matches!(constraint_types.as_slice(), [left, right]
16482            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16483        && !constraint_arguments.has_error()
16484        && close_error.kind() == "ERROR"
16485        && close_error.named_child_count() == 0
16486        && storage.kind() == "storage_class_specifier"
16487        && return_error.kind() == "ERROR"
16488        && return_error.named_child_count() == 1
16489        && return_type.kind() == "identifier"
16490        && node_text(return_type, source) == node_text(parameter_name, source)
16491        && extract_function_declarator(*declarator)
16492            .and_then(cpp_function_declarator_name_node)
16493            .is_some()
16494}
16495
16496fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
16497    node: Node<'tree>,
16498    parameter_name: Node<'_>,
16499    source: &str,
16500) -> Option<Node<'tree>> {
16501    let body = cpp_reparsed_member_function_body(node)?;
16502    let constraint = node.child_by_field_name("type")?;
16503    let constraint_template = constraint.child_by_field_name("name")?;
16504    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16505    let mut argument_cursor = constraint_arguments.walk();
16506    let constraint_types = constraint_arguments
16507        .named_children(&mut argument_cursor)
16508        .collect::<Vec<_>>();
16509    if constraint.kind() != "qualified_identifier"
16510        || constraint_template.kind() != "template_type"
16511        || !matches!(constraint_types.as_slice(), [left, right]
16512            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16513        || constraint_arguments.has_error()
16514        || node
16515            .child_by_field_name("body")
16516            .is_none_or(|candidate| !same_node(candidate, body))
16517    {
16518        return None;
16519    }
16520
16521    let mut cursor = node.walk();
16522    let recovery_errors = node
16523        .named_children(&mut cursor)
16524        .filter(|child| child.kind() == "ERROR")
16525        .collect::<Vec<_>>();
16526    if !recovery_errors
16527        .iter()
16528        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
16529        || !recovery_errors.iter().all(|error| {
16530            error.named_child_count() == 0
16531                || cpp_reparsed_constraint_macro_error(*error, source)
16532                || (error.named_child_count() == 1
16533                    && error
16534                        .named_child(0)
16535                        .is_some_and(|child| child.kind() == "function_declarator"))
16536        })
16537    {
16538        return None;
16539    }
16540
16541    let parameter_text = node_text(parameter_name, source);
16542    let mut declarators = node
16543        .child_by_field_name("declarator")
16544        .and_then(extract_function_declarator)
16545        .into_iter()
16546        .collect::<Vec<_>>();
16547    for error in recovery_errors {
16548        let mut stack = vec![error];
16549        while let Some(current) = stack.pop() {
16550            if current.kind() == "function_declarator" {
16551                declarators.push(current);
16552            }
16553            let mut cursor = current.walk();
16554            stack.extend(current.named_children(&mut cursor));
16555        }
16556    }
16557    declarators.into_iter().find(|declarator| {
16558        cpp_function_declarator_name_node(*declarator)
16559            .is_some_and(|name| name.kind() == "identifier")
16560            && declarator
16561                .child_by_field_name("parameters")
16562                .is_some_and(|parameters| {
16563                    parameters
16564                        .named_children(&mut parameters.walk())
16565                        .filter_map(|parameter| parameter.child_by_field_name("type"))
16566                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
16567                })
16568    })
16569}
16570
16571fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
16572    node: Node<'_>,
16573    parameter_name: Node<'_>,
16574    source: &str,
16575) -> bool {
16576    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
16577}
16578
16579fn cpp_reparsed_template_macro_function_companion_is_indexable(
16580    node: Node<'_>,
16581    parameter_name: Node<'_>,
16582    source: &str,
16583) -> bool {
16584    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
16585        return false;
16586    }
16587    let Some(return_type) = node.child_by_field_name("type") else {
16588        return false;
16589    };
16590    let Some(function_declarator) = node
16591        .child_by_field_name("declarator")
16592        .and_then(extract_function_declarator)
16593    else {
16594        return false;
16595    };
16596    if cpp_function_declarator_name_node(function_declarator).is_none()
16597        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
16598    {
16599        return false;
16600    }
16601    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
16602        return false;
16603    };
16604    let parameter_text = node_text(parameter_name, source);
16605    parameters
16606        .named_children(&mut parameters.walk())
16607        .any(|parameter| {
16608            parameter
16609                .child_by_field_name("type")
16610                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
16611        })
16612}
16613
16614fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
16615    if node.kind() != "ERROR" {
16616        return false;
16617    }
16618    let mut stack = vec![node];
16619    while let Some(current) = stack.pop() {
16620        let macro_shape = match current.kind() {
16621            "call_expression" => current
16622                .child_by_field_name("function")
16623                .zip(current.child_by_field_name("arguments")),
16624            "init_declarator" => current
16625                .child_by_field_name("declarator")
16626                .zip(current.child_by_field_name("value")),
16627            _ => None,
16628        };
16629        if let Some((name, arguments)) = macro_shape
16630            && name.kind() == "identifier"
16631            && arguments.kind() == "argument_list"
16632            && arguments.named_child_count() >= 2
16633            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
16634        {
16635            return true;
16636        }
16637        let mut cursor = current.walk();
16638        stack.extend(current.named_children(&mut cursor));
16639    }
16640    false
16641}
16642
16643fn cpp_recovered_template_macro_constructor<'tree>(
16644    node: Node<'tree>,
16645    source: &str,
16646) -> Option<(Node<'tree>, Node<'tree>)> {
16647    let mut prefix = node.prev_named_sibling()?;
16648    while prefix.kind() == "comment" {
16649        prefix = prefix.prev_named_sibling()?;
16650    }
16651    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
16652    let parameter = parameter_name
16653        .parent()
16654        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
16655    let declarator =
16656        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
16657    Some((declarator, parameter))
16658}
16659
16660fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
16661    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
16662        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16663            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
16664                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
16665                    function,
16666                    parameter_name,
16667                    source,
16668                )
16669        });
16670    }
16671    let Some(parameter_name) =
16672        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
16673    else {
16674        return false;
16675    };
16676    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16677        cpp_reparsed_template_macro_function_companion_is_indexable(
16678            function,
16679            parameter_name,
16680            source,
16681        )
16682    })
16683}
16684
16685fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16686    let function_name = node
16687        .child_by_field_name("declarator")
16688        .and_then(extract_function_declarator)
16689        .and_then(cpp_function_declarator_name_node);
16690    if let Some(body) = cpp_reparsed_member_function_body(node)
16691        && function_name.is_some()
16692        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
16693    {
16694        return true;
16695    }
16696    cpp_reparsed_attribute_member_function(node, source)
16697        || cpp_reparsed_friend_function_is_indexable(node, source)
16698        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
16699        || cpp_reparsed_access_template_function_is_indexable(node, source)
16700        || cpp_recovered_template_macro_constructor(node, source).is_some()
16701}
16702
16703/// Recognize the three top-level nodes produced when an unknown attribute
16704/// macro separates an inline member's declarator from its body in a reparsed
16705/// class interior: an errorful declaration with a missing semicolon, the macro
16706/// call expression, and the complete compound body. Their adjacency and exact
16707/// structured shapes prove one recoverable member envelope; arbitrary calls or
16708/// blocks do not pass this gate.
16709fn cpp_reparsed_macro_attribute_member_sequence(
16710    children: &[Node<'_>],
16711    index: usize,
16712    source: &str,
16713) -> bool {
16714    let Some(prefix) = children.get(index).copied() else {
16715        return false;
16716    };
16717    let declaration = if prefix.kind() == "labeled_statement" {
16718        prefix
16719            .named_child(prefix.named_child_count().saturating_sub(1))
16720            .filter(|child| child.kind() == "declaration")
16721    } else {
16722        (prefix.kind() == "declaration").then_some(prefix)
16723    };
16724    let Some(declaration) = declaration else {
16725        return false;
16726    };
16727    if !declaration.has_error()
16728        || declaration
16729            .child_by_field_name("declarator")
16730            .and_then(extract_function_declarator)
16731            .and_then(cpp_function_declarator_name_node)
16732            .is_none()
16733    {
16734        return false;
16735    }
16736    let Some(attribute_statement) = children.get(index + 1).copied() else {
16737        return false;
16738    };
16739    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
16740        .then(|| attribute_statement.named_child(0))
16741        .flatten()
16742        .filter(|child| child.kind() == "call_expression")
16743    else {
16744        return false;
16745    };
16746    let Some(attribute_name) = attribute_call
16747        .child_by_field_name("function")
16748        .filter(|function| function.kind() == "identifier")
16749        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
16750    else {
16751        return false;
16752    };
16753    if !cpp_export_macro_token(&attribute_name) {
16754        return false;
16755    }
16756    let Some(body) = children.get(index + 2).copied() else {
16757        return false;
16758    };
16759    body.kind() == "compound_statement"
16760        && body.child(0).is_some_and(|open| open.kind() == "{")
16761        && body
16762            .child(body.child_count().saturating_sub(1))
16763            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
16764        && declaration.end_byte() <= attribute_statement.start_byte()
16765        && attribute_statement.end_byte() <= body.start_byte()
16766}
16767
16768/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
16769/// types and specifiers followed by a declarator, over and over, with nothing
16770/// left over. A string-argument attribute macro is what strands them there, and
16771/// the walk indexes exactly what this reads, so admitting the region is safe
16772/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
16773/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
16774/// the whole class body.
16775fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
16776    if node.kind() != "ERROR" {
16777        return false;
16778    }
16779    let run = stranded_declaration_run(node, source);
16780    run.complete && !run.declarations.is_empty()
16781}
16782
16783fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
16784    let mut cursor = root.walk();
16785    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
16786    let mut saw_member = false;
16787    let mut index = 0;
16788    while index < children.len() {
16789        let child = children[index];
16790        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
16791            saw_member = true;
16792            index += 3;
16793            continue;
16794        }
16795        if let Some(recovered) = fragmented_class_body(child, source) {
16796            let Some(tree) = cpp_reparse_fragmented_class_body(
16797                source,
16798                recovered.body.reparse_start,
16799                recovered.body.reparse_end,
16800            ) else {
16801                return false;
16802            };
16803            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
16804                return false;
16805            }
16806            saw_member = true;
16807            index += 1;
16808            while index < children.len()
16809                && children[index].end_byte() <= recovered.body.class_range.end_byte
16810            {
16811                index += 1;
16812            }
16813            continue;
16814        }
16815        match child.kind() {
16816            "comment" => {}
16817            "labeled_statement" => saw_member = true,
16818            "function_definition" => {
16819                if child.has_error()
16820                    && !cpp_reparsed_member_function_is_indexable(child, source)
16821                    && cpp_sentinel_macro_region(child, source).is_none()
16822                {
16823                    return false;
16824                }
16825                saw_member = true;
16826            }
16827            // The attribute a string-argument macro leaves as a call statement
16828            // of its own. It declares nothing; the member it decorated is the
16829            // sibling after it, checked on its own turn.
16830            "expression_statement" if is_string_attribute_macro_statement(child) => {}
16831            "ERROR"
16832                if (cpp_reparsed_member_error_is_indexable(child)
16833                    || cpp_reparsed_adjacent_copy_control_error(child, source)
16834                    || cpp_reparsed_stranded_member_error(child, source))
16835                    && (child
16836                        .next_named_sibling()
16837                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
16838                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
16839            {
16840                saw_member = true;
16841            }
16842            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
16843                saw_member = true;
16844            }
16845            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
16846                saw_member = true;
16847            }
16848            "expression_statement"
16849                if cpp_is_stray_semicolon(child, source)
16850                    && child.prev_named_sibling().is_some_and(|error| {
16851                        cpp_reparsed_member_error_is_indexable(error)
16852                            || cpp_reparsed_adjacent_copy_control_error(error, source)
16853                            || cpp_reparsed_stranded_member_error(error, source)
16854                    }) =>
16855            {
16856                saw_member = true;
16857            }
16858            "compound_statement"
16859                if cpp_reparsed_constructor_body_is_indexable(child, source)
16860                    || cpp_reparsed_attribute_requires_body(child, source) =>
16861            {
16862                saw_member = true;
16863            }
16864            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
16865            _ => return false,
16866        }
16867        index += 1;
16868    }
16869    saw_member
16870}
16871
16872/// Detect the malformed constructor shape that tree-sitter exposes as an
16873/// access-label statement followed by initializer-looking declarations. The
16874/// declarations are not class members: visiting their `location(loc)` and
16875/// `string(s)` function declarators would publish synthetic functions. The
16876/// export-class fallback keeps the original sibling nodes and therefore avoids
16877/// this parser artifact. The returned range identifies the real constructor
16878/// header, which can be reparsed independently as a structured declarator.
16879fn cpp_reparsed_synthetic_initializer_constructor_range(
16880    root: Node<'_>,
16881    class_name: &str,
16882    source: &str,
16883    constructor_end: usize,
16884) -> Option<std::ops::Range<usize>> {
16885    let mut stack = {
16886        let mut cursor = root.walk();
16887        root.named_children(&mut cursor).collect::<Vec<_>>()
16888    };
16889    while let Some(current) = stack.pop() {
16890        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
16891            current,
16892            class_name,
16893            source,
16894            constructor_end,
16895        ) {
16896            return Some(range);
16897        }
16898        if current.kind() == "ERROR" {
16899            let mut cursor = current.walk();
16900            stack.extend(current.named_children(&mut cursor));
16901        }
16902    }
16903    None
16904}
16905
16906/// Recover an inline constructor that a function-like export macro makes
16907/// tree-sitter merge with the following overload. In the reparsed class-body
16908/// region, the access label wraps one declaration whose ERROR contains the
16909/// constructor declarator and its base-initializer/body, while the declaration's
16910/// ordinary declarator is the following overload. Every boundary below comes
16911/// from that CST; no source syntax is reparsed by hand.
16912fn cpp_reparsed_merged_inline_constructor<'tree>(
16913    root: Node<'tree>,
16914    class_name: &str,
16915    source: &str,
16916) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
16917    let mut stack = vec![root];
16918    while let Some(current) = stack.pop() {
16919        if current.kind() != "labeled_statement" {
16920            let mut cursor = current.walk();
16921            stack.extend(current.named_children(&mut cursor));
16922            continue;
16923        }
16924        let declaration = current
16925            .named_children(&mut current.walk())
16926            .find(|child| child.kind() == "declaration")?;
16927        if declaration
16928            .child_by_field_name("type")
16929            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
16930        {
16931            continue;
16932        }
16933        let following = declaration
16934            .child_by_field_name("declarator")
16935            .and_then(extract_function_declarator)
16936            .and_then(cpp_function_declarator_name_node);
16937        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
16938            continue;
16939        }
16940        let mut declaration_cursor = declaration.walk();
16941        let Some(error) = declaration
16942            .named_children(&mut declaration_cursor)
16943            .find(|child| child.kind() == "ERROR")
16944        else {
16945            continue;
16946        };
16947        let mut error_cursor = error.walk();
16948        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
16949        let Some(constructor) = error_children.iter().copied().find(|child| {
16950            child.kind() == "function_declarator"
16951                && cpp_function_declarator_name_node(*child)
16952                    .is_some_and(|name| node_text(name, source).trim() == class_name)
16953        }) else {
16954            continue;
16955        };
16956        let Some(body) = error_children.iter().copied().find_map(|child| {
16957            (child.kind() == "init_declarator")
16958                .then(|| child.child_by_field_name("value"))
16959                .flatten()
16960                .filter(|value| value.kind() == "initializer_list")
16961        }) else {
16962            continue;
16963        };
16964        if constructor.end_byte() > body.start_byte() {
16965            continue;
16966        }
16967        return Some((constructor.start_byte()..body.end_byte(), body));
16968    }
16969    None
16970}
16971
16972fn cpp_reparsed_synthetic_initializer_constructor(
16973    node: Node<'_>,
16974    class_name: &str,
16975    source: &str,
16976    constructor_end: usize,
16977) -> Option<std::ops::Range<usize>> {
16978    if node.kind() != "labeled_statement" {
16979        return None;
16980    }
16981    let mut cursor = node.walk();
16982    let named = node
16983        .named_children(&mut cursor)
16984        .filter(|child| child.kind() != "comment")
16985        .collect::<Vec<_>>();
16986    let label = named.first()?;
16987    if label.kind() != "statement_identifier"
16988        || !matches!(
16989            node_text(*label, source).trim(),
16990            "public" | "private" | "protected"
16991        )
16992    {
16993        return None;
16994    }
16995    let call_error_index = named.iter().position(|child| {
16996        if child.kind() != "ERROR" {
16997            return false;
16998        }
16999        let mut stack = vec![*child];
17000        while let Some(current) = stack.pop() {
17001            if current.kind() == "call_expression"
17002                && current
17003                    .child_by_field_name("function")
17004                    .is_some_and(|function| {
17005                        function.kind() == "identifier"
17006                            && node_text(function, source).trim() == class_name
17007                    })
17008            {
17009                return true;
17010            }
17011            let mut cursor = current.walk();
17012            stack.extend(current.named_children(&mut cursor));
17013        }
17014        false
17015    })?;
17016    let constructor_call = {
17017        let mut stack = vec![named[call_error_index]];
17018        let mut found = None;
17019        while let Some(current) = stack.pop() {
17020            if current.kind() == "call_expression"
17021                && current
17022                    .child_by_field_name("function")
17023                    .is_some_and(|function| {
17024                        function.kind() == "identifier"
17025                            && node_text(function, source).trim() == class_name
17026                    })
17027            {
17028                found = Some(current);
17029                break;
17030            }
17031            let mut cursor = current.walk();
17032            stack.extend(current.named_children(&mut cursor));
17033        }
17034        found
17035    };
17036    let constructor_call = constructor_call?;
17037    named.iter().skip(call_error_index + 1).find(|child| {
17038        child.kind() == "declaration" && child.has_error() && {
17039            let mut cursor = child.walk();
17040            child.named_children(&mut cursor).any(|declarator| {
17041                declarator.kind() == "init_declarator"
17042                    && declarator
17043                        .child_by_field_name("declarator")
17044                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
17045                    && declarator
17046                        .child_by_field_name("value")
17047                        .is_some_and(|value| value.kind() == "initializer_list")
17048            })
17049        }
17050    })?;
17051    Some(constructor_call.start_byte()..constructor_end)
17052}
17053
17054fn cpp_reparsed_exact_constructor_declarator<'tree>(
17055    root: Node<'tree>,
17056    start: usize,
17057    class_name: &str,
17058    source: &str,
17059) -> Option<Node<'tree>> {
17060    let mut candidate = None;
17061    let mut stack = vec![root];
17062    while let Some(current) = stack.pop() {
17063        if current.kind() == "function_declarator"
17064            && current.start_byte() == start
17065            && cpp_function_declarator_name_node(current)
17066                .is_some_and(|name| node_text(name, source).trim() == class_name)
17067        {
17068            if candidate.is_some() {
17069                return None;
17070            }
17071            candidate = Some(current);
17072            continue;
17073        }
17074        let mut cursor = current.walk();
17075        stack.extend(current.named_children(&mut cursor));
17076    }
17077    candidate
17078}
17079
17080fn cpp_is_indexable_item_kind(kind: &str) -> bool {
17081    matches!(
17082        kind,
17083        "namespace_definition"
17084            | "class_specifier"
17085            | "struct_specifier"
17086            | "union_specifier"
17087            | "enum_specifier"
17088            | "function_definition"
17089            | "template_declaration"
17090            | "declaration"
17091            | "field_declaration"
17092            | "alias_declaration"
17093            | "static_assert_declaration"
17094            | "type_definition"
17095            | "using_declaration"
17096            | "linkage_specification"
17097            | "preproc_def"
17098            | "preproc_function_def"
17099            | "preproc_include"
17100            | "preproc_if"
17101            | "preproc_ifdef"
17102            | "preproc_call"
17103    )
17104}
17105
17106#[cfg(test)]
17107mod tests {
17108    use super::*;
17109    use crate::adapter::parse_cpp_file;
17110    use brokk_bifrost_core::analyzer::parsed_file::{
17111        finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
17112        start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
17113    };
17114    use std::fmt::Write;
17115
17116    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
17117        let mut parser = tree_sitter::Parser::new();
17118        parser
17119            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17120            .unwrap();
17121        let tree = parser.parse(source, None).unwrap();
17122        let file = ProjectFile::new(std::env::temp_dir(), name);
17123        parse_cpp_file(&file, source, &tree)
17124    }
17125
17126    #[test]
17127    fn displaced_terminator_ignores_nested_initializer_endif() {
17128        let source = "#ifdef ENABLE_ITEMS\n\
17129struct Item { int reg; };\n\
17130static const struct Item items[] = {{1}};\n\
17131static const struct Item extras[] = {\n\
17132  {0},\n\
17133#ifndef REDUCED\n\
17134  {1},\n\
17135#endif\n\
17136};\n\
17137int read_item(int i) { return items[i].reg; }\n\
17138#endif\n";
17139        let mut parser = tree_sitter::Parser::new();
17140        parser
17141            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17142            .expect("C++ grammar");
17143        let tree = parser.parse(source, None).expect("fixture tree");
17144        let conditional = tree
17145            .root_node()
17146            .named_child(0)
17147            .filter(|node| node.kind() == "preproc_ifdef")
17148            .expect("outer conditional");
17149
17150        assert!(conditional.has_error());
17151        assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
17152        assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
17153    }
17154
17155    #[test]
17156    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
17157        let source = "struct Image { PyObject_HEAD Imaging image; };";
17158        let parsed = parse_cpp_declarations(source, "image.h");
17159        let names = parsed
17160            .declarations()
17161            .iter()
17162            .map(|unit| unit.fq_name())
17163            .collect::<Vec<_>>();
17164
17165        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
17166        assert!(
17167            names.iter().all(|name| name != "Image.Imaging"),
17168            "the pseudo-declarator must not become a field: {names:#?}"
17169        );
17170
17171        let pointer = parse_cpp_declarations(
17172            "struct Image { PyObject_HEAD Imaging *image; };",
17173            "image-pointer.h",
17174        );
17175        let pointer_names = pointer
17176            .declarations()
17177            .iter()
17178            .map(|unit| unit.fq_name())
17179            .collect::<Vec<_>>();
17180        assert!(
17181            pointer_names.iter().any(|name| name == "Image.image"),
17182            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
17183        );
17184        assert!(
17185            pointer_names.iter().all(|name| name != "Image.Imaging"),
17186            "the pointer recovery error must not become a field: {pointer_names:#?}"
17187        );
17188
17189        let unrelated_macro = parse_cpp_declarations(
17190            "struct Image { OTHER_HEAD Imaging other; };",
17191            "image-near-miss.h",
17192        );
17193        let unrelated_names = unrelated_macro
17194            .declarations()
17195            .iter()
17196            .map(|unit| unit.fq_name())
17197            .collect::<Vec<_>>();
17198        assert!(
17199            unrelated_names.iter().all(|name| name != "Image.other"),
17200            "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
17201        );
17202    }
17203
17204    #[test]
17205    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
17206        let source = r#"namespace testing {
17207namespace internal {
17208    namespace detail {
17209        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
17210        public:
17211            int value() const { return count_ + 1; }
17212        private:
17213            int count_;
17214        };
17215        class GTEST_API_ [[nodiscard]] OtherReporter {
17216        public:
17217            int value() const { return count_ + 2; }
17218        private:
17219            int count_;
17220        };
17221    }
17222
17223    template <typename T>
17224    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
17225
17226    class TailReporter {};
17227}
17228}
17229"#;
17230        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
17231        let declarations = parsed.declarations();
17232        let mut parser = tree_sitter::Parser::new();
17233        parser
17234            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17235            .unwrap();
17236        let tree = parser.parse(source, None).unwrap();
17237        let tail_start = source.find("TailReporter").expect("tail class");
17238        let tail_node = tree
17239            .root_node()
17240            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
17241            .expect("tail class AST node");
17242        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
17243        assert!(
17244            tree.root_node().has_error(),
17245            "the malformed class must exercise recovery"
17246        );
17247        assert!(index.region_at(tail_start).is_some());
17248        assert_eq!(
17249            index.enclosing_namespace_components(tail_node, source),
17250            ["testing", "internal"]
17251        );
17252        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
17253        let mut recovered_parsed = ParsedFile::new(String::new());
17254        let class_unit = CodeUnit::new_fq(
17255            file.clone(),
17256            CodeUnitType::Class,
17257            "testing",
17258            "ScopedFakeTestPartResultReporter",
17259            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
17260        );
17261        let scope = ScopeInfo {
17262            package_name: "testing".to_string(),
17263            module: None,
17264            class_unit: Some(class_unit),
17265            template_signature: Some("<typename T>".to_string()),
17266            template_metadata: Some(CppTemplateMetadata {
17267                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
17268                primary_fq_name: String::new(),
17269                parameters: Vec::new(),
17270                specialization_arguments: Vec::new(),
17271                alias_target: None,
17272            }),
17273            declarations_are_fields: true,
17274            recovered_specialization_member_scope: true,
17275            visible_using_namespaces: Vec::new(),
17276        };
17277        let mut visitor = CppVisitor {
17278            file: &file,
17279            source,
17280            parsed: &mut recovered_parsed,
17281            c_tag_semantics: false,
17282            recovered_class_sibling_scopes: HashMap::default(),
17283            consumed_fragment_regions: Vec::new(),
17284            orphaned_namespaces: &index,
17285            partitioned_regions: Vec::new(),
17286            namespace_forward_scans: HashMap::default(),
17287            field_owners: None,
17288            recovery_captures: Vec::new(),
17289            object_macro_fields: HashMap::default(),
17290            ambiguous_object_macro_fields: HashSet::default(),
17291        };
17292        let recovered = visitor
17293            .recovered_namespace_scope(tail_node, &scope)
17294            .expect("the tail must use the stolen namespace scope");
17295        assert_eq!(recovered.package_name, "testing::internal");
17296        assert!(
17297            recovered.class_unit.is_none(),
17298            "recovered namespace declarations cannot retain the malformed class owner"
17299        );
17300        assert!(recovered.template_signature.is_none());
17301        assert!(recovered.template_metadata.is_none());
17302        assert!(!recovered.declarations_are_fields);
17303        assert!(!recovered.recovered_specialization_member_scope);
17304        assert!(
17305            declarations
17306                .iter()
17307                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
17308            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
17309        );
17310        assert!(
17311            declarations
17312                .iter()
17313                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
17314            "the recovered free function remains in its namespace: {declarations:#?}"
17315        );
17316        assert!(
17317            declarations
17318                .iter()
17319                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
17320            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
17321        );
17322        assert!(
17323            declarations.iter().all(|unit| {
17324                !unit
17325                    .short_name()
17326                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
17327            }),
17328            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
17329        );
17330        assert!(
17331            declarations
17332                .iter()
17333                .all(|unit| !unit.identifier().is_empty()),
17334            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
17335        );
17336    }
17337
17338    #[test]
17339    fn object_like_field_macros_materialize_owner_specific_declarations() {
17340        let source = r#"#define PUBLIC_FIELDS int public_value;
17341#define PRIVATE_FIELDS int private_value;
17342#define NOT_A_FIELD_LIST not a declaration
17343
17344struct First {
17345  PUBLIC_FIELDS
17346  PRIVATE_FIELDS
17347};
17348struct Second {
17349  PUBLIC_FIELDS
17350  NOT_A_FIELD_LIST
17351};
17352#undef PUBLIC_FIELDS
17353struct Third {
17354  PUBLIC_FIELDS
17355};
17356"#;
17357        let parsed = parse_cpp_declarations(source, "macro-fields.c");
17358        let fields = parsed
17359            .declarations()
17360            .iter()
17361            .filter(|unit| unit.is_field())
17362            .map(|unit| unit.fq_name())
17363            .collect::<Vec<_>>();
17364
17365        assert!(
17366            fields.contains(&"First.public_value".to_string()),
17367            "{fields:?}"
17368        );
17369        assert!(
17370            fields.contains(&"First.private_value".to_string()),
17371            "{fields:?}"
17372        );
17373        assert!(
17374            fields.contains(&"Second.public_value".to_string()),
17375            "{fields:?}"
17376        );
17377        assert!(
17378            !fields.iter().any(|field| field.contains("not_a_field")),
17379            "malformed macro must fail closed: {fields:?}"
17380        );
17381        assert!(
17382            !fields.iter().any(|field| field.starts_with("Third.")),
17383            "undefined macro must fail closed: {fields:?}"
17384        );
17385    }
17386
17387    #[test]
17388    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
17389        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
17390        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
17391        let mut macros = parsed
17392            .declarations()
17393            .iter()
17394            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
17395            .collect::<Vec<_>>();
17396        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
17397
17398        assert_eq!(macros.len(), 2, "{macros:#?}");
17399        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
17400        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
17401        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
17402        assert_eq!(
17403            parsed.declaration_ranges(macros[1])[0].start_byte,
17404            source.rfind("#define VALUE 2").expect("second definition")
17405        );
17406    }
17407
17408    #[test]
17409    fn identifies_export_macro_class_base_displaced_into_declarator() {
17410        let source = r#"#define PROJECT_API_
17411namespace project {
17412namespace internal {
17413template <typename T>
17414class Base {};
17415}
17416template <typename T>
17417class Wrapper;
17418template <>
17419class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
17420}
17421"#;
17422        let mut parser = tree_sitter::Parser::new();
17423        parser
17424            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17425            .unwrap();
17426        let tree = parser.parse(source, None).unwrap();
17427        let start = source.find("internal::Base<int>").expect("base");
17428        let mut base = tree
17429            .root_node()
17430            .descendant_for_byte_range(start, start + 8)
17431            .expect("base syntax");
17432        while base.kind() != "qualified_identifier" {
17433            base = base.parent().expect("qualified base ancestor");
17434        }
17435        assert!(
17436            is_recovered_exported_class_base_type_node(base, source),
17437            "{}",
17438            tree.root_node().to_sexp()
17439        );
17440    }
17441
17442    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
17443        let mut identities = parsed
17444            .declarations()
17445            .iter()
17446            .filter(|unit| unit.is_function())
17447            .map(|unit| {
17448                (
17449                    unit.fq_name(),
17450                    unit.signature().unwrap_or_default().to_string(),
17451                )
17452            })
17453            .collect::<Vec<_>>();
17454        identities.sort();
17455        identities
17456    }
17457
17458    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
17459    /// prototype macros. Each case is parsed independently so a preceding
17460    /// malformed line cannot flatten the next declaration's fields into one
17461    /// cascading `ERROR`; that shape has no structural proof and must fail
17462    /// closed rather than fall back to a token scan. Parameter names are
17463    /// dropped from signatures just as they are for ordinary C prototypes.
17464    #[test]
17465    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
17466        let cases = [
17467            (
17468                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
17469                "pg_typemap_fit_to_result",
17470                "(VALUE, VALUE)",
17471            ),
17472            (
17473                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
17474                "pg_typemap_result_value",
17475                "(t_typemap *, VALUE, int, int)",
17476            ),
17477            (
17478                "void pg_typemap_mark _(( void * ));",
17479                "pg_typemap_mark",
17480                "(void *)",
17481            ),
17482            (
17483                "void init_pg_type_map _(( void ));",
17484                "init_pg_type_map",
17485                "(void)",
17486            ),
17487            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
17488            (
17489                "extern VALUE pg_extern _(( VALUE, VALUE ));",
17490                "pg_extern",
17491                "(VALUE, VALUE)",
17492            ),
17493            (
17494                "size_t pg_typemap_memsize _(( const void * ));",
17495                "pg_typemap_memsize",
17496                "(const void *)",
17497            ),
17498            (
17499                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
17500                "pg_wrap_socket_io",
17501                "(int, VALUE, VALUE *, int *)",
17502            ),
17503            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
17504            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
17505            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
17506        ];
17507
17508        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
17509            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
17510            assert_eq!(
17511                function_identities(&parsed),
17512                vec![(name.to_string(), signature.to_string())],
17513                "{source}: {:#?}",
17514                parsed.declarations()
17515            );
17516            assert!(
17517                parsed.declarations().iter().all(|unit| !unit.is_field()),
17518                "{source} must not retain the malformed field: {:#?}",
17519                parsed.declarations()
17520            );
17521        }
17522    }
17523
17524    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
17525    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
17526    /// it as a multiplication of a type by a call
17527    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
17528    /// wrapped in an `expression_statement` that `has_error()` only because
17529    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
17530    /// here with a clean preceding field so the prototype remains the
17531    /// parser-owned `expression_statement` this recovery requires.
17532    #[test]
17533    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
17534        let cases = [
17535            (
17536                "PGconn *pg_get_pgconn _(( VALUE ));",
17537                "pg_get_pgconn",
17538                "(VALUE)",
17539            ),
17540            (
17541                "PGresult* pgresult_get _(( VALUE ));",
17542                "pgresult_get",
17543                "(VALUE)",
17544            ),
17545        ];
17546        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
17547            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
17548            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
17549            assert_eq!(
17550                function_identities(&parsed),
17551                vec![(name.to_string(), signature.to_string())],
17552                "{source}: {:#?}",
17553                parsed.declarations()
17554            );
17555            assert_eq!(
17556                parsed
17557                    .declarations()
17558                    .iter()
17559                    .filter(|unit| unit.is_field())
17560                    .map(|unit| unit.fq_name())
17561                    .collect::<Vec<_>>(),
17562                vec!["rb_mPG".to_string()],
17563                "{source}: {:#?}",
17564                parsed.declarations()
17565            );
17566        }
17567    }
17568
17569    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
17570    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
17571    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
17572    /// regions. The witnessed declaration must still become a Function and
17573    /// its type must not survive as the name of a bogus Field. This test does
17574    /// not claim that structurally flattened neighboring prototypes are safe
17575    /// to recover.
17576    #[test]
17577    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
17578        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
17579VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
17580int pg_typemap_fit_to_copy_get                         _(( VALUE ));
17581VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
17582t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
17583VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
17584void pg_typemap_mark                                   _(( void * ));
17585size_t pg_typemap_memsize                              _(( const void * ));
17586void pg_typemap_compact                                _(( void * ));
17587
17588PGconn *pg_get_pgconn                                  _(( VALUE ));
17589t_pg_connection *pg_get_connection                     _(( VALUE ));
17590VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
17591#ifdef __GNUC__
17592__attribute__((format(printf, 3, 4)))
17593#endif
17594NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
17595VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
17596void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
17597
17598
17599VALUE pg_new_result                                    _(( PGresult *, VALUE ));
17600VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
17601PGresult* pgresult_get                                 _(( VALUE ));
17602VALUE pg_result_check                                  _(( VALUE ));
17603VALUE pg_result_clear                                  _(( VALUE ));
17604VALUE pg_tuple_new                                     _(( VALUE, int ));
17605
17606/*
17607 * Fetch the data pointer for the result object
17608 */
17609static inline t_pg_result *
17610pgresult_get_this( VALUE self )
17611{
17612	return RTYPEDDATA_DATA(self);
17613}
17614
17615
17616rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
17617const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
17618rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
17619
17620"#;
17621        let parsed = parse_cpp_declarations(source, "pg.h");
17622        assert!(
17623            function_identities(&parsed)
17624                .iter()
17625                .any(|(name, signature)| name == "pg_typemap_result_value"
17626                    && signature == "(t_typemap *, VALUE, int, int)"),
17627            "the real issue witness must be a Function with its C signature: {:#?}",
17628            parsed.declarations()
17629        );
17630        assert!(
17631            parsed
17632                .declarations()
17633                .iter()
17634                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
17635            "the issue witness must not leave its return type as a Field name: {:#?}",
17636            parsed.declarations()
17637        );
17638    }
17639
17640    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
17641    /// between `explicit` and a constructor name onto the name, producing a
17642    /// `qualified_identifier` whose `::` it had to invent. The declared member
17643    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
17644    /// macro in type position, was already recovered and is the control that
17645    /// both spellings agree.
17646    #[test]
17647    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
17648        let source = r#"class SIMD_4x26 final {
17649   public:
17650      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
17651      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
17652      int m_v;
17653};
17654"#;
17655        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
17656        assert_eq!(
17657            function_identities(&parsed),
17658            vec![
17659                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
17660                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
17661            ],
17662            "{:#?}",
17663            parsed.declarations()
17664        );
17665    }
17666
17667    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
17668    /// `ERROR` holding the wrapped declaration and every declaration after it
17669    /// until the parser recovers. All of them must be indexed, each with the
17670    /// byte range that spells it.
17671    #[test]
17672    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
17673        let source = r#"#include <cstdint>
17674struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
17675    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
17676                     struct llama_model * model,
17677              struct llama_context_params   params),
17678            "use llama_init_from_model instead");
17679    LLAMA_API int32_t llama_tokenize(
17680        const struct llama_vocab * vocab,
17681                      const char * text,
17682                            bool   parse_special);
17683    LLAMA_API int32_t llama_other(int a);
17684"#;
17685        let parsed = parse_cpp_declarations(source, "llama.h");
17686        assert_eq!(
17687            function_identities(&parsed),
17688            vec![
17689                (
17690                    "llama_new_context_with_model".to_string(),
17691                    "(struct llama_model *, struct llama_context_params)".to_string()
17692                ),
17693                ("llama_other".to_string(), "(int)".to_string()),
17694                (
17695                    "llama_tokenize".to_string(),
17696                    "(const struct llama_vocab *, const char *, bool)".to_string()
17697                ),
17698            ],
17699            "{:#?}",
17700            parsed.declarations()
17701        );
17702
17703        // Each recovered declaration owns the source that spells it, so
17704        // navigation lands on the declaration and not on the macro envelope.
17705        for (name, expected) in [
17706            (
17707                "llama_new_context_with_model",
17708                "LLAMA_API struct llama_context * llama_new_context_with_model(",
17709            ),
17710            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
17711            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
17712        ] {
17713            let unit = parsed
17714                .declarations()
17715                .iter()
17716                .find(|unit| unit.is_function() && unit.fq_name() == name)
17717                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
17718            let [range] = parsed.declaration_ranges(unit) else {
17719                panic!("{name} must have exactly one range");
17720            };
17721            let text = &source[range.start_byte..range.end_byte];
17722            assert!(
17723                text.starts_with(expected),
17724                "{name} range is {text:?}, expected it to start with {expected:?}"
17725            );
17726            assert!(
17727                text.ends_with(')') || text.ends_with(';'),
17728                "{name}: {text:?}"
17729            );
17730        }
17731    }
17732
17733    /// whisper.cpp's own `include/whisper.h` writes the same construct with the
17734    /// invocation's `(` at the end of its line, and the parser then keeps no
17735    /// invocation whole. It hands the first one's hint and its own `)` and `;`
17736    /// to the `expression_statement` beside it, and flattens the ones after
17737    /// that into bare `identifier`/`type_identifier`, `(`,
17738    /// `parameter_declaration`, `,` and `ERROR` parts of whatever node it is
17739    /// already inside. No part of that is a head the grouping can be read from,
17740    /// which is why the invocation is read from the token order instead
17741    /// (#3094).
17742    ///
17743    /// The identifiers are `library_*` at whisper's own lengths on purpose:
17744    /// tree-sitter prices error recovery partly by how much input a repair
17745    /// skips, and the same construct written with shorter names parses cleanly.
17746    #[test]
17747    fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
17748        let source = r#"    LIBRARY_DEPRECATED(
17749        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17750        "use library_init_from_file_with_params instead"
17751    );
17752    LIBRARY_DEPRECATED(
17753        LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
17754        "use library_init_from_buffer_with_params instead"
17755    );
17756    LIBRARY_DEPRECATED(
17757        LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
17758        "use library_init_with_params instead"
17759    );
17760"#;
17761        let mut parser = tree_sitter::Parser::new();
17762        parser
17763            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17764            .expect("C++ grammar");
17765        let tree = parser.parse(source, None).expect("C++ tree");
17766        let root = tree.root_node();
17767
17768        // The shape this recovery exists for: the second invocation is not a
17769        // node of its own, and the third is a run of bare siblings inside the
17770        // second one's `ERROR`.
17771        let mut cursor = root.walk();
17772        let items = root.children(&mut cursor).collect::<Vec<_>>();
17773        let [first, hint_statement, rest @ ..] = items.as_slice() else {
17774            panic!("{}", root.to_sexp());
17775        };
17776        assert_eq!(first.kind(), "ERROR");
17777        assert_eq!(hint_statement.kind(), "expression_statement");
17778        assert!(
17779            node_text(*first, source).ends_with(','),
17780            "the first invocation's own `)` and `;` are the statement's, not its own: {}",
17781            root.to_sexp()
17782        );
17783        let swallowing = rest
17784            .iter()
17785            .find(|item| item.kind() == "ERROR")
17786            .unwrap_or_else(|| panic!("{}", root.to_sexp()));
17787        let mut swallowing_cursor = swallowing.walk();
17788        let flattened = swallowing
17789            .children(&mut swallowing_cursor)
17790            .map(|child| child.kind())
17791            .collect::<Vec<_>>();
17792        assert_eq!(
17793            flattened,
17794            vec![
17795                "identifier",
17796                "(",
17797                "parameter_declaration",
17798                ",",
17799                "ERROR",
17800                "type_identifier",
17801                "(",
17802                "parameter_declaration",
17803                ",",
17804                "\"",
17805                "identifier",
17806                "identifier",
17807                "identifier",
17808                "\"",
17809                ")",
17810            ],
17811            "the third invocation must be flattened into the second one's node: {}",
17812            root.to_sexp()
17813        );
17814
17815        // Both are admitted, and each names the byte just past its own `;`.
17816        let ancestry = ParentIndex::new(root);
17817        let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
17818            .expect("the first invocation");
17819        assert_eq!(
17820            &source[..first_run.invocation_end],
17821            &source[..source.find("instead\"\n    );").expect("first hint")
17822                + "instead\"\n    );".len()]
17823        );
17824        assert_eq!(
17825            first_run.region_end, first_run.invocation_end,
17826            "the first invocation swallowed nothing, so the recovery owns only its own bytes"
17827        );
17828        let swallowing_run = collapsed_macro_declaration_run(*swallowing, source, &ancestry)
17829            .expect("the second invocation");
17830        assert!(
17831            swallowing_run.invocation_end < swallowing.end_byte(),
17832            "the second invocation swallowed the third"
17833        );
17834        assert_eq!(
17835            swallowing_run.region_end,
17836            root.end_byte(),
17837            "a swallowing invocation owns the region to the close of its declaration scope"
17838        );
17839
17840        let parsed = parse_cpp_declarations(source, "library.h");
17841        assert_eq!(
17842            function_identities(&parsed),
17843            vec![
17844                (
17845                    "library_init".to_string(),
17846                    "(struct library_model_loader *)".to_string()
17847                ),
17848                (
17849                    "library_init_from_buffer".to_string(),
17850                    "(void *, size_t)".to_string()
17851                ),
17852                (
17853                    "library_init_from_file".to_string(),
17854                    "(const char *)".to_string()
17855                ),
17856            ],
17857            "{:#?}",
17858            parsed.declarations()
17859        );
17860    }
17861
17862    /// The hand-off that keeps the scan finite. An invocation that reaches the
17863    /// end of its own declaration scope swallowed nothing, and the ordinary
17864    /// `macro_wrapped_declarations` reader has it. The scan reparses exactly
17865    /// that region for every invocation it finds, so admitting it here would
17866    /// hand the scan its own input back.
17867    #[test]
17868    fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
17869        let source = r#"LIBRARY_DEPRECATED(
17870        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17871        "use library_init_from_file_with_params instead"
17872    );"#;
17873        let mut parser = tree_sitter::Parser::new();
17874        parser
17875            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17876            .expect("C++ grammar");
17877        let tree = parser.parse(source, None).expect("C++ tree");
17878        let root = tree.root_node();
17879        let head = root.named_child(0).expect("the invocation");
17880        let ancestry = ParentIndex::new(root);
17881        assert!(
17882            collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
17883            "{}",
17884            root.to_sexp()
17885        );
17886        assert!(
17887            !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
17888            "the ordinary reader must be the one that has it: {}",
17889            root.to_sexp()
17890        );
17891        // The index is a speed substitution, never a semantic one: the
17892        // unindexed path is `Node::parent` itself and must agree.
17893        let unindexed = ParentIndex::unindexed();
17894        assert!(
17895            collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
17896            "indexed and unindexed climbs must agree"
17897        );
17898        assert_eq!(
17899            macro_wrapped_declarations(head, source, &ancestry).len(),
17900            macro_wrapped_declarations(head, source, &unindexed).len(),
17901            "indexed and unindexed climbs must agree"
17902        );
17903    }
17904
17905    /// Negative controls for the same recovery: a macro invocation whose
17906    /// arguments are not a declaration recovers nothing, whether the parser
17907    /// keeps it clean, reads the arguments as a type, or reads them as a bare
17908    /// declarator.
17909    #[test]
17910    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
17911        for source in [
17912            "int before;\nFOO(1, 2);\nint after;\n",
17913            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
17914            "int before;\nMACRO(int a, int b);\nint after;\n",
17915            "DECLARE_HANDLE(HWND);\nint after;\n",
17916        ] {
17917            let parsed = parse_cpp_declarations(source, "macro-call.h");
17918            assert_eq!(
17919                function_identities(&parsed),
17920                Vec::new(),
17921                "{source:?} must declare no function: {:#?}",
17922                parsed.declarations()
17923            );
17924        }
17925    }
17926
17927    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
17928    /// makes tree-sitter read the macro as the member's type and its argument
17929    /// list as a parenthesized declarator, which then swallows the attributed
17930    /// member and the member written after it. Both are members.
17931    #[test]
17932    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
17933        let source = r#"#include <string_view>
17934namespace Botan {
17935class DL_Group final {
17936   public:
17937      DL_Group() = default;
17938      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17939      DL_Group(std::string_view pem, int format);
17940      size_t get_p() const;
17941};
17942}
17943"#;
17944        let parsed = parse_cpp_declarations(source, "dl_group.h");
17945        assert_eq!(
17946            function_identities(&parsed),
17947            vec![
17948                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
17949                (
17950                    "Botan.DL_Group.DL_Group".to_string(),
17951                    "(std::string_view)".to_string()
17952                ),
17953                (
17954                    "Botan.DL_Group.DL_Group".to_string(),
17955                    "(std::string_view, int)".to_string()
17956                ),
17957                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17958            ],
17959            "{:#?}",
17960            parsed.declarations()
17961        );
17962    }
17963
17964    /// #2552 shape 3, export-macro class. The class head macro makes the body a
17965    /// `compound_statement`, so members come from the region reparse, and one
17966    /// string-attribute member used to make that reparse unindexable -- which
17967    /// left the class with no members at all.
17968    #[test]
17969    fn an_export_macro_class_keeps_its_string_attribute_members() {
17970        let source = r#"#include <string_view>
17971namespace Botan {
17972class BOTAN_PUBLIC_API(2, 0) DL_Group final {
17973   public:
17974      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17975      DL_Group(std::string_view pem, int format);
17976      size_t get_p() const;
17977};
17978}
17979"#;
17980        let parsed = parse_cpp_declarations(source, "dl_group.h");
17981        assert!(
17982            parsed
17983                .declarations()
17984                .iter()
17985                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
17986            "{:#?}",
17987            parsed.declarations()
17988        );
17989        assert_eq!(
17990            function_identities(&parsed),
17991            vec![
17992                (
17993                    "Botan.DL_Group.DL_Group".to_string(),
17994                    "(std::string_view)".to_string()
17995                ),
17996                (
17997                    "Botan.DL_Group.DL_Group".to_string(),
17998                    "(std::string_view, int)".to_string()
17999                ),
18000                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
18001            ],
18002            "{:#?}",
18003            parsed.declarations()
18004        );
18005    }
18006
18007    /// #2552 shape 3, the `= default` variant plus the access-label
18008    /// constructor. The attributed defaulted constructor separates cleanly, but
18009    /// it strands the next member in a bare `ERROR`, and a constructor written
18010    /// with a member-initializer list under `private:` dissolves into
18011    /// expression soup that the reparse recovers from its own source range.
18012    #[test]
18013    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
18014        let source = r#"namespace Botan {
18015class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
18016   public:
18017      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
18018      XMSS_Parameters(int oid, int len);
18019      size_t len() const;
18020
18021   private:
18022      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
18023            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
18024
18025      int m_oid;
18026      int m_wots_oid;
18027      size_t m_element_size;
18028      size_t m_tree_height;
18029};
18030}
18031"#;
18032        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
18033        let constructors = function_identities(&parsed)
18034            .into_iter()
18035            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
18036            .map(|(_, signature)| signature)
18037            .collect::<Vec<_>>();
18038        assert_eq!(
18039            constructors,
18040            vec![
18041                "()".to_string(),
18042                "(int, int)".to_string(),
18043                "(int, int, size_t, size_t)".to_string(),
18044            ],
18045            "{:#?}",
18046            parsed.declarations()
18047        );
18048    }
18049
18050    /// Negative control for the same rule: a real qualified name spells its
18051    /// `::` in the source, so the separator is present rather than MISSING and
18052    /// the out-of-line definition keeps its owner.
18053    #[test]
18054    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
18055        let source = r#"namespace shell {
18056struct Outer {
18057   struct Inner {
18058      Inner(int v);
18059      void run(int v);
18060   };
18061};
18062Outer::Inner::Inner(int v) {}
18063void Outer::Inner::run(int v) {}
18064}
18065"#;
18066        let parsed = parse_cpp_declarations(source, "outer.cpp");
18067        let names = function_identities(&parsed)
18068            .into_iter()
18069            .map(|(fq_name, _)| fq_name)
18070            .collect::<Vec<_>>();
18071        assert!(
18072            names
18073                .iter()
18074                .all(|name| name.starts_with("shell.Outer$Inner.")),
18075            "{names:#?}"
18076        );
18077    }
18078
18079    #[test]
18080    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
18081        let source = r#"namespace control {
18082template <typename T>
18083class AnySpan;
18084template <typename T>
18085class ABSL_ATTRIBUTE_VIEW AnySpan {
18086 public:
18087  int begin() const;
18088};
18089}
18090
18091namespace absl {
18092ABSL_NAMESPACE_BEGIN
18093template <typename T>
18094class ABSL_ATTRIBUTE_VIEW Span {
18095 public:
18096  int begin() const;
18097  int back() const;
18098};
18099
18100int begin();
18101int back();
18102}
18103"#;
18104        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
18105        let declarations = parsed.declarations();
18106        assert!(
18107            declarations
18108                .iter()
18109                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
18110        );
18111        for method in ["begin", "back"] {
18112            assert!(declarations.iter().any(|unit| {
18113                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
18114            }));
18115            assert!(
18116                declarations.iter().any(|unit| {
18117                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
18118                })
18119            );
18120        }
18121        assert!(
18122            declarations
18123                .iter()
18124                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
18125        );
18126        assert!(
18127            declarations
18128                .iter()
18129                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
18130        );
18131        assert!(
18132            declarations
18133                .iter()
18134                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
18135        );
18136    }
18137
18138    #[test]
18139    fn explicit_global_member_definition_has_canonical_package_boundary() {
18140        let source = r#"
18141namespace arangodb::aql {
18142class ExecutionPlan {
18143 public:
18144  template<class... Args> Node* createNode(Args&&... args);
18145};
18146}
18147
18148template<class... Args>
18149Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
18150"#;
18151        let parsed = parse_cpp_declarations(source, "global-member.cpp");
18152
18153        assert!(parsed.declarations().iter().any(|unit| {
18154            unit.is_function()
18155                && unit.package_name() == "arangodb::aql"
18156                && unit.short_name() == "ExecutionPlan.createNode"
18157                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
18158        }));
18159    }
18160
18161    #[test]
18162    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
18163        let source = r#"
18164#ifndef TINYXML2_INCLUDED
18165#define TINYXML2_INCLUDED
18166namespace tinyxml2 {
18167class TINYXML2_LIB XMLUtil {
18168 public:
18169  static const char* SkipWhiteSpace(const char* p) {
18170    while (*p) {
18171      if (*p == ' ') {
18172        ++p;
18173      }
18174    }
18175    return p;
18176  }
18177  static bool StringEqual(const char* p, const char* q) {
18178    return p == q;
18179  }
18180  class TINYXML2_LIB Helper {
18181   public:
18182    void Touch();
18183  };
18184  static void ToStr(int value, char* buffer);
18185 private:
18186  static const char* writeBoolTrue;
18187};
18188
18189class TINYXML2_LIB XMLNode {
18190 public:
18191  virtual XMLNode* ShallowClone() const = 0;
18192  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
18193};
18194}
18195#endif
18196"#;
18197        let mut parser = tree_sitter::Parser::new();
18198        parser
18199            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18200            .unwrap();
18201        let tree = parser.parse(source, None).unwrap();
18202        let mut boundary_found = false;
18203        walk_named_tree_preorder(tree.root_node(), true, |node| {
18204            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
18205                && name == "XMLUtil"
18206            {
18207                boundary_found = fragmented_export_sibling_class_boundary(
18208                    node,
18209                    source,
18210                    &ParentIndex::unindexed(),
18211                )
18212                .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
18213                .is_some_and(|(_, name, _)| name == "XMLNode");
18214            }
18215            WalkControl::Continue
18216        });
18217        assert!(
18218            boundary_found,
18219            "fixture must exercise the recovered sibling boundary"
18220        );
18221
18222        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
18223        assert!(
18224            parsed
18225                .declarations()
18226                .iter()
18227                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
18228            "{:#?}",
18229            parsed.declarations()
18230        );
18231        assert!(
18232            parsed
18233                .declarations()
18234                .iter()
18235                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
18236            "{:#?}",
18237            parsed.declarations()
18238        );
18239        assert!(parsed.declarations().iter().any(|unit| {
18240            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
18241        }));
18242        assert!(
18243            parsed
18244                .declarations()
18245                .iter()
18246                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
18247        );
18248        assert!(
18249            parsed
18250                .declarations()
18251                .iter()
18252                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
18253        );
18254    }
18255
18256    #[test]
18257    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
18258        // Clang's diagnostic suite intentionally contains this ill-formed
18259        // spelling. The analyzer must retain the parser's explicit-global AST
18260        // boundary instead of constructing `cwg311::::cwg311::X`.
18261        let parsed = parse_cpp_declarations(
18262            r#"
18263namespace cwg311 {
18264namespace X { namespace Y {} }
18265namespace ::cwg311::X {}
18266}
18267"#,
18268            "explicit-global-namespace.cpp",
18269        );
18270
18271        assert!(parsed.declarations().iter().any(|unit| {
18272            unit.kind() == CodeUnitType::Module
18273                && unit.short_name() == "cwg311::X"
18274                && unit.fq_name() == "cwg311::X"
18275        }));
18276        assert!(
18277            parsed
18278                .declarations()
18279                .iter()
18280                .all(|unit| !unit.short_name().contains("::::")),
18281            "recovered namespace names must not retain empty scope components: {:#?}",
18282            parsed.declarations()
18283        );
18284    }
18285
18286    #[test]
18287    fn repeated_scope_separator_does_not_create_empty_function_owner() {
18288        let scope = ScopeInfo {
18289            package_name: "X".to_string(),
18290            module: None,
18291            class_unit: None,
18292            template_signature: None,
18293            template_metadata: None,
18294            declarations_are_fields: false,
18295            recovered_specialization_member_scope: false,
18296            visible_using_namespaces: Vec::new(),
18297        };
18298
18299        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
18300
18301        assert!(owner.is_none());
18302        assert_eq!(name, "doit");
18303        assert_eq!(package, "X");
18304    }
18305
18306    #[test]
18307    fn trailing_decltype_expression_is_not_a_function_declarator() {
18308        let source = r#"
18309namespace boost { namespace detail {
18310#if ! defined(BOOST_NO_SFINAE_EXPR) && \
18311    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
18312    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
18313#define BOOST_THREAD_PROVIDES_INVOKE
18314#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
18315template <class Fp, class A0, class ...Args>
18316inline auto
18317invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
18318       BOOST_THREAD_RV_REF(Args) ...args)
18319    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
18320{
18321    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
18322}
18323#endif
18324#endif
18325}}
18326"#;
18327        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
18328
18329        assert!(
18330            parsed
18331                .declarations()
18332                .iter()
18333                .all(|unit| unit.short_name() != ".*f")
18334        );
18335    }
18336
18337    fn find_class_named<'tree>(
18338        root: Node<'tree>,
18339        source: &str,
18340        expected_name: &str,
18341    ) -> Option<Node<'tree>> {
18342        let mut stack = vec![root];
18343        while let Some(node) = stack.pop() {
18344            if node.kind() == "class_specifier"
18345                && node
18346                    .child_by_field_name("name")
18347                    .is_some_and(|name| node_text(name, source) == expected_name)
18348            {
18349                return Some(node);
18350            }
18351            let mut cursor = node.walk();
18352            stack.extend(node.named_children(&mut cursor));
18353        }
18354        None
18355    }
18356
18357    #[test]
18358    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
18359        let source = r#"EXPORT void definition(struct Value value) {}
18360EXPORT void prototype(struct Value value);
18361"#;
18362        let mut parser = tree_sitter::Parser::new();
18363        parser
18364            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18365            .unwrap();
18366        let tree = parser.parse(source, None).unwrap();
18367        let root = tree.root_node();
18368        let mut cursor = root.walk();
18369        let callables = root
18370            .named_children(&mut cursor)
18371            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
18372            .collect::<Vec<_>>();
18373
18374        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
18375        for callable in callables {
18376            assert!(callable.has_error(), "fixture must exercise error recovery");
18377            assert!(
18378                cpp_sentinel_macro_parts(callable, source).is_none(),
18379                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
18380            );
18381        }
18382    }
18383
18384    #[test]
18385    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
18386        let source = r#"namespace absl {
18387ABSL_NAMESPACE_BEGIN
18388// Generate a floating-point variate conforming to a Beta distribution:
18389template <typename RealType = double>
18390class beta_distribution {
18391 public:
18392  using result_type = RealType;
18393
18394
18395  beta_distribution() : beta_distribution(1) {}
18396
18397  explicit beta_distribution(result_type alpha, result_type beta = 1)
18398      : param_(alpha, beta) {}
18399
18400  explicit beta_distribution(const param_type& p) : param_(p) {}
18401
18402  void reset() {}
18403
18404  // Generating functions
18405  template <typename URBG>
18406  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
18407    return (*this)(g, param_);
18408  }
18409
18410};
18411ABSL_NAMESPACE_END
18412}  // namespace absl
18413"#;
18414        let mut parser = tree_sitter::Parser::new();
18415        parser
18416            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18417            .unwrap();
18418        let tree = parser.parse(source, None).unwrap();
18419        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18420        let body = namespace
18421            .child_by_field_name("body")
18422            .expect("fixture namespace body");
18423        let sentinel = body.named_child(0).expect("sentinel envelope");
18424        let callable = sentinel
18425            .child_by_field_name("declarator")
18426            .and_then(extract_function_declarator)
18427            .and_then(cpp_function_declarator_name_node)
18428            .expect("preserved callable name");
18429
18430        assert_eq!(sentinel.kind(), "function_definition");
18431        assert_eq!(callable.kind(), "operator_name");
18432        assert!(
18433            cpp_sentinel_macro_parts(sentinel, source).is_some(),
18434            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
18435        );
18436    }
18437
18438    #[test]
18439    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
18440        let source = r#"namespace absl {
18441ABSL_NAMESPACE_BEGIN
18442// absl::discrete_distribution
18443//
18444// A discrete distribution produces random integers i, where 0 <= i < n
18445template <typename IntType = int>
18446class discrete_distribution {
18447 public:
18448  using result_type = IntType;
18449  class param_type {
18450   public:
18451    param_type() { init(); }
18452    template <typename InputIterator>
18453    explicit param_type(InputIterator begin, InputIterator end)
18454        : p_(begin, end) {
18455      init();
18456    }
18457  };
18458  discrete_distribution() : param_() {}
18459  explicit discrete_distribution(const param_type& p) : param_(p) {}
18460};
18461ABSL_NAMESPACE_END
18462}  // namespace absl
18463"#;
18464        let mut parser = tree_sitter::Parser::new();
18465        parser
18466            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18467            .unwrap();
18468        let tree = parser.parse(source, None).unwrap();
18469        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18470        let body = namespace
18471            .child_by_field_name("body")
18472            .expect("fixture namespace body");
18473        let sentinel = body.named_child(0).expect("sentinel envelope");
18474        let callable = sentinel
18475            .child_by_field_name("declarator")
18476            .and_then(extract_function_declarator)
18477            .and_then(cpp_function_declarator_name_node)
18478            .expect("preserved callable name");
18479
18480        assert_eq!(sentinel.kind(), "function_definition");
18481        assert_eq!(callable.kind(), "identifier");
18482        assert!(
18483            cpp_sentinel_macro_parts(sentinel, source).is_some(),
18484            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
18485        );
18486    }
18487
18488    #[test]
18489    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
18490        let source = r#"
18491#define CPPCHECKLIB
18492class Library {
18493    struct Container {
18494        CPPCHECKLIB static std::string toString(Yield yield);
18495        CPPCHECKLIB static std::string toString(Action action);
18496    };
18497};
18498"#;
18499        let mut parser = tree_sitter::Parser::new();
18500        parser
18501            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18502            .unwrap();
18503        let tree = parser.parse(source, None).unwrap();
18504        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
18505        let parsed = parse_cpp_file(&file, source, &tree);
18506        assert!(
18507            parsed
18508                .declarations()
18509                .iter()
18510                .all(|unit| unit.fq_name() != "Library$Container.std"),
18511            "the qualified return-type namespace must not become a field: {:#?}",
18512            parsed.declarations()
18513        );
18514        for expected in ["(Yield)", "(Action)"] {
18515            assert!(
18516                parsed.declarations().iter().any(|unit| {
18517                    unit.is_function()
18518                        && unit.fq_name() == "Library$Container.toString"
18519                        && unit.signature() == Some(expected)
18520                }),
18521                "recovered toString overload {expected} is missing: {:#?}",
18522                parsed.declarations()
18523            );
18524        }
18525    }
18526
18527    #[test]
18528    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
18529        let source = r#"
18530#define SIMPLECPP_LIB
18531namespace simplecpp {
18532using TokenString = std::string;
18533struct Location { int line{}; };
18534class SIMPLECPP_LIB Token {
18535  TokenString prefix;
18536  void prefix_method() {}
18537 public:
18538  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18539      whitespaceahead(wsahead), location(loc), string(s)
18540      // The comment must not hide the constructor body from recovery.
18541      {
18542      flags();
18543  }
18544  TokenString string;
18545  bool whitespaceahead;
18546  Location location;
18547  Token *previous{};
18548 private:
18549  void flags() {
18550      whitespaceahead = true;
18551  }
18552};
18553}
18554"#;
18555        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
18556
18557        let location_fields = parsed
18558            .declarations()
18559            .iter()
18560            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
18561            .collect::<Vec<_>>();
18562        assert_eq!(
18563            location_fields.len(),
18564            1,
18565            "location should have one class-owned declaration: {:#?}",
18566            parsed.declarations()
18567        );
18568        assert!(
18569            location_fields[0].is_field(),
18570            "location has wrong kind: {:#?}",
18571            parsed.declarations()
18572        );
18573        assert!(
18574            parsed.declarations().iter().all(|unit| {
18575                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
18576            })
18577        );
18578        assert!(
18579            parsed.declarations().iter().all(|unit| {
18580                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18581            })
18582        );
18583        assert!(
18584            parsed
18585                .declarations()
18586                .iter()
18587                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
18588        );
18589        assert!(
18590            parsed
18591                .declarations()
18592                .iter()
18593                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
18594            "the recovered class must retain its constructor: {:#?}",
18595            parsed.declarations()
18596        );
18597        assert!(
18598            parsed
18599                .declarations()
18600                .iter()
18601                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
18602        );
18603        assert!(parsed.declarations().iter().any(|unit| {
18604            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
18605        }));
18606        let constructor = parsed
18607            .declarations()
18608            .iter()
18609            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
18610            .expect("recovered constructor");
18611        let constructor_start = source.find("Token(const").expect("constructor start");
18612        let constructor_end = source
18613            .get(
18614                ..source
18615                    .find("  TokenString string;")
18616                    .expect("constructor end"),
18617            )
18618            .expect("constructor slice")
18619            .trim_end()
18620            .len();
18621        assert!(
18622            parsed
18623                .navigation_ranges
18624                .get(constructor)
18625                .is_some_and(|ranges| {
18626                    ranges.iter().any(|range| {
18627                        range.start_byte == constructor_start && range.end_byte == constructor_end
18628                    })
18629                }),
18630            "constructor navigation must span the full body: {:#?}",
18631            parsed.navigation_ranges
18632        );
18633        assert_eq!(
18634            parsed
18635                .signature_metadata
18636                .get(constructor)
18637                .and_then(|metadata| metadata.first())
18638                .and_then(SignatureMetadata::callable_linkage),
18639            Some(CallableLinkage::External)
18640        );
18641        let token_class = parsed
18642            .declarations()
18643            .iter()
18644            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
18645            .expect("recovered Token class");
18646        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
18647        assert!(
18648            parsed
18649                .navigation_ranges
18650                .get(token_class)
18651                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
18652            "class navigation must include the terminating semicolon: {:#?}",
18653            parsed.navigation_ranges
18654        );
18655    }
18656
18657    #[test]
18658    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
18659        let source = r#"
18660#define SIMPLECPP_LIB
18661namespace simplecpp {
18662using TokenString = std::string;
18663class Macro;
18664struct Location {
18665  unsigned int fileIndex{};
18666  unsigned int line{};
18667  unsigned int col{};
18668};
18669struct Output {
18670  int type;
18671};
18672class SIMPLECPP_LIB Token {
18673 public:
18674  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18675      whitespaceahead(wsahead), location(loc), string(s) {
18676      flags();
18677  }
18678  Token(const Token &tok) :
18679      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
18680      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
18681      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
18682  Token &operator=(const Token &tok) = delete;
18683  const TokenString& str() const { return string; }
18684  void setstr(const std::string &s) { string = s; flags(); }
18685  bool isOneOf(const char ops[]) const;
18686  TokenString macro;
18687  char op;
18688  bool comment;
18689  bool name;
18690  bool number;
18691  bool whitespaceahead;
18692  Location location;
18693  Token *previous{};
18694  Token *next{};
18695 private:
18696  void flags() {
18697      name = !string.empty();
18698      comment = false;
18699      number = false;
18700      op = 0;
18701  }
18702  TokenString string;
18703};
18704}
18705struct Following {
18706  int type;
18707};
18708class SIMPLECPP_LIB Later {
18709 public:
18710  Later(int value) : value(value) {}
18711  int value;
18712};
18713"#;
18714        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
18715        assert!(
18716            parsed
18717                .declarations()
18718                .iter()
18719                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
18720        );
18721        assert!(
18722            !parsed
18723                .declarations()
18724                .iter()
18725                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
18726        );
18727        assert!(
18728            parsed
18729                .declarations()
18730                .iter()
18731                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
18732        );
18733        assert!(
18734            !parsed
18735                .declarations()
18736                .iter()
18737                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18738        );
18739        assert!(
18740            parsed
18741                .declarations()
18742                .iter()
18743                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
18744        );
18745        assert!(
18746            parsed
18747                .declarations()
18748                .iter()
18749                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
18750        );
18751        assert!(
18752            parsed
18753                .declarations()
18754                .iter()
18755                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
18756        );
18757        assert!(
18758            parsed
18759                .declarations()
18760                .iter()
18761                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
18762        );
18763        assert!(
18764            parsed
18765                .declarations()
18766                .iter()
18767                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
18768        );
18769        assert!(
18770            parsed
18771                .declarations()
18772                .iter()
18773                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
18774        );
18775        assert!(parsed.declarations().iter().all(|unit| {
18776            !matches!(
18777                unit.fq_name().as_str(),
18778                "simplecpp.Token.Following" | "simplecpp.Token.Later"
18779            )
18780        }));
18781        assert!(
18782            !parsed
18783                .declarations()
18784                .iter()
18785                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
18786            "the following struct must remain outside the recovered Token class"
18787        );
18788    }
18789
18790    #[test]
18791    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
18792        let source = r#"
18793#define SIMPLECPP_LIB
18794namespace {
18795namespace simplecpp {
18796using TokenString = std::string;
18797struct Location { int line{}; };
18798class SIMPLECPP_LIB HiddenToken {
18799 public:
18800  HiddenToken(const TokenString &s, const Location &loc) :
18801      location(loc), string(s) {
18802      flags();
18803  }
18804  TokenString string;
18805  Location location;
18806  HiddenToken *previous{};
18807 private:
18808  void flags() {}
18809};
18810}
18811}
18812"#;
18813        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
18814        let constructor = parsed
18815            .declarations()
18816            .iter()
18817            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
18818            .expect("recovered anonymous-namespace constructor");
18819        assert_eq!(
18820            parsed
18821                .signature_metadata
18822                .get(constructor)
18823                .and_then(|metadata| metadata.first())
18824                .and_then(SignatureMetadata::callable_linkage),
18825            Some(CallableLinkage::Internal)
18826        );
18827    }
18828
18829    #[test]
18830    fn macro_qualified_static_field_keeps_real_declarator() {
18831        let source = r#"#define JSON_INLINE_VARIABLE
18832struct Reader {
18833static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
18834static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
18835static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
18836};"#;
18837        let mut parser = tree_sitter::Parser::new();
18838        parser
18839            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18840            .unwrap();
18841        let tree = parser.parse(source, None).unwrap();
18842        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
18843        let parsed = parse_cpp_file(&file, source, &tree);
18844        for expected in [
18845            "Reader.npos",
18846            "Reader.other",
18847            "Reader.pointer",
18848            "Reader.reference",
18849        ] {
18850            assert!(
18851                parsed
18852                    .declarations()
18853                    .iter()
18854                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
18855                "real macro-decorated field {expected} is missing: {:#?}",
18856                parsed.declarations()
18857            );
18858        }
18859        assert!(
18860            parsed
18861                .declarations()
18862                .iter()
18863                .all(|unit| unit.fq_name() != "Reader.std"),
18864            "qualified type prefix became a pseudo-field: {:#?}",
18865            parsed.declarations()
18866        );
18867        let root = tree.root_node();
18868        let mut stack = vec![root];
18869        let mut signatures = Vec::new();
18870        while let Some(current) = stack.pop() {
18871            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
18872            {
18873                signatures.extend(
18874                    declarators
18875                        .into_iter()
18876                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
18877                );
18878            }
18879            let mut cursor = current.walk();
18880            stack.extend(current.named_children(&mut cursor));
18881        }
18882        signatures.sort();
18883        assert_eq!(
18884            signatures,
18885            [
18886                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
18887                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
18888                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
18889                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
18890            ]
18891        );
18892    }
18893
18894    fn member_function_linkage(source: &str) -> CallableLinkage {
18895        let mut parser = tree_sitter::Parser::new();
18896        parser
18897            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18898            .unwrap();
18899        let tree = parser.parse(source, None).unwrap();
18900        let ancestry = ParentIndex::new(tree.root_node());
18901        let mut stack = vec![tree.root_node()];
18902        while let Some(node) = stack.pop() {
18903            if node.kind() == "function_definition" {
18904                let mut current = node.parent();
18905                while let Some(parent) = current {
18906                    if matches!(
18907                        parent.kind(),
18908                        "class_specifier" | "struct_specifier" | "union_specifier"
18909                    ) {
18910                        return cpp_callable_linkage(node, source, &ancestry);
18911                    }
18912                    current = parent.parent();
18913                }
18914            }
18915            let mut cursor = node.walk();
18916            stack.extend(node.named_children(&mut cursor));
18917        }
18918        panic!("fixture has no member function definition");
18919    }
18920
18921    #[test]
18922    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
18923        assert_eq!(
18924            member_function_linkage("struct Named { int method() { return 1; } };"),
18925            CallableLinkage::External
18926        );
18927        assert_eq!(
18928            member_function_linkage(
18929                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
18930            ),
18931            CallableLinkage::Internal
18932        );
18933        assert_eq!(
18934            member_function_linkage("struct { int method() { return 1; } } instance;"),
18935            CallableLinkage::Internal
18936        );
18937        assert_eq!(
18938            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
18939            CallableLinkage::Internal
18940        );
18941    }
18942
18943    #[test]
18944    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
18945        let source = r#"
18946#ifndef PROTON_VALUE_HPP
18947#define PROTON_VALUE_HPP
18948namespace proton {
18949namespace internal {
18950class value_base {
18951  protected:
18952    internal::data& data();
18953    internal::data data_;
18954  friend class codec::encoder;
18955  friend class codec::decoder;
18956};
18957}
18958class value : public internal::value_base, private internal::comparable<value> {
18959  private:
18960    template<class T, class U=void> struct assignable :
18961        public std::enable_if<codec::is_encodable<T>::value, U> {};
18962    template<class U> struct assignable<value, U> {};
18963  public:
18964    PN_CPP_EXTERN value();
18965    PN_CPP_EXTERN value(const value&);
18966    PN_CPP_EXTERN value& operator=(const value&);
18967    PN_CPP_EXTERN value(value&&);
18968    PN_CPP_EXTERN value& operator=(value&&);
18969    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
18970    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
18971        codec::encoder e(*this);
18972        e << x;
18973        return *this;
18974    }
18975    PN_CPP_EXTERN type_id type() const;
18976    PN_CPP_EXTERN bool empty() const;
18977    PN_CPP_EXTERN void clear();
18978    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
18979    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
18980  friend PN_CPP_EXTERN void swap(value&, value&);
18981  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
18982  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
18983  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
18984    value(pn_data_t* d);
18985    void reset(pn_data_t* d = 0);
18986};
18987}
18988#endif
18989"#;
18990        let mut parser = tree_sitter::Parser::new();
18991        parser
18992            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18993            .unwrap();
18994        let tree = parser.parse(source, None).unwrap();
18995        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
18996        let parsed = parse_cpp_file(&file, source, &tree);
18997        let macro_constructors = parsed
18998            .signature_metadata
18999            .iter()
19000            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
19001            .flat_map(|(_, metadata)| metadata)
19002            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
19003            .collect::<Vec<_>>();
19004
19005        assert_eq!(
19006            macro_constructors.len(),
19007            3,
19008            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
19009            parsed.declarations()
19010        );
19011        assert!(
19012            macro_constructors.iter().all(|metadata| {
19013                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
19014            }),
19015            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
19016        );
19017    }
19018
19019    #[test]
19020    fn recovered_export_class_typedef_uses_displaced_alias_name() {
19021        let source = r#"
19022namespace spi {
19023class Filter {
19024public:
19025    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
19026};
19027}
19028namespace filter {
19029class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
19030{
19031public:
19032    typedef spi::Filter BASE_CLASS;
19033    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
19034    BEGIN_LOG4CXX_CAST_MAP()
19035    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
19036    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
19037    END_LOG4CXX_CAST_MAP()
19038    FilterDecision decide() const;
19039};
19040}
19041"#;
19042        let mut parser = tree_sitter::Parser::new();
19043        parser
19044            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19045            .unwrap();
19046        let tree = parser.parse(source, None).unwrap();
19047        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
19048        let parsed = parse_cpp_file(&file, source, &tree);
19049        assert!(
19050            parsed.declarations().iter().any(|unit| {
19051                unit.is_class()
19052                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
19053                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
19054            }),
19055            "the displaced typedef alias must retain its declared name: {:#?}",
19056            parsed.declarations()
19057        );
19058        assert!(
19059            parsed
19060                .declarations()
19061                .iter()
19062                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
19063            "the qualified underlying type must not become a false nested alias: {:#?}",
19064            parsed.declarations()
19065        );
19066    }
19067
19068    #[test]
19069    fn exported_single_base_recovery_uses_displaced_class_name() {
19070        let source = r#"
19071class CORE_EXPORT QgsPoint : public AbstractGeometry
19072{
19073    Q_GADGET
19074
19075    Q_PROPERTY( double x READ x WRITE setX )
19076    Q_PROPERTY( double y READ y WRITE setY )
19077    Q_PROPERTY( double z READ z WRITE setZ )
19078    Q_PROPERTY( double m READ m WRITE setM )
19079
19080  public:
19081#ifndef SIP_RUN
19082    QgsPoint(
19083      double x = std::numeric_limits<double>::quiet_NaN(),
19084      double y = std::numeric_limits<double>::quiet_NaN(),
19085      double z = std::numeric_limits<double>::quiet_NaN(),
19086      double m = std::numeric_limits<double>::quiet_NaN(),
19087      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
19088    );
19089#else
19090    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 )];
19091    % MethodCode
19092    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
19093    {
19094      int state;
19095      sipIsErr = 0;
19096      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
19097      if ( !sipIsErr )
19098      {
19099        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
19100      }
19101      sipReleaseType( p, sipType_QgsPointXY, state );
19102    }
19103    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
19104    {
19105      int state;
19106      sipIsErr = 0;
19107
19108      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
19109      if ( !sipIsErr )
19110      {
19111        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
19112      }
19113      sipReleaseType( p, sipType_QPointF, state );
19114    }
19115    else if (
19116      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
19117      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
19118      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
19119      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
19120    {
19121      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
19122      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
19123      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
19124      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
19125      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
19126      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
19127    }
19128    else // Invalid ctor arguments
19129    {
19130      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
19131      sipIsErr = 1;
19132    }
19133    % End
19134#endif
19135
19136    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
19137    explicit QgsPoint( QPointF p ) SIP_SKIP;
19138    explicit QgsPoint(
19139      Qgis::WkbType wkbType,
19140      double x = std::numeric_limits<double>::quiet_NaN(),
19141      double y = std::numeric_limits<double>::quiet_NaN(),
19142      double z = std::numeric_limits<double>::quiet_NaN(),
19143      double m = std::numeric_limits<double>::quiet_NaN()
19144    ) SIP_SKIP;
19145    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19146    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
19147    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19148#ifndef SIP_RUN
19149  private:
19150    bool fuzzyHelper(
19151      double epsilon,
19152      const AbstractGeometry &other,
19153      bool is3DFlag,
19154      bool isMeasureFlag
19155    ) const
19156    {
19157      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
19158    }
19159#endif
19160};
19161class Ordinary : public Base { public: Ordinary(); };
19162class API_EXPORT Plain { public: Plain(); };
19163class API_EXPORT : public Base {};
19164class
19165PN_CPP_CLASS_EXTERN Sender : public Link {
19166    Sender();
19167    struct impl;
19168    struct impl& get_impl() const;
19169};
19170class thread_ctx_t {};
19171class ctx_t ZMQ_FINAL : public thread_ctx_t {
19172    bool start();
19173};
19174"#;
19175        let mut parser = tree_sitter::Parser::new();
19176        parser
19177            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19178            .unwrap();
19179        let tree = parser.parse(source, None).unwrap();
19180        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
19181        let parsed = parse_cpp_file(&file, source, &tree);
19182        let declarations = parsed.declarations();
19183
19184        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
19185            assert!(
19186                declarations
19187                    .iter()
19188                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
19189                "missing recovered class {expected}: {declarations:#?}"
19190            );
19191        }
19192        let qgs_point = declarations
19193            .iter()
19194            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
19195            .expect("recovered QgsPoint class");
19196        assert_eq!(
19197            parsed.raw_supertypes.get(qgs_point),
19198            Some(&vec!["AbstractGeometry".to_string()]),
19199            "single-base export recovery must retain its displaced base"
19200        );
19201        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
19202        assert!(
19203            parsed
19204                .navigation_ranges
19205                .get(qgs_point)
19206                .is_some_and(|ranges| {
19207                    !ranges.is_empty()
19208                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
19209                }),
19210            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
19211            parsed.navigation_ranges.get(qgs_point)
19212        );
19213        let sender = declarations
19214            .iter()
19215            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
19216            .expect("recovered Sender class");
19217        assert_eq!(
19218            parsed.raw_supertypes.get(sender),
19219            Some(&vec!["Link".to_string()]),
19220            "post-declarator export recovery must retain its displaced base"
19221        );
19222        let recovered_member = declarations
19223            .iter()
19224            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
19225            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
19226        assert_eq!(
19227            parsed
19228                .signature_metadata
19229                .get(recovered_member)
19230                .and_then(|metadata| metadata.first())
19231                .and_then(SignatureMetadata::callable_linkage),
19232            Some(CallableLinkage::External),
19233            "a named recovered class's members have external linkage"
19234        );
19235        let ctx = declarations
19236            .iter()
19237            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
19238            .expect("recovered ctx_t class");
19239        assert_eq!(
19240            parsed.raw_supertypes.get(ctx),
19241            Some(&vec!["thread_ctx_t".to_string()]),
19242            "postfix export-macro recovery must retain its displaced base"
19243        );
19244        assert!(
19245            declarations.iter().any(|unit| {
19246                unit.is_function()
19247                    && unit.fq_name() == "QgsPoint.QgsPoint"
19248                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
19249            }),
19250            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
19251        );
19252        assert!(
19253            declarations.iter().all(|unit| {
19254                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
19255            }),
19256            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
19257        );
19258    }
19259
19260    #[test]
19261    fn function_like_export_macro_classes_keep_names_and_base_edges() {
19262        // Every sibling shape tree-sitter produces after the `class MACRO(2, 0)`
19263        // error: a plain body, a single base, `final` without a base, `final`
19264        // with one base, and `final` with a comma-separated base list.
19265        let source = r#"
19266namespace api {
19267class PROJECT_PUBLIC_API(2, 0) Prelude {
19268  public:
19269    Prelude();
19270};
19271class PROJECT_PUBLIC_API(2, 0) Base {
19272  public:
19273    Base(int value);
19274};
19275class PROJECT_PUBLIC_API(2, 0) Mixin {
19276  public:
19277    Mixin();
19278};
19279class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
19280  public:
19281    Adopted(int value);
19282};
19283class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
19284  public:
19285    Derived(int value);
19286};
19287class PROJECT_PUBLIC_API(2, 0) Solo final {
19288  public:
19289    Solo();
19290};
19291class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
19292  public:
19293    Blended(int value);
19294};
19295class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
19296  public:
19297    Woven(int value);
19298};
19299} // namespace api
19300"#;
19301        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
19302        let declarations = parsed.declarations();
19303        let class_named = |name: &str| {
19304            declarations
19305                .iter()
19306                .find(|unit| unit.is_class() && unit.fq_name() == name)
19307                .unwrap_or_else(|| {
19308                    panic!("missing function-like export macro class {name}: {declarations:#?}")
19309                })
19310        };
19311        let base = class_named("api.Base");
19312        class_named("api.Prelude");
19313        class_named("api.Mixin");
19314
19315        assert_eq!(
19316            parsed.raw_supertypes.get(class_named("api.Adopted")),
19317            Some(&vec!["Base".to_string()])
19318        );
19319        assert_eq!(
19320            parsed.raw_supertypes.get(class_named("api.Derived")),
19321            Some(&vec!["Base".to_string()])
19322        );
19323        assert_eq!(
19324            parsed.raw_supertypes.get(class_named("api.Solo")),
19325            None,
19326            "a final class without a base list must not invent a supertype"
19327        );
19328        assert_eq!(
19329            parsed.raw_supertypes.get(class_named("api.Blended")),
19330            Some(&vec!["Base".to_string(), "Mixin".to_string()])
19331        );
19332        assert_eq!(
19333            parsed.raw_supertypes.get(class_named("api.Woven")),
19334            Some(&vec!["Base".to_string(), "Mixin".to_string()])
19335        );
19336        assert!(
19337            declarations
19338                .iter()
19339                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19340            "the export macro must not become a declaration: {declarations:#?}"
19341        );
19342        assert!(
19343            declarations.iter().all(|unit| !matches!(
19344                unit.identifier(),
19345                "final" | "public" | "protected" | "private"
19346            )),
19347            "the head specifiers must not become declarations: {declarations:#?}"
19348        );
19349        assert!(
19350            parsed
19351                .navigation_ranges
19352                .get(base)
19353                .is_some_and(|ranges| !ranges.is_empty()),
19354            "the recovered base must retain a navigable declaration range"
19355        );
19356    }
19357
19358    #[test]
19359    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
19360        // #2557: the class name is the last identifier before the head ends
19361        // (`final`, the base clause `:`, or the body), whatever its spelling.
19362        // A class named in capitals (`X509_CA`) is a class, and an object-like
19363        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
19364        // the control whose spelling never mattered.
19365        let source = r#"
19366namespace api {
19367class PROJECT_PUBLIC_API(2, 0) Base {
19368  public:
19369    Base();
19370};
19371class PROJECT_PUBLIC_API(2, 0) Mixin {
19372  public:
19373    Mixin();
19374};
19375class PROJECT_PUBLIC_API(2, 0) Name {
19376  public:
19377    Name();
19378};
19379class PROJECT_PUBLIC_API(2, 0) X509_CA final {
19380  public:
19381    X509_CA();
19382};
19383class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
19384  public:
19385    HSS_LMS_KEY();
19386};
19387class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
19388  public:
19389    GOST_3410();
19390};
19391class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
19392  public:
19393    PKCS11_RSA();
19394};
19395class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
19396  public:
19397    Plain();
19398};
19399class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
19400  public:
19401    Decorated();
19402};
19403class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
19404  public:
19405    Layered();
19406};
19407} // namespace api
19408"#;
19409        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
19410        let declarations = parsed.declarations();
19411        let class_named = |name: &str| {
19412            declarations
19413                .iter()
19414                .find(|unit| unit.is_class() && unit.fq_name() == name)
19415                .unwrap_or_else(|| {
19416                    panic!("missing function-like export macro class {name}: {declarations:#?}")
19417                })
19418        };
19419        for (name, bases) in [
19420            ("api.Name", None),
19421            ("api.X509_CA", None),
19422            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
19423            ("api.GOST_3410", Some(vec!["Base"])),
19424            ("api.PKCS11_RSA", None),
19425            ("api.Plain", None),
19426            ("api.Decorated", Some(vec!["Base"])),
19427            ("api.Layered", Some(vec!["Base", "Mixin"])),
19428        ] {
19429            let expected =
19430                bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
19431            assert_eq!(
19432                parsed.raw_supertypes.get(class_named(name)),
19433                expected.as_ref(),
19434                "{name}"
19435            );
19436        }
19437        assert!(
19438            declarations.iter().all(|unit| !matches!(
19439                unit.identifier(),
19440                "PROJECT_PUBLIC_API"
19441                    | "OTHER_MACRO"
19442                    | "FIRST_MACRO"
19443                    | "SECOND_MACRO"
19444                    | "final"
19445                    | "public"
19446            )),
19447            "macros and head specifiers must not become declarations: {declarations:#?}"
19448        );
19449    }
19450
19451    #[test]
19452    fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
19453        // #2924: `class MACRO(2, 0) Name : public virtual Base {` leaves the
19454        // head, the body's `{` and its first member in one declaration-scope
19455        // ERROR, scatters the remaining members across the container's
19456        // siblings, and ends the container on the class's own `}`. The class
19457        // was lost and the macro minted a class of its own.
19458        let source = r#"
19459namespace api {
19460
19461/**
19462* Doc comment
19463*/
19464class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
19465   public:
19466      /**
19467      * Construct from a point.
19468      */
19469      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
19470
19471#if defined(PROJECT_HAS_LEGACY_POINT)
19472      /**
19473      * Construct from a legacy point.
19474      */
19475      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
19476#endif
19477
19478      std::string algo_name() const override;
19479
19480      AlgorithmIdentifier algorithm_identifier() const override;
19481};
19482
19483}
19484"#;
19485        let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
19486        let declarations = parsed.declarations();
19487        let class = declarations
19488            .iter()
19489            .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
19490            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19491        assert_eq!(
19492            parsed.raw_supertypes.get(class),
19493            Some(&vec!["BaseKey".to_string()]),
19494            "the virtual base is the class's base: {declarations:#?}"
19495        );
19496        for member in [
19497            "api.VirtualBased.algo_name",
19498            "api.VirtualBased.algorithm_identifier",
19499        ] {
19500            assert!(
19501                declarations
19502                    .iter()
19503                    .any(|unit| unit.is_function() && unit.fq_name() == member),
19504                "{member} must be owned by the recovered class: {declarations:#?}"
19505            );
19506        }
19507        assert!(
19508            declarations
19509                .iter()
19510                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19511            "an unrecovered head must not mint a macro-named class: {declarations:#?}"
19512        );
19513    }
19514
19515    #[test]
19516    fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
19517        // #2924: object-like macro lines before the head demote the `class`
19518        // keyword to a bare identifier and make the macro invocation an
19519        // `init_declarator`, so the head arrives as a `declaration` rather than
19520        // as an ERROR carrying a `class_specifier`. The name is still the last
19521        // identifier before `final`, and `virtual` is a base specifier rather
19522        // than a base.
19523        let source = r#"
19524namespace api {
19525
19526DIAGNOSTIC_PUSH
19527DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
19528
19529class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
19530                                               public virtual api::Inner::Key {
19531   public:
19532      std::string algo_name() const override;
19533};
19534
19535DIAGNOSTIC_POP
19536
19537}
19538"#;
19539        let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
19540        let declarations = parsed.declarations();
19541        let class = declarations
19542            .iter()
19543            .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
19544            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19545        assert_eq!(
19546            parsed.raw_supertypes.get(class),
19547            Some(&vec![
19548                "api::Outer::Key".to_string(),
19549                "api::Inner::Key".to_string()
19550            ]),
19551            "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
19552        );
19553        assert!(
19554            declarations
19555                .iter()
19556                .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
19557            "the member is owned by the recovered class: {declarations:#?}"
19558        );
19559        assert!(
19560            declarations
19561                .iter()
19562                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19563            "the macro invocation must not mint a declaration: {declarations:#?}"
19564        );
19565    }
19566
19567    #[test]
19568    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
19569        // The class embedded in a preceding malformed body follows the same
19570        // rule (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration.
19571        let fixture = |head: &str, name: &str| {
19572            format!(
19573                r#"
19574namespace api {{
19575class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
19576   public:
19577      /** Return a descriptive string. */
19578      const char* what() const noexcept override {{ return m_msg.c_str(); }}
19579
19580      /** Return the type of error. */
19581      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
19582
19583      /** Return an associated error code. */
19584      virtual int error_code() const noexcept {{ return 0; }}
19585
19586      /** Avoid throwing the base directly. */
19587      explicit Exception(std::string_view msg);
19588
19589      /** Avoid throwing the base directly. */
19590      Exception(const char* prefix, std::string_view msg);
19591
19592      /** Avoid throwing the base directly. */
19593      Exception(std::string_view msg, const std::exception& e);
19594
19595   private:
19596      std::string m_msg;
19597}};
19598
19599class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
19600   public:
19601      explicit {name}(std::string_view msg);
19602
19603      explicit {name}(std::string_view msg, std::string_view where);
19604
19605      {name}(std::string_view msg, const std::exception& e);
19606
19607      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
19608}};
19609}} // namespace api
19610"#
19611            )
19612        };
19613        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
19614            let source = fixture(head, name);
19615            let mut parser = Parser::new();
19616            parser
19617                .set_language(&tree_sitter_cpp::LANGUAGE.into())
19618                .expect("set C++ grammar");
19619            let tree = parser.parse(&source, None).expect("parse fixture");
19620            let mut embedded = Vec::new();
19621            let mut stack = vec![tree.root_node()];
19622            while let Some(node) = stack.pop() {
19623                embedded.extend(
19624                    recover_embedded_function_like_export_classes(node, &source)
19625                        .into_iter()
19626                        .map(|recovered| (recovered.name, recovered.raw_supertypes)),
19627                );
19628                let mut cursor = node.walk();
19629                stack.extend(node.named_children(&mut cursor));
19630            }
19631            assert!(
19632                embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
19633                "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
19634                tree.root_node().to_sexp()
19635            );
19636            assert!(
19637                embedded
19638                    .iter()
19639                    .all(|(recovered, _)| recovered != "OTHER_MACRO"),
19640                "{head}: the object-like macro is not a class: {embedded:#?}"
19641            );
19642
19643            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
19644            let declarations = parsed.declarations();
19645            let class = declarations
19646                .iter()
19647                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
19648                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
19649            assert_eq!(
19650                parsed.raw_supertypes.get(class),
19651                Some(&vec!["Exception".to_string()]),
19652                "{head}"
19653            );
19654            assert!(
19655                declarations
19656                    .iter()
19657                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
19658                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
19659            );
19660        }
19661    }
19662
19663    #[test]
19664    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
19665        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar keeps
19666        // `class MACRO(3, 6) Name final :` in the error, the first base as the
19667        // `type` of the following declaration, and closes that declaration with
19668        // a zero-width `MISSING identifier`. Reading the head by position must
19669        // not take that missing node for the class name: an empty name panics
19670        // at `FqName` construction, which took the Botan corpus replay down
19671        // (#2557). The head's tokens straddle the two nodes, so the positional
19672        // read spans both and recovers the class rather than leaving the
19673        // bodyless `class PROJECT_PUBLIC_API` specifier behind (#2924).
19674        let source = r#"
19675namespace api {
19676class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
19677                                                    public virtual Botan::EC_PublicKey {
19678   public:
19679      std::string algo_name() const override { return "ECDSA"; }
19680};
19681} // namespace api
19682"#;
19683        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
19684        let declarations = parsed.declarations();
19685        assert!(
19686            declarations
19687                .iter()
19688                .all(|unit| !unit.identifier().is_empty()),
19689            "no declaration may carry an empty name: {declarations:#?}"
19690        );
19691        assert!(
19692            declarations
19693                .iter()
19694                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
19695            "macros and head specifiers must not become declarations: {declarations:#?}"
19696        );
19697        let class = declarations
19698            .iter()
19699            .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
19700            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19701        assert_eq!(
19702            parsed.raw_supertypes.get(class),
19703            Some(&vec![
19704                "Botan::TPM2::PublicKey".to_string(),
19705                "Botan::EC_PublicKey".to_string()
19706            ]),
19707            "both qualified virtual bases are bases: {declarations:#?}"
19708        );
19709        assert!(
19710            declarations
19711                .iter()
19712                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19713            "the head must not mint a macro-named class: {declarations:#?}"
19714        );
19715    }
19716
19717    #[test]
19718    fn function_like_export_class_survives_a_preceding_malformed_body() {
19719        let source = r#"
19720namespace api {
19721class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
19722   public:
19723      /** Return a descriptive string. */
19724      const char* what() const noexcept override { return m_msg.c_str(); }
19725
19726      /** Return the type of error. */
19727      virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
19728
19729      /** Return an associated error code. */
19730      virtual int error_code() const noexcept { return 0; }
19731
19732      /** Avoid throwing the base directly. */
19733      explicit Exception(std::string_view msg);
19734
19735      /** Avoid throwing the base directly. */
19736      Exception(const char* prefix, std::string_view msg);
19737
19738      /** Avoid throwing the base directly. */
19739      Exception(std::string_view msg, const std::exception& e);
19740
19741   private:
19742      std::string m_msg;
19743};
19744
19745class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
19746   public:
19747      explicit Invalid_Argument(std::string_view msg);
19748
19749      explicit Invalid_Argument(std::string_view msg, std::string_view where);
19750
19751      Invalid_Argument(std::string_view msg, const std::exception& e);
19752
19753      ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
19754};
19755} // namespace api
19756"#;
19757        let mut parser = Parser::new();
19758        parser
19759            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19760            .expect("set C++ grammar");
19761        let tree = parser.parse(source, None).expect("parse fixture");
19762        let mut stack = vec![tree.root_node()];
19763        let mut saw_embedded_shape = false;
19764        while let Some(node) = stack.pop() {
19765            saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
19766                .iter()
19767                .any(|recovered| recovered.name == "Invalid_Argument");
19768            let mut cursor = node.walk();
19769            stack.extend(node.named_children(&mut cursor));
19770        }
19771        assert!(
19772            saw_embedded_shape,
19773            "fixture must retain the embedded error geometry: {}",
19774            tree.root_node().to_sexp()
19775        );
19776
19777        let parsed = parse_cpp_file(
19778            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
19779            source,
19780            &tree,
19781        );
19782        let declarations = parsed.declarations();
19783        let exception = declarations
19784            .iter()
19785            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
19786            .expect("qualified-base export class");
19787        let invalid = declarations
19788            .iter()
19789            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
19790            .expect("class embedded in the preceding malformed body");
19791
19792        assert_eq!(
19793            parsed.raw_supertypes.get(exception),
19794            Some(&vec!["std::exception".to_string()])
19795        );
19796        assert_eq!(
19797            parsed.raw_supertypes.get(invalid),
19798            Some(&vec!["Exception".to_string()])
19799        );
19800        assert!(
19801            parsed.materialization_records.iter().any(|record| matches!(
19802                record,
19803                MaterializationRecord::RecoveredDeclaration { unit, .. }
19804                    if unit == invalid
19805            )),
19806            "the embedded class must retain recovery provenance: {:#?}",
19807            parsed.materialization_records
19808        );
19809    }
19810
19811    #[test]
19812    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
19813        let source = r#"
19814public:
19815   explicit Lookup_Error(std::string_view err) : Exception(err) {}
19816
19817   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
19818"#;
19819        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
19820            .expect("reparse merged constructor body");
19821        let (range, body) =
19822            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
19823                .unwrap_or_else(|| {
19824                    panic!(
19825                        "the merged constructor must retain its structured declarator/body: {}",
19826                        tree.root_node().to_sexp()
19827                    )
19828                });
19829        assert_eq!(
19830            source.get(range).expect("constructor range"),
19831            "Lookup_Error(std::string_view err) : Exception(err) {}"
19832        );
19833        assert_eq!(node_text(body, source), "{}");
19834    }
19835
19836    #[test]
19837    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
19838        let positive_source =
19839            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
19840        let mut parser = tree_sitter::Parser::new();
19841        parser
19842            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19843            .unwrap();
19844        let positive_tree = parser.parse(positive_source, None).unwrap();
19845        assert!(cpp_reparsed_members_are_indexable(
19846            positive_tree.root_node(),
19847            positive_source
19848        ));
19849
19850        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
19851        let negative_tree = parser.parse(negative_source, None).unwrap();
19852        assert!(!cpp_reparsed_members_are_indexable(
19853            negative_tree.root_node(),
19854            negative_source
19855        ));
19856    }
19857
19858    #[test]
19859    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
19860        let copy_control_source = r#"
19861public:
19862    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
19863    explicit Token(const Token* tok);
19864    ~Token();
19865    Token* astOperand1() { return nullptr; }
19866"#;
19867        let constraint_source = r#"
19868private:
19869    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19870    static T *tokAtImpl(T *tok, int index) {
19871        return tok;
19872    }
19873
19874    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19875    static T *linkAtImpl(T *tok, int index) {
19876        return tok;
19877    }
19878
19879public:
19880    int late() const { return 1; }
19881"#;
19882        let mut parser = tree_sitter::Parser::new();
19883        parser
19884            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19885            .unwrap();
19886        let copy_control_tree = parser
19887            .parse(copy_control_source, None)
19888            .expect("parse copy-control fixture");
19889        assert!(
19890            copy_control_tree.root_node().has_error(),
19891            "fixture must exercise adjacent copy-control recovery"
19892        );
19893        assert!(
19894            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
19895            "a complete late getter must remain recoverable after adjacent copy-control declarations"
19896        );
19897        let mut cursor = copy_control_tree.root_node().walk();
19898        assert!(
19899            copy_control_tree
19900                .root_node()
19901                .named_children(&mut cursor)
19902                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
19903            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
19904            copy_control_tree.root_node().to_sexp()
19905        );
19906        let constraint_tree = parser
19907            .parse(constraint_source, None)
19908            .expect("parse constraint-macro fixture");
19909        assert!(constraint_tree.root_node().has_error());
19910        assert!(
19911            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
19912            "complete constraint-macro members must not hide a later ordinary member"
19913        );
19914        let mut cursor = constraint_tree.root_node().walk();
19915        assert!(
19916            constraint_tree
19917                .root_node()
19918                .named_children(&mut cursor)
19919                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
19920                    child,
19921                    constraint_source
19922                )),
19923            "fixture must retain the split constraint-macro prefix/function geometry"
19924        );
19925    }
19926
19927    #[test]
19928    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
19929        let source = r#"
19930struct Analyzer {
19931    struct Action {
19932        Action() = default;
19933        Action(const Action&) = default;
19934        Action& operator=(const Action& rhs) & = default;
19935
19936        template<class T,
19937                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
19938                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
19939        // NOLINTNEXTLINE(google-explicit-constructor)
19940        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
19941        {}
19942
19943        enum : std::uint16_t { None = 0, Read = (1 << 0) };
19944        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
19945
19946    private:
19947        unsigned int mFlag{};
19948    };
19949
19950    enum class Direction : unsigned char { Forward, Reverse };
19951    virtual Action analyze(Direction d) const = 0;
19952};
19953"#;
19954        let mut parser = tree_sitter::Parser::new();
19955        parser
19956            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19957            .unwrap();
19958        let tree = parser.parse(source, None).unwrap();
19959        assert!(tree.root_node().has_error());
19960        let root = tree.root_node();
19961        let outer = root
19962            .named_children(&mut root.walk())
19963            .find(|child| child.kind() == "ERROR")
19964            .expect("fragmented Analyzer prefix");
19965        let outer_recovered =
19966            fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
19967        assert_eq!(outer_recovered.name, "Analyzer");
19968        let outer_tree = cpp_reparse_fragmented_class_body(
19969            source,
19970            outer_recovered.body.reparse_start,
19971            outer_recovered.body.reparse_end,
19972        )
19973        .expect("reparse Analyzer body");
19974        let outer_root = outer_tree.root_node();
19975        let action_prefix = outer_root
19976            .named_children(&mut outer_root.walk())
19977            .find(|child| child.kind() == "ERROR")
19978            .expect("fragmented Action prefix");
19979        let action_recovered = fragmented_class_body(action_prefix, source)
19980            .expect("structured Action fragment boundary");
19981        assert_eq!(action_recovered.name, "Action");
19982        let action_tree = cpp_reparse_fragmented_class_body(
19983            source,
19984            action_recovered.body.reparse_start,
19985            action_recovered.body.reparse_end,
19986        )
19987        .expect("reparse Action body");
19988        let action_root = action_tree.root_node();
19989        let macro_prefix = action_root
19990            .named_children(&mut action_root.walk())
19991            .find(|child| child.kind() == "ERROR")
19992            .expect("constraint macro prefix");
19993        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
19994            .expect("structured template macro prefix");
19995        let macro_companion =
19996            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
19997        assert!(
19998            cpp_reparsed_template_macro_constructor_companion_is_indexable(
19999                macro_companion,
20000                macro_parameter,
20001                source,
20002            ),
20003            "split constrained constructor must be admitted: {}",
20004            macro_companion.to_sexp()
20005        );
20006        assert!(
20007            cpp_reparsed_members_are_indexable(action_root, source),
20008            "complete Action body must pass the recovery gate: {}",
20009            action_tree.root_node().to_sexp()
20010        );
20011        assert!(
20012            cpp_reparsed_members_are_indexable(outer_root, source),
20013            "complete Analyzer body must pass the recovery gate: {}",
20014            outer_tree.root_node().to_sexp()
20015        );
20016        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
20017        let parsed = parse_cpp_file(&file, source, &tree);
20018        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
20019            assert!(
20020                parsed
20021                    .declarations()
20022                    .iter()
20023                    .any(|unit| unit.fq_name() == expected),
20024                "missing recovered declaration {expected}: {:#?}",
20025                parsed.declarations()
20026            );
20027        }
20028        assert!(
20029            parsed
20030                .declarations()
20031                .iter()
20032                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
20033            "nested members must not remain flattened: {:#?}",
20034            parsed.declarations()
20035        );
20036    }
20037
20038    #[test]
20039    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
20040        let source = r#"
20041raw_hash_set& operator=(raw_hash_set&& that) {
20042  return move_assign(
20043      std::move(that),
20044      typename AllocTraits::propagate_on_container_move_assignment());
20045}
20046
20047iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
20048  return {};
20049}
20050
20051void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
20052
20053iterator insert(const_iterator hint, value_type&& value)
20054    ABSL_ATTRIBUTE_LIFETIME_BOUND {
20055  return {};
20056}
20057
20058friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
20059  return left.size() == right.size();
20060}
20061
20062static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
20063  return static_cast<slot_type*>(buffer);
20064}
20065
20066protected:
20067// Included-range recovery can attach this comment to the template prefix.
20068template <class K>
20069void AssertOnFind([[maybe_unused]] const K& key) {
20070  Check(key);
20071}
20072"#;
20073        let mut parser = tree_sitter::Parser::new();
20074        parser
20075            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20076            .unwrap();
20077        let tree = parser.parse(source, None).unwrap();
20078        assert!(
20079            tree.root_node().has_error(),
20080            "the fixture must exercise tree-sitter's errorful member shapes"
20081        );
20082        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
20083
20084        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
20085        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
20086        assert!(!cpp_reparsed_members_are_indexable(
20087            incomplete_tree.root_node(),
20088            incomplete_source
20089        ));
20090
20091        let outside_error_source = "int foo() stray_attribute {}\n";
20092        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
20093        assert!(outside_error_tree.root_node().has_error());
20094        assert!(!cpp_reparsed_members_are_indexable(
20095            outside_error_tree.root_node(),
20096            outside_error_source
20097        ));
20098
20099        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
20100        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20101        assert!(!cpp_reparsed_members_are_indexable(
20102            variable_initializer_tree.root_node(),
20103            variable_initializer_source
20104        ));
20105    }
20106
20107    #[test]
20108    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
20109        let positive_source = r#"
20110std::pair<iterator, bool> insert(init_type&& value)
20111    ABSL_ATTRIBUTE_LIFETIME_BOUND
20112#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20113  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20114#endif
20115{
20116  return emplace(std::move(value));
20117}
20118"#;
20119        let mut parser = tree_sitter::Parser::new();
20120        parser
20121            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20122            .unwrap();
20123        let positive_tree = parser.parse(positive_source, None).unwrap();
20124        assert!(
20125            positive_tree.root_node().has_error(),
20126            "the fixture must exercise the split attribute/requires shape"
20127        );
20128        assert!(cpp_reparsed_members_are_indexable(
20129            positive_tree.root_node(),
20130            positive_source
20131        ));
20132
20133        let template_return_source = r#"
20134pair<int> insert(init_type&& value)
20135    ABSL_ATTRIBUTE_LIFETIME_BOUND
20136#if LANGUAGE_LEVEL >= 202002L
20137  requires(!Predicate<init_type>::value)
20138#endif
20139// Attributes and the function body may be separated by comments.
20140{
20141  return {};
20142}
20143"#;
20144        let template_return_tree = parser.parse(template_return_source, None).unwrap();
20145        assert!(
20146            cpp_reparsed_members_are_indexable(
20147                template_return_tree.root_node(),
20148                template_return_source
20149            ),
20150            "template-return attribute/requires tree: {}",
20151            template_return_tree.root_node().to_sexp()
20152        );
20153
20154        let no_body_source = r#"
20155std::pair<iterator, bool> insert(init_type&& value)
20156    ABSL_ATTRIBUTE_LIFETIME_BOUND
20157#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20158  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20159#endif
20160+ 0;
20161"#;
20162        let no_body_tree = parser.parse(no_body_source, None).unwrap();
20163        assert!(!cpp_reparsed_members_are_indexable(
20164            no_body_tree.root_node(),
20165            no_body_source
20166        ));
20167
20168        let extra_payload_source = r#"
20169pair<int> insert(init_type&& value)
20170    ABSL_ATTRIBUTE_LIFETIME_BOUND
20171#if LANGUAGE_LEVEL >= 202002L
20172  int unrelated;
20173  requires(Predicate<init_type>::value)
20174#endif
20175{
20176  return {};
20177}
20178"#;
20179        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
20180        assert!(!cpp_reparsed_members_are_indexable(
20181            extra_payload_tree.root_node(),
20182            extra_payload_source
20183        ));
20184
20185        let variable_initializer_source = r#"
20186int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
20187#if LANGUAGE_LEVEL >= 202002L
20188  requires(true)
20189#endif
20190{
20191  bad;
20192}
20193"#;
20194        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20195        assert!(!cpp_reparsed_members_are_indexable(
20196            variable_initializer_tree.root_node(),
20197            variable_initializer_source
20198        ));
20199    }
20200
20201    #[test]
20202    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
20203        let source = r#"namespace absl {
20204ABSL_NAMESPACE_BEGIN namespace container_internal {
20205
20206class raw_hash_set : public Base {
20207 public:
20208  using value_type = int;
20209
20210  template <class U,
20211            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
20212  void insert(U value) { (void)value; }
20213
20214  struct InsertSlot {
20215    raw_hash_set& s;
20216  };
20217};
20218
20219}
20220ABSL_NAMESPACE_END
20221}"#;
20222        let mut parser = tree_sitter::Parser::new();
20223        parser
20224            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20225            .unwrap();
20226        let tree = parser.parse(source, None).unwrap();
20227        let root = tree.root_node();
20228        let outer_namespace = root
20229            .named_children(&mut root.walk())
20230            .find(|child| child.kind() == "namespace_definition")
20231            .expect("outer absl namespace");
20232        let declaration_list = outer_namespace
20233            .child_by_field_name("body")
20234            .expect("outer namespace body");
20235        let sentinel_function = declaration_list
20236            .named_children(&mut declaration_list.walk())
20237            .find(|child| child.kind() == "function_definition")
20238            .expect("malformed namespace sentinel function");
20239        let ancestry = ParentIndex::new(root);
20240        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
20241            .expect("structured nested namespace sentinel");
20242        let fragmented =
20243            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
20244                .expect("fragmented raw_hash_set class");
20245        assert_eq!(fragmented.class_node.kind(), "ERROR");
20246        assert_eq!(fragmented.name, "raw_hash_set");
20247        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
20248
20249        let outer_scope =
20250            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
20251        let mut outer_siblings = Vec::new();
20252        push_cpp_sentinel_sibling_classes(
20253            &mut outer_siblings,
20254            declaration_list,
20255            sentinel.function,
20256            &outer_scope,
20257            source,
20258            &ancestry,
20259        );
20260        let [outer_shadow] = outer_siblings.as_slice() else {
20261            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
20262        };
20263        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
20264        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
20265
20266        let field = "    raw_hash_set& s;";
20267        let start = source.find(field).expect("InsertSlot field") + 4;
20268        let node = root
20269            .descendant_for_byte_range(start, start + "raw_hash_set".len())
20270            .expect("raw_hash_set type node");
20271        let recovered = cpp_sentinel_recovered_classes(root, source);
20272        let [deep_class] = recovered.as_slice() else {
20273            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
20274        };
20275        assert_eq!(
20276            deep_class.namespace_scope_components,
20277            vec!["absl", "container_internal"]
20278        );
20279        assert_eq!(
20280            deep_class.scope_components,
20281            vec!["absl", "container_internal", "raw_hash_set"]
20282        );
20283        assert!(
20284            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
20285                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
20286        );
20287
20288        assert_eq!(
20289            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
20290            Some(vec![
20291                "absl".to_string(),
20292                "container_internal".to_string(),
20293                "raw_hash_set".to_string(),
20294                "InsertSlot".to_string(),
20295            ])
20296        );
20297
20298        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
20299        let parsed = parse_cpp_file(&file, source, &tree);
20300        let raw_hash_set = parsed
20301            .declarations()
20302            .iter()
20303            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
20304            .expect("recovered raw_hash_set class");
20305        assert_eq!(
20306            raw_hash_set.fq_name(),
20307            "absl::container_internal.raw_hash_set",
20308            "the recovered declaration must publish under the deeper sentinel namespace"
20309        );
20310        assert_eq!(
20311            parsed.raw_supertypes.get(raw_hash_set),
20312            Some(&vec!["Base".to_string()]),
20313            "the structured base clause on the fragmented ERROR prefix must survive publication"
20314        );
20315        assert!(
20316            parsed.materialization_records.iter().any(|record| matches!(
20317                record,
20318                MaterializationRecord::RecoveredDeclaration { recovery, unit }
20319                    if unit == raw_hash_set && *recovery == deep_class.class_range
20320            )),
20321            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
20322            parsed.materialization_records
20323        );
20324    }
20325
20326    /// Issue #2358: recording an aggregate definition must not walk the whole
20327    /// file.
20328    ///
20329    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
20330    /// class-like shape that has a body, so the removal step runs once per
20331    /// aggregate. It used to `retain` over `top_level_declarations` and over
20332    /// *every* child list in the file on each of those calls, comparing whole
20333    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
20334    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
20335    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
20336    /// tens of thousands of times over and the C forward differential never
20337    /// finished.
20338    ///
20339    /// A definition the file has not already declared removes nothing, so the
20340    /// honest cost is zero regardless of how many other aggregates surround it.
20341    /// Two sizes an order of magnitude apart pin that the count is not merely
20342    /// small but independent of the file.
20343    ///
20344    /// The declaration walk answers every ancestor question from a
20345    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
20346    /// the root for each one (#2361). Substituting the index is only safe
20347    /// because it answers the identical question, so pin that on the shapes
20348    /// this file's recovery paths care about: anonymous and named aggregates,
20349    /// nested namespaces, templates, macro-displaced declarations and the
20350    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
20351    /// too -- `Node::parent` walks the visible tree, not the named one.
20352    #[test]
20353    fn the_parent_index_answers_what_tree_sitter_answers() {
20354        const SHAPES: [&str; 5] = [
20355            "namespace outer { namespace inner { struct Tag { int field; }; } }",
20356            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
20357            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
20358            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
20359            "class API Broken : public First, public Second {\n  void member();\n",
20360        ];
20361        for source in SHAPES {
20362            let mut parser = tree_sitter::Parser::new();
20363            parser
20364                .set_language(&tree_sitter_cpp::LANGUAGE.into())
20365                .unwrap();
20366            let tree = parser.parse(source, None).unwrap();
20367            let root = tree.root_node();
20368            let ancestry = ParentIndex::new(root);
20369            let mut nodes = 0usize;
20370            let mut stack = vec![root];
20371            while let Some(node) = stack.pop() {
20372                nodes += 1;
20373                assert_eq!(
20374                    node.parent().map(|parent| parent.id()),
20375                    ancestry.parent(node).map(|parent| parent.id()),
20376                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
20377                );
20378                let mut cursor = node.walk();
20379                stack.extend(node.children(&mut cursor));
20380            }
20381            assert!(nodes > 1, "{source:?} produced no tree to compare");
20382        }
20383    }
20384
20385    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
20386    /// index for every ancestor edge. Asking tree-sitter directly makes each
20387    /// edge re-descend from the root, turning declaration extraction on a
20388    /// deeply nested generated header from quadratic output work into a cubic
20389    /// tree walk. Exact query counts pin the route without a machine-dependent
20390    /// wall-clock ceiling.
20391    #[test]
20392    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
20393        const DEPTH: usize = 64;
20394        let mut source = String::new();
20395        for level in 0..DEPTH {
20396            writeln!(source, "namespace n{level} {{").unwrap();
20397        }
20398        source.push_str("int deepest(int value);\n");
20399        for _ in 0..DEPTH {
20400            source.push_str("}\n");
20401        }
20402
20403        let mut parser = tree_sitter::Parser::new();
20404        parser
20405            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20406            .unwrap();
20407        let tree = parser.parse(&source, None).unwrap();
20408        let root = tree.root_node();
20409        let ancestry = ParentIndex::new(root);
20410        let mut function_declarator = None;
20411        walk_named_tree_preorder(root, true, |node| {
20412            if node.kind() == "function_declarator" {
20413                function_declarator = Some(node);
20414                WalkControl::Break
20415            } else {
20416                WalkControl::Continue
20417            }
20418        });
20419        let function_declarator = function_declarator.expect("deepest function declarator");
20420        let ancestor_count =
20421            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
20422
20423        ancestry.reset_parent_query_count_for_test();
20424        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
20425        assert_eq!(DEPTH, lexical_scope.len());
20426        assert_eq!(
20427            ancestor_count + 1,
20428            ancestry.parent_query_count_for_test(),
20429            "lexical-scope ancestry bypassed the parent index"
20430        );
20431
20432        ancestry.reset_parent_query_count_for_test();
20433        assert_eq!(
20434            DispatchExtensibility::Closed,
20435            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
20436        );
20437        assert_eq!(
20438            ancestor_count,
20439            ancestry.parent_query_count_for_test(),
20440            "dispatch ancestry bypassed the parent index"
20441        );
20442
20443        ancestry.reset_parent_query_count_for_test();
20444        assert_eq!(
20445            CallableLinkage::External,
20446            cpp_callable_linkage(function_declarator, &source, &ancestry)
20447        );
20448        assert_eq!(
20449            ancestor_count + 1,
20450            ancestry.parent_query_count_for_test(),
20451            "linkage ancestry bypassed the parent index"
20452        );
20453
20454        ancestry.reset_parent_query_count_for_test();
20455        assert!(!cpp_callable_is_structural_constructor(
20456            function_declarator,
20457            &source,
20458            &ancestry
20459        ));
20460        assert_eq!(
20461            ancestor_count + 1,
20462            ancestry.parent_query_count_for_test(),
20463            "constructor ancestry bypassed the parent index"
20464        );
20465    }
20466
20467    /// Forward declarations followed by definitions are compacted as one
20468    /// batch, without rescanning the shared namespace/top-level lists for each
20469    /// tag. Definitions are intentionally visited in reverse order so the
20470    /// assertion also pins eager remove-and-reappend ordering.
20471    #[test]
20472    fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
20473        for aggregates in [64usize, 512] {
20474            let mut source =
20475                String::from("typedef unsigned long long u64;\nnamespace generated {\n");
20476            for index in 0..aggregates {
20477                writeln!(source, "struct tag{index};").unwrap();
20478            }
20479            for index in (0..aggregates).rev() {
20480                writeln!(
20481                    source,
20482                    "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
20483                )
20484                .unwrap();
20485            }
20486            source.push_str("}\n");
20487
20488            start_code_unit_removal_scan_probe();
20489            let parsed = parse_cpp_declarations(&source, "vmlinux.h");
20490            let scanned = finish_code_unit_removal_scan_probe();
20491
20492            let expected_names: Vec<String> = (0..aggregates)
20493                .rev()
20494                .map(|index| format!("tag{index}"))
20495                .collect();
20496            let top_level_names: Vec<String> = parsed
20497                .top_level_declarations
20498                .iter()
20499                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20500                .map(|unit| unit.short_name().to_string())
20501                .collect();
20502            let namespace = parsed
20503                .declarations()
20504                .iter()
20505                .find(|unit| {
20506                    unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
20507                })
20508                .expect("generated namespace should be declared");
20509            let child_names: Vec<String> = parsed.children[namespace]
20510                .iter()
20511                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20512                .map(|unit| unit.short_name().to_string())
20513                .collect();
20514            assert_eq!(
20515                aggregates,
20516                parsed
20517                    .declarations()
20518                    .iter()
20519                    .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20520                    .count(),
20521                "every aggregate must still be declared at {aggregates} aggregates"
20522            );
20523            assert_eq!(expected_names, top_level_names);
20524            assert_eq!(expected_names, child_names);
20525            assert_eq!(
20526                0, scanned,
20527                "replacing {aggregates} forward declarations must compact their shared lists once"
20528            );
20529        }
20530    }
20531
20532    #[test]
20533    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
20534        const DISTINCT_PER_KIND: usize = 64;
20535        let mut source = String::new();
20536        for index in 0..DISTINCT_PER_KIND {
20537            writeln!(source, "typedef int Alias{index};").unwrap();
20538        }
20539        writeln!(source, "typedef long Alias0;").unwrap();
20540        for index in 0..DISTINCT_PER_KIND {
20541            writeln!(source, "#define MACRO_{index} {index}").unwrap();
20542        }
20543        writeln!(source, "#define MACRO_0 duplicate").unwrap();
20544        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
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 file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
20552
20553        start_declaration_identity_comparison_probe();
20554        let parsed = parse_cpp_file(&file, &source, &tree);
20555        let comparisons = finish_declaration_identity_comparison_probe();
20556
20557        assert_eq!(
20558            DISTINCT_PER_KIND + 1,
20559            parsed
20560                .declarations()
20561                .iter()
20562                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
20563                .count(),
20564            "every physical typedef alias declaration must be retained so \
20565             conditional branch guards stay available to the resolver"
20566        );
20567        assert_eq!(
20568            DISTINCT_PER_KIND + 1,
20569            parsed
20570                .declarations()
20571                .iter()
20572                .filter(|unit| {
20573                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
20574                })
20575                .count(),
20576            "distinct macro redefinitions must remain available to temporal lookup"
20577        );
20578        assert_eq!(
20579            2,
20580            parsed
20581                .declarations()
20582                .iter()
20583                .filter(|unit| {
20584                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
20585                })
20586                .count(),
20587            "function overloads must remain distinct"
20588        );
20589
20590        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
20591        assert!(
20592            comparisons <= dedup_inputs * 4,
20593            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
20594        );
20595    }
20596
20597    #[test]
20598    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
20599        let source = r#"namespace absl {
20600ABSL_NAMESPACE_BEGIN namespace container_internal {
20601template <typename T>
20602class broken {
20603 public:
20604  using value_type = T;
20605  T operator->() const { return &operator*(); }
20606  using alias = value_type;
20607};
20608}
20609}
20610"#;
20611        let mut parser = tree_sitter::Parser::new();
20612        parser
20613            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20614            .unwrap();
20615        let tree = parser.parse(source, None).unwrap();
20616        let broken = find_class_named(tree.root_node(), source, "broken")
20617            .expect("the positive fixture must expose the broken class node");
20618        assert!(
20619            broken.has_error(),
20620            "the positive fixture must retain an internal parser error"
20621        );
20622        assert!(
20623            cpp_complete_class_body_close(broken).is_some(),
20624            "the positive fixture must expose a real class body close"
20625        );
20626        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20627        assert!(
20628            recovered.iter().any(|class| {
20629                class.scope_components == ["absl", "container_internal", "broken"]
20630            }),
20631            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
20632        );
20633    }
20634
20635    #[test]
20636    fn sentinel_recovery_keeps_members_after_nested_body_close() {
20637        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
20638NLOHMANN_BASIC_JSON_TPL_DECLARATION
20639class basic_json {
20640 private:
20641  union storage {
20642    int value;
20643  } data;
20644 public:
20645  using late_alias = int;
20646  late_alias value() const;
20647};
20648NLOHMANN_JSON_NAMESPACE_END
20649"#;
20650        let mut parser = tree_sitter::Parser::new();
20651        parser
20652            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20653            .unwrap();
20654        let tree = parser.parse(source, None).unwrap();
20655        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20656        let basic_json = recovered
20657            .iter()
20658            .find(|class| {
20659                class
20660                    .scope_components
20661                    .last()
20662                    .is_some_and(|name| name == "basic_json")
20663            })
20664            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
20665        let late_alias = source
20666            .find("late_alias value")
20667            .expect("late alias reference");
20668        assert!(
20669            basic_json.class_range.start_byte < late_alias
20670                && late_alias < basic_json.class_range.end_byte,
20671            "the recovered class range must include members after a nested close: {basic_json:#?}"
20672        );
20673    }
20674
20675    #[test]
20676    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
20677        let source = r#"namespace absl {
20678ABSL_NAMESPACE_BEGIN namespace container_internal {
20679template <typename T>
20680class broken {
20681 public:
20682  using value_type = T;
20683  T operator->() const { return &operator*(); }
20684}
20685}
20686"#;
20687        let mut parser = tree_sitter::Parser::new();
20688        parser
20689            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20690            .unwrap();
20691        let tree = parser.parse(source, None).unwrap();
20692        let broken = find_class_named(tree.root_node(), source, "broken")
20693            .expect("the negative fixture must expose the malformed class node");
20694        assert!(
20695            broken.has_error(),
20696            "the negative fixture must retain a parser error"
20697        );
20698        assert!(
20699            cpp_complete_class_body_close(broken).is_none(),
20700            "the malformed class must not expose a real body close"
20701        );
20702        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20703        assert!(
20704            recovered
20705                .iter()
20706                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
20707            "an incomplete class must not borrow the namespace close: {recovered:#?}"
20708        );
20709    }
20710
20711    #[test]
20712    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
20713        let source = r#"namespace absl {
20714ABSL_NAMESPACE_BEGIN namespace container_internal {
20715template <typename T>
20716struct broken {
20717  using value_type = T;
20718};
20719}
20720
20721#ifdef OWNER_DEF
20722template <typename T>
20723typename broken<T>::value_type broken<T>::method() {
20724  value_type value{};
20725  return value;
20726}
20727#endif
20728
20729namespace sibling {
20730template <typename T>
20731typename broken<T>::value_type broken<T>::other() {
20732  value_type value{};
20733  return value;
20734}
20735}
20736
20737ABSL_NAMESPACE_END
20738}
20739"#;
20740        let mut parser = tree_sitter::Parser::new();
20741        parser
20742            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20743            .unwrap();
20744        let tree = parser.parse(source, None).unwrap();
20745        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20746        let broken = recovered
20747            .iter()
20748            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20749            .expect("the sentinel class must be recovered");
20750        let method_start = source
20751            .find("typename broken<T>::value_type broken<T>::method()")
20752            .expect("guarded sibling owner");
20753        let method_end = source[method_start..]
20754            .find("\n}")
20755            .map(|offset| method_start + offset + 2)
20756            .expect("guarded sibling owner close");
20757        assert!(
20758            broken
20759                .owner_ranges
20760                .iter()
20761                .any(|owner| owner.range.start_byte <= method_start
20762                    && method_end <= owner.range.end_byte),
20763            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
20764        );
20765        let sibling_start = source
20766            .find("typename broken<T>::value_type broken<T>::other()")
20767            .expect("nested namespace sibling owner");
20768        assert!(
20769            broken
20770                .owner_ranges
20771                .iter()
20772                .all(|owner| owner.range.start_byte > sibling_start
20773                    || owner.range.end_byte <= sibling_start),
20774            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
20775        );
20776    }
20777
20778    #[test]
20779    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
20780        let source = r#"#ifdef OUTER
20781namespace absl {
20782ABSL_NAMESPACE_BEGIN namespace container_internal {
20783template <typename T>
20784struct broken {
20785  using value_type = T;
20786};
20787}
20788}
20789
20790#ifdef OWNER_DEF
20791template <typename T>
20792typename broken<T>::value_type broken<T>::method() {
20793  value_type value{};
20794  return value;
20795}
20796#endif
20797#endif
20798"#;
20799        let mut parser = tree_sitter::Parser::new();
20800        parser
20801            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20802            .unwrap();
20803        let tree = parser.parse(source, None).unwrap();
20804        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20805        let broken = recovered
20806            .iter()
20807            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20808            .expect("the sentinel class must be recovered");
20809        let method_start = source
20810            .find("typename broken<T>::value_type broken<T>::method()")
20811            .expect("outer sibling owner");
20812        assert!(
20813            broken
20814                .owner_ranges
20815                .iter()
20816                .all(|owner| owner.range.start_byte > method_start
20817                    || owner.range.end_byte <= method_start),
20818            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
20819        );
20820    }
20821
20822    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
20823    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
20824        let mut signatures = parsed
20825            .declarations()
20826            .iter()
20827            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
20828            .filter_map(|unit| unit.signature().map(str::to_string))
20829            .collect::<Vec<_>>();
20830        signatures.sort();
20831        signatures.dedup();
20832        signatures
20833    }
20834
20835    #[test]
20836    fn callable_parameter_types_come_from_the_ast_parameter_list() {
20837        let source = r#"
20838template <typename T, ENABLE_BYTES(T)>
20839Vec256<T> DupOdd(Vec256<T> value) { return value; }
20840
20841struct Visitor {
20842  void fail(this auto const& self) {}
20843};
20844"#;
20845        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
20846        let dup_odd = parsed
20847            .declarations()
20848            .iter()
20849            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
20850            .expect("DupOdd declaration");
20851        assert_eq!(
20852            dup_odd.signature(),
20853            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
20854        );
20855        assert_eq!(
20856            parsed
20857                .signature_metadata
20858                .get(dup_odd)
20859                .and_then(|metadata| metadata.first())
20860                .and_then(SignatureMetadata::callable_parameter_types),
20861            Some(["Vec256<T>".to_string()].as_slice())
20862        );
20863
20864        let fail = parsed
20865            .declarations()
20866            .iter()
20867            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
20868            .expect("explicit-object member");
20869        assert_eq!(fail.signature(), Some("(const this auto &)"));
20870        let metadata = parsed
20871            .signature_metadata
20872            .get(fail)
20873            .and_then(|metadata| metadata.first())
20874            .expect("explicit-object signature metadata");
20875        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
20876        assert!(
20877            metadata
20878                .callable_arity()
20879                .is_some_and(|arity| arity.accepts(0))
20880        );
20881    }
20882
20883    #[test]
20884    fn trailing_qualifiers_survive_parameter_list_whitespace() {
20885        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
20886        // declarator's structure, so an out-of-line definition that spells its
20887        // parameter list with different whitespace than the declaration must
20888        // still carry it.
20889        let source = r#"
20890struct Widget {
20891  bool multiline(int settings, int supprs) const;
20892  bool doublespace(int settings, int supprs) const;
20893  bool noexcept_multiline(int settings, int supprs) noexcept;
20894  bool ref_multiline(int settings, int supprs) &&;
20895};
20896bool
20897Widget::multiline (int settings,
20898                   int supprs) const
20899{ return settings + supprs > 0; }
20900bool Widget::doublespace(int settings,  int supprs) const { return true; }
20901bool Widget::noexcept_multiline(int settings,
20902                                int supprs) noexcept { return true; }
20903bool Widget::ref_multiline(int settings,
20904                           int supprs) && { return true; }
20905"#;
20906        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
20907        assert_eq!(
20908            vec!["(int, int) const".to_string()],
20909            identity_signatures(&parsed, "Widget.multiline")
20910        );
20911        assert_eq!(
20912            vec!["(int, int) const".to_string()],
20913            identity_signatures(&parsed, "Widget.doublespace")
20914        );
20915        assert_eq!(
20916            vec!["(int, int) noexcept".to_string()],
20917            identity_signatures(&parsed, "Widget.noexcept_multiline")
20918        );
20919        assert_eq!(
20920            vec!["(int, int) &&".to_string()],
20921            identity_signatures(&parsed, "Widget.ref_multiline")
20922        );
20923    }
20924
20925    #[test]
20926    fn macro_fragmented_plain_class_keeps_following_member_signature() {
20927        let source = r#"
20928struct CString {};
20929class CMessage {
20930public:
20931  CString GetParams(unsigned int index, unsigned int length = -1) const
20932      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
20933    return GetParamsColon(index, length);
20934  }
20935  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
20936};
20937CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
20938  return {};
20939}
20940"#;
20941        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
20942        assert_eq!(
20943            vec!["(unsigned int, unsigned int) const".to_string()],
20944            identity_signatures(&parsed, "CMessage.GetParamsColon")
20945        );
20946    }
20947
20948    #[test]
20949    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
20950        let source = r#"
20951#pragma once
20952#define DEMO_DEPRECATED(message)
20953namespace demo {
20954struct Base {
20955    static int aligned(int value) { return value; }
20956    int legacy(int value) const
20957        DEMO_DEPRECATED("use replacement()") { return value; }
20958    int replacement() const;
20959    void run(int value);
20960};
20961struct OtherBase {
20962    void run(int value);
20963    static int aligned(int value) { return value; }
20964};
20965struct Derived : Base {};
20966struct Override : Base {
20967    void run(int value);
20968    static int aligned(int value) { return value; }
20969};
20970struct RecoveredOverride : Base {
20971    int legacy(int value) const
20972        DEMO_DEPRECATED("use replacement()") { return value; }
20973    void run(int value);
20974};
20975struct Hidden : Base {
20976    void run(int first, int second);
20977    static int aligned(int first, int second) { return first + second; }
20978};
20979struct Ambiguous : Base, OtherBase {};
20980}
20981struct Global {};
20982"#;
20983        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
20984        let declarations = parsed.declarations();
20985        let fq_names = declarations
20986            .iter()
20987            .map(|unit| unit.fq_name())
20988            .collect::<std::collections::BTreeSet<_>>();
20989
20990        for expected in [
20991            "demo.Base",
20992            "demo.Base.aligned",
20993            "demo.Base.legacy",
20994            "demo.Base.replacement",
20995            "demo.Base.run",
20996            "demo.Derived",
20997            "demo.OtherBase",
20998            "demo.Override",
20999            "demo.RecoveredOverride",
21000            "demo.Hidden",
21001            "demo.Ambiguous",
21002            "Global",
21003        ] {
21004            assert!(
21005                fq_names.contains(expected),
21006                "missing {expected} from namespaced macro fragment: {declarations:#?}"
21007            );
21008        }
21009        assert!(
21010            !fq_names.contains("Derived"),
21011            "following class escaped its namespace: {declarations:#?}"
21012        );
21013        assert!(
21014            !fq_names.contains("demo.Global"),
21015            "global class crossed the recovered namespace boundary: {declarations:#?}"
21016        );
21017    }
21018
21019    #[test]
21020    fn trailing_qualifiers_still_separate_genuine_overloads() {
21021        // The qualifier must keep distinguishing the real C++ overload sets it
21022        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
21023        let source = r#"
21024struct Widget {
21025  int* slot(int index);
21026  const int* slot(int index) const;
21027  int log(int severity) &;
21028  int log(int severity) &&;
21029};
21030"#;
21031        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
21032        assert_eq!(
21033            vec!["(int)".to_string(), "(int) const".to_string()],
21034            identity_signatures(&parsed, "Widget.slot")
21035        );
21036        assert_eq!(
21037            vec!["(int) &".to_string(), "(int) &&".to_string()],
21038            identity_signatures(&parsed, "Widget.log")
21039        );
21040    }
21041
21042    #[test]
21043    fn virtual_specifier_is_not_part_of_the_identity_signature() {
21044        // `override` never appears on the out-of-line definition, and C++ does
21045        // not make it part of the signature, so it must not split the identity.
21046        let source = r#"
21047struct Base {
21048  virtual void run(int value) const;
21049};
21050struct Widget : Base {
21051  void run(int value) const override;
21052};
21053void Widget::run(int value) const {}
21054"#;
21055        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
21056        assert_eq!(
21057            vec!["(int) const".to_string()],
21058            identity_signatures(&parsed, "Widget.run")
21059        );
21060    }
21061
21062    #[test]
21063    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
21064        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
21065        // the function type, so a declaration that spells `const int` and a
21066        // definition that spells `int` are one entity.
21067        let source = r#"
21068struct Widget {
21069  bool value_params(const int settings, const int supprs);
21070  void pointee_const(const int* p);
21071  void pointer_const(int* const p);
21072  void both_const(const int* const p);
21073  void reference_const(const int& p);
21074  void array_const(const int values[4]);
21075};
21076bool Widget::value_params(int settings, int supprs) { return true; }
21077void Widget::pointer_const(int* p) {}
21078void Widget::both_const(const int* p) {}
21079"#;
21080        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
21081        assert_eq!(
21082            vec!["(int, int)".to_string()],
21083            identity_signatures(&parsed, "Widget.value_params")
21084        );
21085        assert_eq!(
21086            vec!["(int *)".to_string()],
21087            identity_signatures(&parsed, "Widget.pointer_const")
21088        );
21089        assert_eq!(
21090            vec!["(const int *)".to_string()],
21091            identity_signatures(&parsed, "Widget.both_const")
21092        );
21093        // The const that is not top-level still distinguishes the type.
21094        assert_eq!(
21095            vec!["(const int *)".to_string()],
21096            identity_signatures(&parsed, "Widget.pointee_const")
21097        );
21098        assert_eq!(
21099            vec!["(const int &)".to_string()],
21100            identity_signatures(&parsed, "Widget.reference_const")
21101        );
21102        assert_eq!(
21103            vec!["(const int [4])".to_string()],
21104            identity_signatures(&parsed, "Widget.array_const")
21105        );
21106    }
21107
21108    #[test]
21109    fn top_level_parameter_const_still_separates_pointee_overloads() {
21110        let source = r#"
21111struct Widget {
21112  void take(const int* p);
21113  void take(int* p);
21114};
21115"#;
21116        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
21117        assert_eq!(
21118            vec!["(const int *)".to_string(), "(int *)".to_string()],
21119            identity_signatures(&parsed, "Widget.take")
21120        );
21121    }
21122
21123    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
21124        let mut parser = tree_sitter::Parser::new();
21125        parser
21126            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21127            .unwrap();
21128        let tree = parser.parse(source, None).unwrap();
21129        let start = source.find(callable_name).expect("callable declaration");
21130        let declarator =
21131            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
21132        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
21133    }
21134
21135    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
21136        let mut shapes = comparable_shapes(source, callable_name);
21137        assert_eq!(1, shapes.len(), "{shapes:?}");
21138        match shapes.remove(0) {
21139            CppComparableSlot::Shape(shape) => shape,
21140            other => panic!("expected a comparable shape, got {other:?}"),
21141        }
21142    }
21143
21144    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
21145        let mut current = shape.root();
21146        loop {
21147            match shape.node(current) {
21148                CppComparableNode::Named { .. } => return shape.node(current),
21149                CppComparableNode::Pointer { inner, .. }
21150                | CppComparableNode::Reference { inner }
21151                | CppComparableNode::Array { inner } => current = *inner,
21152                CppComparableNode::Generic { base, .. } => current = *base,
21153            }
21154        }
21155    }
21156
21157    #[test]
21158    fn comparable_shape_keeps_pointee_const() {
21159        assert_ne!(
21160            sole_comparable_shape("void f(const char* p);", "f("),
21161            sole_comparable_shape("void f(char* p);", "f(")
21162        );
21163    }
21164
21165    #[test]
21166    fn comparable_shape_keeps_inner_pointer_const() {
21167        assert_ne!(
21168            sole_comparable_shape("void f(int** p);", "f("),
21169            sole_comparable_shape("void f(int* const* p);", "f(")
21170        );
21171    }
21172
21173    #[test]
21174    fn comparable_shape_drops_top_level_pointer_const() {
21175        assert_eq!(
21176            sole_comparable_shape("void f(int* const p);", "f("),
21177            sole_comparable_shape("void f(int* p);", "f(")
21178        );
21179    }
21180
21181    #[test]
21182    fn comparable_shape_drops_top_level_base_const() {
21183        assert_eq!(
21184            sole_comparable_shape("void f(const int p);", "f("),
21185            sole_comparable_shape("void f(int p);", "f(")
21186        );
21187    }
21188
21189    #[test]
21190    fn comparable_shape_decays_top_level_array_to_pointer() {
21191        assert_eq!(
21192            sole_comparable_shape("void f(int a[3]);", "f("),
21193            sole_comparable_shape("void f(int* a);", "f(")
21194        );
21195        assert_eq!(
21196            sole_comparable_shape("void f(int* a[3]);", "f("),
21197            sole_comparable_shape("void f(int** a);", "f(")
21198        );
21199    }
21200
21201    #[test]
21202    fn comparable_shape_keeps_array_behind_pointer() {
21203        assert_ne!(
21204            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
21205            sole_comparable_shape("struct S { void f(int** a); };", "f(")
21206        );
21207    }
21208
21209    #[test]
21210    fn comparable_shape_records_written_name_and_lexical_scope() {
21211        let declared =
21212            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
21213        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
21214        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
21215            panic!("named leaf");
21216        };
21217        assert_eq!(["Msg".to_string()].as_slice(), name.path());
21218        assert_eq!(
21219            ["ns".to_string(), "S".to_string()].as_slice(),
21220            name.lexical_scope()
21221        );
21222        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
21223            panic!("named leaf");
21224        };
21225        assert_eq!(
21226            ["ns".to_string(), "Msg".to_string()].as_slice(),
21227            name.path()
21228        );
21229        assert!(name.lexical_scope().is_empty());
21230        assert_ne!(declared, defined);
21231    }
21232
21233    #[test]
21234    fn comparable_shape_marks_sized_primitive_leaf() {
21235        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
21236        let CppComparableNode::Named {
21237            name, primitive, ..
21238        } = comparable_named_leaf(&shape)
21239        else {
21240            panic!("named leaf");
21241        };
21242        assert!(primitive);
21243        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
21244        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
21245    }
21246
21247    #[test]
21248    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
21249        assert_eq!(
21250            vec![CppComparableSlot::Unstructured],
21251            comparable_shapes("void f(void (*cb)(int));", "f(")
21252        );
21253    }
21254
21255    #[test]
21256    fn comparable_shape_reports_ellipsis_slot() {
21257        let shapes = comparable_shapes("void f(int a, ...);", "f(");
21258        assert_eq!(2, shapes.len(), "{shapes:?}");
21259        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
21260    }
21261
21262    #[test]
21263    fn comparable_shape_keeps_template_argument_const() {
21264        assert_ne!(
21265            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
21266            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
21267        );
21268    }
21269
21270    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
21271    /// file-scope tag that a later `struct inner *` at file scope may name.
21272    #[test]
21273    fn c_file_mints_aggregate_member_tag_at_file_scope() {
21274        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21275        let parsed = parse_cpp_declarations(source, "x.c");
21276        let declarations = parsed.declarations();
21277
21278        assert!(
21279            declarations
21280                .iter()
21281                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21282            "expected a file-scope inner tag, got {declarations:?}"
21283        );
21284        assert!(
21285            declarations
21286                .iter()
21287                .all(|unit| unit.fq_name() != "outer$inner"),
21288            "expected no nested identity, got {declarations:?}"
21289        );
21290        assert!(
21291            declarations
21292                .iter()
21293                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
21294        );
21295        // Members still belong to their own aggregate.
21296        assert!(
21297            declarations
21298                .iter()
21299                .any(|unit| unit.fq_name() == "inner.value")
21300        );
21301        assert!(
21302            declarations
21303                .iter()
21304                .any(|unit| unit.fq_name() == "outer.item")
21305        );
21306
21307        let outer = declarations
21308            .iter()
21309            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
21310            .expect("outer");
21311        assert!(
21312            parsed
21313                .children
21314                .get(outer)
21315                .into_iter()
21316                .flatten()
21317                .all(|child| child.fq_name() != "inner"),
21318            "the tag must not hang off the aggregate it is written inside: {:?}",
21319            parsed.children
21320        );
21321    }
21322
21323    /// A header carries no compilation language of its own, and a `.cpp`
21324    /// translation unit really does declare a nested class. Both keep exactly
21325    /// the C++ extraction they had before the C dialect existed.
21326    #[test]
21327    fn header_and_cpp_files_keep_nested_tag_identity() {
21328        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21329        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
21330            let parsed = parse_cpp_declarations(source, name);
21331            let declarations = parsed.declarations();
21332            assert!(
21333                declarations
21334                    .iter()
21335                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21336                "{name} must keep the nested identity, got {declarations:?}"
21337            );
21338            assert!(
21339                declarations.iter().all(|unit| unit.fq_name() != "inner"),
21340                "{name} must not mint a file-scope tag, got {declarations:?}"
21341            );
21342            assert!(
21343                declarations
21344                    .iter()
21345                    .any(|unit| unit.fq_name() == "outer$inner.value")
21346            );
21347        }
21348    }
21349
21350    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
21351    #[test]
21352    fn uppercase_c_extension_keeps_cpp_tag_scope() {
21353        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21354        let parsed = parse_cpp_declarations(source, "x.C");
21355        assert!(
21356            parsed
21357                .declarations()
21358                .iter()
21359                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
21360        );
21361    }
21362
21363    /// There is no such thing as a partially nested tag in C: every level of a
21364    /// nested aggregate chain lands at the same enclosing scope.
21365    #[test]
21366    fn c_file_mints_every_nesting_level_at_file_scope() {
21367        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
21368        let parsed = parse_cpp_declarations(source, "z.c");
21369        let declarations = parsed.declarations();
21370
21371        for tag in ["a", "b", "c"] {
21372            assert!(
21373                declarations
21374                    .iter()
21375                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
21376                "expected a file-scope {tag}, got {declarations:?}"
21377            );
21378        }
21379        assert!(
21380            declarations
21381                .iter()
21382                .all(|unit| !unit.fq_name().contains('$')),
21383            "no level may keep a nested identity, got {declarations:?}"
21384        );
21385        // Each member still belongs to the aggregate that declares it.
21386        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
21387        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
21388        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
21389    }
21390
21391    /// An enum tag is a tag; its enumerators stay members of the enum, which is
21392    /// what makes them ordinary identifiers at the enum's own (file) scope.
21393    #[test]
21394    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
21395        let source = "struct outer { enum color { RED, GREEN } c; };\n";
21396        let parsed = parse_cpp_declarations(source, "e.c");
21397        let declarations = parsed.declarations();
21398
21399        let color = declarations
21400            .iter()
21401            .find(|unit| unit.is_class() && unit.fq_name() == "color")
21402            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
21403        assert!(
21404            declarations
21405                .iter()
21406                .all(|unit| unit.fq_name() != "outer$color")
21407        );
21408        for enumerator in ["color.RED", "color.GREEN"] {
21409            assert!(
21410                declarations.iter().any(|unit| unit.fq_name() == enumerator),
21411                "expected {enumerator}, got {declarations:?}"
21412            );
21413        }
21414        let children = parsed
21415            .children
21416            .get(color)
21417            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
21418        assert!(
21419            ["color.RED", "color.GREEN"]
21420                .iter()
21421                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
21422            "enumerators must hang off their enum: {children:?}"
21423        );
21424    }
21425
21426    #[test]
21427    fn c_file_mints_member_list_union_at_file_scope() {
21428        let source = "struct outer { union inner { int a; float b; } item; };\n";
21429        let parsed = parse_cpp_declarations(source, "u.c");
21430        let declarations = parsed.declarations();
21431        assert!(
21432            declarations
21433                .iter()
21434                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21435            "expected a file-scope inner union, got {declarations:?}"
21436        );
21437        assert!(
21438            declarations
21439                .iter()
21440                .all(|unit| unit.fq_name() != "outer$inner")
21441        );
21442        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
21443        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
21444    }
21445
21446    /// A tag declared in a namespace member list is not a file-scope tag: the
21447    /// nearest enclosing non-aggregate scope is the namespace.
21448    #[test]
21449    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
21450        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
21451        let parsed = parse_cpp_declarations(source, "n.c");
21452        let declarations = parsed.declarations();
21453        let inner = declarations
21454            .iter()
21455            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
21456            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
21457        assert_eq!(inner.package_name(), "ns");
21458        assert!(
21459            declarations
21460                .iter()
21461                .all(|unit| unit.fq_name() != "ns.outer$inner")
21462        );
21463    }
21464
21465    /// Pins today's treatment of a tag declared inside a function body: the
21466    /// declaration walk does not descend into statement bodies, so no unit is
21467    /// minted for it in either dialect. C block scope is out of scope for the
21468    /// dialect change, and this test proves the change did not disturb it.
21469    #[test]
21470    fn function_local_tags_are_unchanged_in_both_dialects() {
21471        let source =
21472            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
21473        for name in ["y.c", "y.cpp"] {
21474            let parsed = parse_cpp_declarations(source, name);
21475            let declarations = parsed.declarations();
21476            assert!(
21477                declarations
21478                    .iter()
21479                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
21480                "{name}: {declarations:?}"
21481            );
21482            for tag in ["localtag", "deeper", "localtag$deeper"] {
21483                assert!(
21484                    declarations.iter().all(|unit| unit.fq_name() != tag),
21485                    "{name} must not mint {tag}, got {declarations:?}"
21486                );
21487            }
21488        }
21489    }
21490
21491    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
21492    /// re-scope: the typedef name is identical in both dialects.
21493    #[test]
21494    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
21495        let source = "typedef struct { int v; } T;\n";
21496        for name in ["t.c", "t.cpp"] {
21497            let parsed = parse_cpp_declarations(source, name);
21498            let declarations = parsed.declarations();
21499            assert!(
21500                declarations
21501                    .iter()
21502                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
21503                "{name}: {declarations:?}"
21504            );
21505        }
21506    }
21507
21508    /// The same principle for every anonymous aggregate the walk indexes, not
21509    /// just the typedef one: a promoted anonymous union in a member list, an
21510    /// anonymous struct given a receiver type by its declarator, a
21511    /// function-local aggregate, and one inside an inline member function
21512    /// body. The C++ reading used to mint none of these, so a pure C++
21513    /// workspace indexed no members at all (#3301).
21514    #[test]
21515    fn anonymous_aggregate_members_are_identical_in_both_dialects() {
21516        let sources = [
21517            "struct outer { union { int a; float b; }; };\n",
21518            "struct outer { struct { int *ops; } sock; };\n",
21519            "void run(void) { struct { int v; } item; }\n",
21520            "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
21521        ];
21522        for source in sources {
21523            let names = |name: &str| {
21524                parse_cpp_declarations(source, name)
21525                    .declarations()
21526                    .iter()
21527                    .map(|unit| unit.fq_name())
21528                    .collect::<std::collections::BTreeSet<_>>()
21529            };
21530            let c_names = names("a.c");
21531            let cpp_names = names("a.cpp");
21532            assert_eq!(
21533                c_names, cpp_names,
21534                "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
21535            );
21536            assert!(
21537                c_names
21538                    .iter()
21539                    .any(|name| name.contains('.') && name != "outer"),
21540                "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
21541            );
21542        }
21543    }
21544
21545    #[test]
21546    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
21547        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
21548        let parsed = parse_cpp_declarations(source, "socket.c");
21549        let declarations = parsed.declarations();
21550        assert_eq!(
21551            declarations
21552                .iter()
21553                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
21554                .count(),
21555            1,
21556            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
21557        );
21558        for expected in [
21559            "PAL_HANDLE",
21560            "PAL_HANDLE.sock",
21561            "PAL_HANDLE$sock",
21562            "PAL_HANDLE$sock.ops",
21563        ] {
21564            assert!(
21565                declarations.iter().any(|unit| unit.fq_name() == expected),
21566                "expected {expected}, got {declarations:?}"
21567            );
21568        }
21569    }
21570
21571    /// `class` is not C. Source that spells one in a `.c` file is not C code,
21572    /// so it keeps the C++ reading rather than acquiring a half-C identity.
21573    #[test]
21574    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
21575        let source = "class outer { class inner { int v; }; };\n";
21576        let c_parsed = parse_cpp_declarations(source, "k.c");
21577        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
21578        let c_declarations = c_parsed.declarations();
21579        let cpp_declarations = cpp_parsed.declarations();
21580        assert!(
21581            c_declarations
21582                .iter()
21583                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21584            "{c_declarations:?}"
21585        );
21586        assert_eq!(
21587            c_declarations
21588                .iter()
21589                .map(|unit| unit.fq_name())
21590                .collect::<std::collections::BTreeSet<_>>(),
21591            cpp_declarations
21592                .iter()
21593                .map(|unit| unit.fq_name())
21594                .collect::<std::collections::BTreeSet<_>>()
21595        );
21596    }
21597
21598    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
21599    /// every (node, class-like name) pair a tree offers, and require the same
21600    /// answer from both.
21601    ///
21602    /// The release build has no `debug_assertions` agreement check, so this is
21603    /// what pins the two together there.  Both query orders are exercised:
21604    /// document order is what the walk does, and reverse order proves that a
21605    /// question about an earlier byte than one already answered is still
21606    /// filtered back to its own prefix rather than answered from the wider
21607    /// fold.
21608    ///
21609    /// Returns how many questions were answered with a namespace, so a fixture
21610    /// can assert it actually reached the path (#2754).
21611    fn namespace_forward_scan_agreement(source: &str) -> usize {
21612        let mut parser = tree_sitter::Parser::new();
21613        parser
21614            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21615            .unwrap();
21616        let tree = parser.parse(source, None).unwrap();
21617        let root = tree.root_node();
21618        let ancestry = ParentIndex::new(root);
21619
21620        let mut nodes = Vec::new();
21621        let mut names = std::collections::BTreeSet::new();
21622        let mut cursor = root.walk();
21623        let mut stack = vec![root];
21624        while let Some(node) = stack.pop() {
21625            if matches!(
21626                node.kind(),
21627                "class_specifier" | "struct_specifier" | "union_specifier"
21628            ) && let Some(name) = class_like_name(node, source, &ancestry)
21629            {
21630                names.insert(name);
21631            }
21632            nodes.push(node);
21633            stack.extend(node.named_children(&mut cursor));
21634        }
21635        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
21636        assert!(!names.is_empty(), "fixture declares no class-like name");
21637
21638        let mut answered = 0usize;
21639        for reversed in [false, true] {
21640            let mut scan = CppNamespaceForwardScan::default();
21641            let ordered: Vec<_> = if reversed {
21642                nodes.iter().rev().copied().collect()
21643            } else {
21644                nodes.clone()
21645            };
21646            answered = 0;
21647            for node in ordered {
21648                for name in &names {
21649                    scan.advance_to(root, node.start_byte(), source, &ancestry);
21650                    let carried = scan.unique_earlier_forward(name, node);
21651                    assert_eq!(
21652                        carried,
21653                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
21654                        "carried-forward scan and prefix scan disagree about {name} at \
21655                         {} node starting at byte {} (reversed order: {reversed})",
21656                        node.kind(),
21657                        node.start_byte()
21658                    );
21659                    answered += usize::from(carried.is_some());
21660                }
21661            }
21662        }
21663        answered
21664    }
21665
21666    /// A malformed namespace whose forward declarations are the only identity
21667    /// signal left for the class definitions tree-sitter pushed out to file
21668    /// scope.  Both recovered classes open the guard; only the first one is
21669    /// separated from the namespace by nothing but recovery trivia, so only the
21670    /// first one borrows.  The carried-forward scan has to reproduce both
21671    /// answers.
21672    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
21673namespace ns {
21674class Widget;
21675class Gadget;
21676int x = ;
21677}
21678class API Widget {
21679public:
21680    void first();
21681};
21682class API Gadget {
21683public:
21684    void second();
21685};
21686"#;
21687
21688    #[test]
21689    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
21690        assert!(
21691            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
21692            "the fixture must actually reach the namespace-borrow path"
21693        );
21694
21695        // Nothing here may be answered, and the two paths have to agree about
21696        // that too: a clean namespace is not an identity proof, two forwards of
21697        // one name are ambiguous rather than a guess, and a forward inside a
21698        // function body is not at namespace scope.
21699        for source in [
21700            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
21701            r#"#define API
21702namespace ns {
21703class Widget;
21704class Widget;
21705int x = ;
21706}
21707class API Widget {
21708public:
21709    void method();
21710};
21711"#,
21712            r#"#define API
21713namespace ns {
21714void host() {
21715    class Widget;
21716}
21717int x = ;
21718}
21719class API Widget {
21720public:
21721    void method();
21722};
21723"#,
21724        ] {
21725            assert_eq!(
21726                namespace_forward_scan_agreement(source),
21727                0,
21728                "no borrow is justified here: {source}"
21729            );
21730        }
21731    }
21732
21733    /// The fold is incremental, so a walk that asks about steadily later bytes
21734    /// must never re-fold a node an earlier question already folded, and must
21735    /// never skip one that lies between two questions.
21736    #[test]
21737    fn carried_forward_namespace_scan_folds_each_node_once() {
21738        let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
21739        let mut parser = tree_sitter::Parser::new();
21740        parser
21741            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21742            .unwrap();
21743        let tree = parser.parse(source, None).unwrap();
21744        let root = tree.root_node();
21745        let ancestry = ParentIndex::new(root);
21746
21747        let mut incremental = CppNamespaceForwardScan::default();
21748        for cutoff in 0..=source.len() {
21749            incremental.advance_to(root, cutoff, source, &ancestry);
21750        }
21751        let mut whole = CppNamespaceForwardScan::default();
21752        whole.advance_to(root, source.len(), source, &ancestry);
21753
21754        let mut incremental_shape: Vec<_> = incremental
21755            .forwards
21756            .iter()
21757            .map(|(name, forwards)| {
21758                (
21759                    name.clone(),
21760                    forwards
21761                        .iter()
21762                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
21763                        .collect::<Vec<_>>(),
21764                )
21765            })
21766            .collect();
21767        let mut whole_shape: Vec<_> = whole
21768            .forwards
21769            .iter()
21770            .map(|(name, forwards)| {
21771                (
21772                    name.clone(),
21773                    forwards
21774                        .iter()
21775                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
21776                        .collect::<Vec<_>>(),
21777                )
21778            })
21779            .collect();
21780        incremental_shape.sort();
21781        whole_shape.sort();
21782        for (_, forwards) in &mut incremental_shape {
21783            forwards.sort();
21784        }
21785        for (_, forwards) in &mut whole_shape {
21786            forwards.sort();
21787        }
21788
21789        assert!(!whole_shape.is_empty(), "fixture folds no forward");
21790        assert_eq!(
21791            incremental_shape, whole_shape,
21792            "one byte at a time must fold exactly what one whole pass folds"
21793        );
21794    }
21795
21796    /// Drive the region reparse and the whitespace-padded reparse it replaced
21797    /// over the same region and require identical trees.
21798    ///
21799    /// The release build has no `debug_assertions` agreement check, so this is
21800    /// what pins the two together there. Each body is reparsed at its own
21801    /// offset and again after a long prefix, because the prefix is the whole
21802    /// difference between the two techniques: the padded parse lexes it as
21803    /// whitespace, the included-range parse never sees it, and the tree has to
21804    /// come out the same either way (#2788).
21805    fn fragmented_class_reparse_agreement(body: &str) {
21806        for prefix in [
21807            String::new(),
21808            "// leading comment\n".to_string(),
21809            // A body starts just after its class head's `{`, which is normally
21810            // mid-line: the padded parse then has spaces before the region on
21811            // the region's own line, and the included-range parse has nothing
21812            // at all before it.
21813            "class Widget : public Base { ".to_string(),
21814            "namespace filler {\n".to_string()
21815                + &"struct Filler { int member; };\n".repeat(200)
21816                + "}\n",
21817            "namespace filler {\n".to_string()
21818                + &"struct Filler { int member; };\n".repeat(200)
21819                + "}\nclass Widget : public Base { ",
21820        ] {
21821            let source = format!("{prefix}{body}");
21822            let start = prefix.len();
21823            let end = source.len();
21824            let region = cpp_reparse_fragmented_class_body(&source, start, end)
21825                .expect("the region reparse must produce a tree");
21826            let padded = cpp_reparse_padded_class_body(&source, start, end)
21827                .expect("the padded reparse must produce a tree");
21828            assert_eq!(
21829                cpp_tree_shape(&region),
21830                cpp_tree_shape(&padded),
21831                "region and padded reparse disagree at offset {start} of {end} bytes"
21832            );
21833            assert_eq!(
21834                region.root_node().start_byte(),
21835                start,
21836                "the reparsed region keeps its original offsets"
21837            );
21838        }
21839    }
21840
21841    #[test]
21842    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
21843        // A conditional immediately after an access label: the shape the padded
21844        // technique was kept for, because the directive and its macro name
21845        // become an ERROR plus the following declaration's apparent type.
21846        fragmented_class_reparse_agreement(
21847            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
21848        );
21849        fragmented_class_reparse_agreement(
21850            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
21851        );
21852        // The merged inline constructor and the nested fragmented bodies the
21853        // #938 recovery reads out of a reparse.
21854        fragmented_class_reparse_agreement(
21855            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
21856        );
21857        fragmented_class_reparse_agreement(
21858            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
21859        );
21860        // A body that is not member-shaped at all still has to reparse the same
21861        // way, because the admission gate reads the tree to reject it.
21862        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
21863    }
21864
21865    fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
21866        let mut siblings = Vec::new();
21867        let mut anchor = node;
21868        while let Some(parent) = anchor.parent() {
21869            let at_translation_unit = parent.kind() == "translation_unit";
21870            let mut sibling = anchor.next_named_sibling();
21871            while let Some(current) = sibling {
21872                if at_translation_unit
21873                    && (current.kind() == "namespace_definition"
21874                        || (current.kind() == "function_definition"
21875                            && first_class_like_child(current).is_some()))
21876                {
21877                    return siblings;
21878                }
21879                siblings.push(current);
21880                if cpp_is_stray_close_brace(current, source) {
21881                    if let Some(semicolon) = current
21882                        .next_named_sibling()
21883                        .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
21884                    {
21885                        siblings.push(semicolon);
21886                    }
21887                    return siblings;
21888                }
21889                if current.start_byte() >= node.end_byte()
21890                    && matches!(current.kind(), "ERROR" | "labeled_statement")
21891                    && cpp_nested_stray_close_brace(current, source).is_some()
21892                {
21893                    return siblings;
21894                }
21895                sibling = current.next_named_sibling();
21896            }
21897            anchor = parent;
21898        }
21899        siblings
21900    }
21901
21902    #[test]
21903    fn following_named_siblings_cursor_matches_native_traversal() {
21904        // The damaged #2924 shape scatters members across container siblings,
21905        // and the anonymous punctuation forces the cursor to filter children
21906        // exactly as Node::next_named_sibling does.
21907        let source = r#"
21908namespace api {
21909class PROJECT_API(2, 0) Widget : public virtual Base {
21910  public:
21911    Widget() {}
21912    void first();
21913};
21914enum After { Value };
21915}
21916"#;
21917        let mut parser = tree_sitter::Parser::new();
21918        parser
21919            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21920            .unwrap();
21921        let tree = parser.parse(source, None).unwrap();
21922        let root = tree.root_node();
21923        assert!(root.has_error(), "fixture must remain damaged");
21924
21925        let ancestry = ParentIndex::new(root);
21926        let mut stack = vec![root];
21927        let mut checked = 0;
21928        let mut damaged = 0;
21929        let mut anonymous = 0;
21930        let mut nonempty_sequences = 0;
21931        let mut named_then_anonymous = 0;
21932        while let Some(node) = stack.pop() {
21933            let expected = native_following_named_siblings(node, source)
21934                .iter()
21935                .map(Node::id)
21936                .collect::<Vec<_>>();
21937            let actual = cpp_following_named_siblings(node, source, &ancestry)
21938                .iter()
21939                .map(Node::id)
21940                .collect::<Vec<_>>();
21941            assert_eq!(
21942                actual,
21943                expected,
21944                "cursor traversal diverged at byte {}",
21945                node.start_byte()
21946            );
21947
21948            let mut cursor = node.walk();
21949            let children = node.children(&mut cursor).collect::<Vec<_>>();
21950            stack.extend(children.iter().copied());
21951            checked += 1;
21952            damaged += usize::from(node.is_error() || node.is_missing());
21953            anonymous += usize::from(!node.is_named());
21954            nonempty_sequences += usize::from(!actual.is_empty());
21955            named_then_anonymous += usize::from(
21956                children
21957                    .windows(2)
21958                    .any(|pair| pair[0].is_named() && !pair[1].is_named()),
21959            );
21960        }
21961        assert!(checked > 0, "fixture must provide nodes to compare");
21962        assert!(damaged > 0, "fixture must cover damaged nodes");
21963        assert!(anonymous > 0, "fixture must cover anonymous nodes");
21964        assert!(
21965            nonempty_sequences > 0,
21966            "fixture must exercise following named siblings"
21967        );
21968        assert!(
21969            named_then_anonymous > 0,
21970            "fixture must exercise anonymous nodes following a named anchor"
21971        );
21972    }
21973
21974    /// A header shaped like the generated ones this walk is slow on: many
21975    /// enums, classes whose members share the enums' names, nested enums, an
21976    /// ownerless enumerator, and namespaced repeats of all of it.
21977    fn many_enums_and_mixed_declarations() -> String {
21978        let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
21979        for index in 0..40 {
21980            let _ = write!(
21981                source,
21982                "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
21983                 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
21984                 class Color{index}Like {{ public: int member{index}; }};\n"
21985            );
21986        }
21987        source.push_str("namespace outer {\n");
21988        for index in 0..20 {
21989            let _ = write!(
21990                source,
21991                "enum Shade{index} {{ DARK{index} }};\n\
21992                 struct Shade{index}Holder {{ int field{index}; }};\n"
21993            );
21994        }
21995        source.push_str("}\n");
21996        source
21997    }
21998
21999    /// Drive [`CppFieldOwnerIndex`] and the declaration scan it replaced over
22000    /// every question a fixture's declarations can ask, and require the same
22001    /// answer from both.
22002    ///
22003    /// The release build has no `debug_assertions` agreement check, so this is
22004    /// what pins the two together there. The index is fed one declaration at a
22005    /// time and every question is re-asked after each one, which is what proves
22006    /// the incremental record agrees -- a whole-set rebuild would pass a weaker
22007    /// test. Each of the fixture's own fields is also fed in restated as a
22008    /// declaration of another file: the scan ignores those because it asks
22009    /// about the asking unit's own source, and the index has to ignore them for
22010    /// the same reason.
22011    ///
22012    /// Returns how many questions the fixture answered `true`, so a caller can
22013    /// assert that it actually reached the path (#2786).
22014    fn field_owner_index_agreement(source: &str, name: &str) -> usize {
22015        let parsed = parse_cpp_declarations(source, name);
22016        let file = ProjectFile::new(std::env::temp_dir(), name);
22017        let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
22018
22019        let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
22020        declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
22021
22022        let foreign: Vec<CodeUnit> = declarations
22023            .iter()
22024            .filter(|unit| unit.kind() == CodeUnitType::Field)
22025            .map(|unit| {
22026                CodeUnit::new_fq(
22027                    elsewhere.clone(),
22028                    unit.kind(),
22029                    unit.package_name().to_string(),
22030                    unit.short_name().to_string(),
22031                    unit.fq().clone(),
22032                )
22033            })
22034            .collect();
22035
22036        // One owner chain deeper than any C++ short name reaches today
22037        // (`cpp_member_fq`: at most one `.`, separating the owner chain from
22038        // the member). The scan asks `starts_with("owner.")`, so a field like
22039        // this answers for every owner in its chain, and the index has to
22040        // record every one of them rather than only the innermost.
22041        let mut packages: Vec<String> = declarations
22042            .iter()
22043            .map(|unit| unit.package_name().to_string())
22044            .collect();
22045        packages.push(String::new());
22046        packages.sort();
22047        packages.dedup();
22048        let deeper: Vec<CodeUnit> = packages
22049            .iter()
22050            .map(|package_name| {
22051                CodeUnit::new_fq(
22052                    file.clone(),
22053                    CodeUnitType::Field,
22054                    package_name.clone(),
22055                    "SynthOwner.middle.leaf".to_string(),
22056                    cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
22057                )
22058            })
22059            .collect();
22060
22061        // Every (package, owner) pair anything could ask about: each unit's own
22062        // short name, each dotted prefix of it, and the empty owner an
22063        // anonymous enum asks with (#2140).
22064        let mut questions: Vec<(String, String)> = Vec::new();
22065        for unit in declarations.iter().chain(deeper.iter()) {
22066            let package_name = unit.package_name().to_string();
22067            let short_name = unit.short_name();
22068            questions.push((package_name.clone(), short_name.to_string()));
22069            questions.push((package_name.clone(), String::new()));
22070            for (offset, _) in short_name.match_indices('.') {
22071                questions.push((package_name.clone(), short_name[..offset].to_string()));
22072            }
22073        }
22074        questions.sort();
22075        questions.dedup();
22076
22077        let mut index = CppFieldOwnerIndex::default();
22078        let mut recorded: Vec<&CodeUnit> = Vec::new();
22079        let mut answered = 0usize;
22080        for unit in foreign
22081            .iter()
22082            .chain(declarations.iter())
22083            .chain(deeper.iter())
22084        {
22085            index.record(unit, &file);
22086            recorded.push(unit);
22087            for (package_name, owner_short_name) in &questions {
22088                let carried = index.owns_fields(package_name, owner_short_name);
22089                assert_eq!(
22090                    carried,
22091                    cpp_declarations_hold_owned_fields(
22092                        recorded.iter().copied(),
22093                        &file,
22094                        package_name,
22095                        owner_short_name
22096                    ),
22097                    "the carried field index and the declaration scan disagree about \
22098                     {package_name:?}/{owner_short_name:?} after recording {}",
22099                    unit.fq_name()
22100                );
22101                answered += usize::from(carried);
22102            }
22103        }
22104
22105        // The whole-set build the first question performs must land on the same
22106        // index the incremental record built.
22107        let rebuilt = CppFieldOwnerIndex::of(
22108            foreign
22109                .iter()
22110                .chain(declarations.iter())
22111                .chain(deeper.iter()),
22112            &file,
22113        );
22114        for (package_name, owner_short_name) in &questions {
22115            assert_eq!(
22116                rebuilt.owns_fields(package_name, owner_short_name),
22117                index.owns_fields(package_name, owner_short_name),
22118                "a rebuilt index must answer what the incremental one answers for \
22119                 {package_name:?}/{owner_short_name:?}"
22120            );
22121        }
22122        answered
22123    }
22124
22125    /// The one thing the field index cannot absorb by addition: a deferred
22126    /// replacement of a declaration that owns children removes those children.
22127    ///
22128    /// The first enum builds the index, the struct records `Color.RED` into it,
22129    /// the body-less second `Color` replaces the first and takes `Color.RED`
22130    /// with it, and the last enum then asks about owner `Color`. An index that
22131    /// survived that removal answers `true` where the declarations say `false`,
22132    /// which is exactly what the in-walk agreement assertion catches (#2786).
22133    #[test]
22134    fn a_replacement_that_removes_children_drops_the_field_index() {
22135        let source =
22136            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
22137        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
22138        let mut names: Vec<_> = parsed
22139            .declarations()
22140            .iter()
22141            .map(|unit| unit.fq_name())
22142            .collect();
22143        names.sort();
22144        assert_eq!(
22145            names,
22146            vec![
22147                "Color".to_string(),
22148                "First".to_string(),
22149                "First.A".to_string()
22150            ],
22151            "the replaced Color owns no field any more"
22152        );
22153    }
22154
22155    /// A recovery that re-declares what the file already declared mints
22156    /// nothing.
22157    ///
22158    /// The reparse walk replaces the outer `Widget`, which removes its method,
22159    /// and then re-creates that method from the region. Creation alone would
22160    /// call the method recovered; it was there before the recovery opened, so
22161    /// the recovered set is empty and only the region's own reparse window is
22162    /// recorded (#2787).
22163    #[test]
22164    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
22165        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
22166                      BEGIN_NS\n\
22167                      namespace demo { struct Widget { void doWork(); }; }\n\
22168                      END_NS\n";
22169        let parsed = parse_cpp_declarations(source, "restored.cpp");
22170        let recovered: Vec<String> = parsed
22171            .materialization_records
22172            .iter()
22173            .filter_map(|record| match record {
22174                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
22175                _ => None,
22176            })
22177            .collect();
22178        assert!(
22179            recovered.is_empty(),
22180            "the region declares nothing the file did not already declare: {recovered:?}"
22181        );
22182        let mut names: Vec<String> = parsed
22183            .declarations()
22184            .iter()
22185            .map(|unit| unit.fq_name())
22186            .collect();
22187        names.sort();
22188        assert_eq!(
22189            names,
22190            vec![
22191                "demo".to_string(),
22192                "demo.Widget".to_string(),
22193                "demo.Widget.doWork".to_string(),
22194            ]
22195        );
22196    }
22197
22198    /// Four macro-sentinel recoveries in one file (#941). Each one must record
22199    /// exactly the declarations it minted -- not the ones an earlier recovery
22200    /// minted, and not the file's other declarations -- in start-byte order,
22201    /// and each record must carry its own reparse window (#2787).
22202    #[test]
22203    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
22204        let mut source = String::new();
22205        for index in 0..4 {
22206            let _ = write!(
22207                source,
22208                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
22209            );
22210        }
22211        source.push_str("void outside() {}\n");
22212        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
22213
22214        let recovered: Vec<(String, (usize, usize))> = parsed
22215            .materialization_records
22216            .iter()
22217            .filter_map(|record| match record {
22218                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
22219                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
22220                }
22221                _ => None,
22222            })
22223            .collect();
22224
22225        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
22226        for index in 0..4 {
22227            // The window the reparse covers: everything after the opening
22228            // sentinel token up to the newline before the closing one.
22229            let region = format!("namespace demo{index}");
22230            let region_start = source.find(&region).expect("each region is in the source");
22231            let start = source[..region_start]
22232                .rfind("BEGIN_NS")
22233                .expect("each region opens with a sentinel")
22234                + "BEGIN_NS".len();
22235            let end = start
22236                + source[start..]
22237                    .find("END_NS")
22238                    .expect("each region closes with a sentinel")
22239                - 1;
22240            let window = (start, end);
22241            for name in [
22242                format!("demo{index}"),
22243                format!("demo{index}.Widget{index}"),
22244                format!("demo{index}.Widget{index}.doWork{index}"),
22245            ] {
22246                expected.push((name, window));
22247            }
22248        }
22249        assert_eq!(
22250            recovered, expected,
22251            "each recovery records its own minted declarations, in order"
22252        );
22253        assert!(
22254            parsed
22255                .declarations()
22256                .iter()
22257                .any(|unit| unit.fq_name() == "outside"),
22258            "the declaration outside every region stays parsed and unrecovered"
22259        );
22260    }
22261
22262    #[test]
22263    fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
22264        assert!(
22265            field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
22266            "the fixture must actually own fields"
22267        );
22268
22269        // The shapes that make the two implementations diverge if the index
22270        // records the wrong keys: a nested enum's owner is its whole dotted
22271        // chain, a class named like an enum owns fields under that same name, a
22272        // `$` in a short name is an owner separator the dotted prefix rule must
22273        // not split on, and the same enum name in two namespaces is two owners.
22274        for (source, name) in [
22275            ("struct S { enum E { V }; };\n", "nested.hpp"),
22276            (
22277                "enum Color { RED };\nstruct Color { int RED; };\n",
22278                "class-like.c",
22279            ),
22280            ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
22281            (
22282                "enum E { V };\nnamespace ns { enum E { V }; }\n",
22283                "repeated.hpp",
22284            ),
22285            ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
22286        ] {
22287            field_owner_index_agreement(source, name);
22288        }
22289    }
22290
22291    /// `blocks` copies of one plain class-with-methods template. The class the
22292    /// question is about is always the first one, so the only thing that
22293    /// changes between two of these sources is how much unrelated tree
22294    /// surrounds it.
22295    fn repeated_class_blocks(blocks: usize) -> String {
22296        let mut source = String::from("namespace demo {\n");
22297        for index in 0..blocks {
22298            let _ = write!(
22299                source,
22300                "\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"
22301            );
22302        }
22303        source.push_str("\n}\n");
22304        source
22305    }
22306
22307    /// #1496: asking whether a recovered class shape claims one range must cost
22308    /// the path to that range, not a pass over the whole translation unit.
22309    ///
22310    /// The C++ inverse scan asks this once per candidate type reference, so a
22311    /// whole-tree walk makes one file's scan quadratic in its own size. Both
22312    /// sources here answer `None` -- these are plain classes that no recovery
22313    /// shape claims -- which is exactly the case that used to pay full price.
22314    #[test]
22315    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
22316        let mut answers = Vec::new();
22317        let mut visits = Vec::new();
22318        let mut node_counts = Vec::new();
22319        for blocks in [200usize, 400] {
22320            let source = repeated_class_blocks(blocks);
22321            let mut parser = tree_sitter::Parser::new();
22322            parser
22323                .set_language(&tree_sitter_cpp::LANGUAGE.into())
22324                .unwrap();
22325            let tree = parser.parse(&source, None).unwrap();
22326            let start_byte = source.find("class Widget0 ").expect("first class");
22327            let end_byte = start_byte
22328                + source[start_byte..]
22329                    .find("};")
22330                    .expect("first class terminator")
22331                + "};".len();
22332            let range = Range {
22333                start_byte,
22334                end_byte,
22335                start_line: 0,
22336                end_line: 0,
22337            };
22338            reset_recovered_class_body_node_visits_for_test();
22339            let recovered_export_classes =
22340                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
22341            answers.push(recovered_class_body_at(
22342                &recovered_export_classes,
22343                tree.root_node(),
22344                &source,
22345                "Widget0",
22346                &range,
22347            ));
22348            visits.push(recovered_class_body_node_visits_for_test());
22349            let mut nodes = 0usize;
22350            let mut stack = vec![tree.root_node()];
22351            while let Some(node) = stack.pop() {
22352                nodes += 1;
22353                let mut cursor = node.walk();
22354                stack.extend(node.named_children(&mut cursor));
22355            }
22356            node_counts.push(nodes);
22357        }
22358
22359        assert_eq!(
22360            answers,
22361            vec![None, None],
22362            "no recovered shape claims a plain class"
22363        );
22364        assert_eq!(
22365            visits[0], visits[1],
22366            "the walk must follow the range's own path, so doubling the unrelated \
22367             classes must not change the node count: {visits:?} over trees of \
22368             {node_counts:?} nodes"
22369        );
22370        assert!(
22371            visits[1] * 20 < node_counts[1],
22372            "the walk must stay far below one pass over the tree: {visits:?} over \
22373             trees of {node_counts:?} nodes"
22374        );
22375    }
22376
22377    #[test]
22378    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
22379        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
22380        let mut parser = tree_sitter::Parser::new();
22381        parser
22382            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22383            .expect("C++ grammar");
22384        let tree = parser.parse(source, None).expect("fixture tree");
22385        let mut stack = vec![tree.root_node()];
22386        let mut recovered = None;
22387        while let Some(node) = stack.pop() {
22388            if node.kind() == "field_declaration"
22389                && let Some(field) = recovered_function_like_field_declarator(node, source)
22390            {
22391                recovered = Some((node, field.name));
22392                break;
22393            }
22394            let mut cursor = node.walk();
22395            stack.extend(node.named_children(&mut cursor));
22396        }
22397        let (declaration, name) = recovered.unwrap_or_else(|| {
22398            panic!(
22399                "pointer-wrapped macro field was not recovered: {}",
22400                tree.root_node().to_sexp()
22401            )
22402        });
22403        assert_eq!(node_text(name, source), "handshake");
22404        let recovered =
22405            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
22406        assert_eq!(recovered.pointer_depth(), 1);
22407        assert_eq!(
22408            render_cpp_field_signature(declaration, name, source),
22409            "struct handshake * handshake;"
22410        );
22411    }
22412
22413    #[test]
22414    fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
22415        let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
22416        let mut parser = tree_sitter::Parser::new();
22417        parser
22418            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22419            .expect("C++ grammar");
22420        let tree = parser.parse(source, None).expect("fixture tree");
22421        let mut stack = vec![tree.root_node()];
22422        let mut recovered = None;
22423        while let Some(node) = stack.pop() {
22424            if let Some(field) = recovered_pyobject_head_field(node, source) {
22425                recovered = Some((node, field));
22426                break;
22427            }
22428            let mut cursor = node.walk();
22429            stack.extend(node.named_children(&mut cursor));
22430        }
22431        let (declaration, recovered) = recovered.unwrap_or_else(|| {
22432            panic!(
22433                "pointer field after PyObject_HEAD was not recovered: {}",
22434                tree.root_node().to_sexp()
22435            )
22436        });
22437        assert_eq!(node_text(recovered.name, source), "image");
22438        assert_eq!(recovered.pointer_depth(), 1);
22439        assert_eq!(
22440            render_cpp_field_signature(declaration, recovered.declarator, source),
22441            "ImagingObject *image;"
22442        );
22443    }
22444}