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 = fragmented_export_sibling_class_boundary(node, source);
678    let boundary_index = boundary.and_then(|boundary| {
679        siblings
680            .iter()
681            .position(|candidate| same_node(*candidate, boundary))
682    });
683    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
684    let mut sibling_index = 0;
685    // A complete recovered class's synthetic wrapper is immediately followed
686    // by its displaced semicolon (comments and a trailing attribute macro --
687    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
688    // may sit between the body and that semicolon). Only scan for a later
689    // stray close when real member siblings intervene; otherwise every earlier
690    // complete class would borrow the next malformed class's close and claim
691    // its members. The trailing-attribute case is the gtest shape: the scan
692    // borrowed a close ~1900 lines later and re-owned a following
693    // `namespace testing { namespace internal {` block as class members,
694    // doubling the package path ("testing::internal::testing::internal") and
695    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
696    // boundary assert (#2297).
697    while let Some(current) = siblings.get(sibling_index).copied() {
698        if current.kind() == "comment" {
699            sibling_index += 1;
700            continue;
701        }
702        if is_trailing_attribute_macro_sibling(current) {
703            sibling_index += 1;
704            continue;
705        }
706        if cpp_is_stray_semicolon(current, source) {
707            return None;
708        }
709        break;
710    }
711    while let Some(current) = siblings.get(sibling_index).copied() {
712        let next = siblings.get(sibling_index + 1).copied();
713        if cpp_is_stray_close_brace(current, source)
714            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
715        {
716            let semicolon = next.expect("checked above");
717            return Some(FragmentedExportBody {
718                reparse_start,
719                reparse_end: current.start_byte(),
720                class_range: Range {
721                    start_byte: node.start_byte(),
722                    end_byte: semicolon.end_byte(),
723                    start_line: node.start_position().row + 1,
724                    end_line: semicolon.end_position().row + 1,
725                },
726            });
727        }
728        // When the final access label keeps the class close in its malformed
729        // declaration body, tree-sitter nests the lone `}` ERROR below the
730        // label instead of exposing it as a direct sibling. Search only the
731        // scattered siblings after the synthetic wrapper. The first such
732        // close is the class terminator because nested class bodies retain
733        // their own balanced class_specifier nodes.
734        if current.start_byte() >= body.end_byte()
735            && let Some(close) = cpp_nested_stray_close_brace(current, source)
736        {
737            return Some(FragmentedExportBody {
738                reparse_start,
739                reparse_end: close.start_byte(),
740                class_range: Range {
741                    start_byte: node.start_byte(),
742                    end_byte: current.end_byte(),
743                    start_line: node.start_position().row + 1,
744                    end_line: current.end_position().row + 1,
745                },
746            });
747        }
748        sibling_index += 1;
749    }
750    boundary.map(|boundary| FragmentedExportBody {
751        reparse_start,
752        reparse_end: boundary.start_byte(),
753        class_range: Range {
754            start_byte: node.start_byte(),
755            end_byte: boundary.start_byte(),
756            start_line: node.start_position().row + 1,
757            end_line: boundary.start_position().row + 1,
758        },
759    })
760}
761
762/// Find a later macro-export class that tree-sitter lifted through an enclosing
763/// preprocessor container. A class that is still a direct sibling can be a
764/// nested member of the current fragmented class, so only a changed parent is
765/// a proven boundary between the two recovered class envelopes.
766fn fragmented_export_sibling_class_boundary<'tree>(
767    node: Node<'tree>,
768    source: &str,
769) -> Option<Node<'tree>> {
770    let node_parent = node.parent()?;
771    cpp_following_named_siblings(node, source, &ParentIndex::unindexed())
772        .into_iter()
773        .find(|candidate| {
774            recover_exported_class_function_definition(*candidate, source).is_some()
775                && candidate
776                    .parent()
777                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
778        })
779}
780
781/// A trailing attribute macro after a recovered class's closing brace, spelled
782/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
783/// bare identifier is never a class member (members need a type), so this
784/// sibling can only be the class's own tail (#2297).
785fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
786    if node.kind() != "expression_statement" {
787        return false;
788    }
789    let mut cursor = node.walk();
790    let mut children = node.named_children(&mut cursor);
791    children
792        .next()
793        .is_some_and(|child| child.kind() == "identifier")
794        && children.next().is_none()
795}
796
797/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
798/// export-class wrapper can place the class close inside an access-label node,
799/// so direct-sibling checks alone miss the boundary.  Walk named CST children
800/// only; the helper does not inspect source text beyond the existing structured
801/// stray-brace predicate.
802fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
803    let mut stack = vec![node];
804    while let Some(current) = stack.pop() {
805        if cpp_is_stray_close_brace(current, source) {
806            return Some(current);
807        }
808        let mut cursor = current.walk();
809        stack.extend(current.named_children(&mut cursor));
810    }
811    None
812}
813
814/// Return named siblings that follow `node`, including siblings that tree-sitter
815/// attached to an enclosing container after malformed recovery split the local
816/// declaration list. Stop at the first structurally visible class close so a
817/// later namespace or exported class cannot supply the recovery boundary.
818///
819/// The climb takes a [`ParentIndex`] because it runs once per visited node and
820/// walks every level to the root: with `Node::parent`, which re-descends from
821/// the root on each call, a translation unit holding thousands of siblings --
822/// generated instruction tables, for instance -- made this the whole cost of
823/// indexing the file. Callers inside the declaration walk pass the walk's
824/// index; the four that ask from outside a traversal pass
825/// [`ParentIndex::unindexed`], which is `Node::parent` itself, so their
826/// behaviour is unchanged.
827fn cpp_following_named_siblings<'tree>(
828    node: Node<'tree>,
829    source: &str,
830    ancestry: &ParentIndex<'tree>,
831) -> Vec<Node<'tree>> {
832    let mut siblings = Vec::new();
833    let mut anchor = node;
834    while let Some(parent) = ancestry.parent(anchor) {
835        let at_translation_unit = parent.kind() == "translation_unit";
836        let mut sibling = anchor.next_named_sibling();
837        while let Some(current) = sibling {
838            if at_translation_unit
839                && (current.kind() == "namespace_definition"
840                    || (current.kind() == "function_definition"
841                        && first_class_like_child(current).is_some()))
842            {
843                return siblings;
844            }
845            siblings.push(current);
846            if cpp_is_stray_close_brace(current, source) {
847                if let Some(semicolon) = current
848                    .next_named_sibling()
849                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
850                {
851                    siblings.push(semicolon);
852                }
853                return siblings;
854            }
855            if current.start_byte() >= node.end_byte()
856                && matches!(current.kind(), "ERROR" | "labeled_statement")
857                && cpp_nested_stray_close_brace(current, source).is_some()
858            {
859                return siblings;
860            }
861            sibling = current.next_named_sibling();
862        }
863        anchor = parent;
864    }
865    siblings
866}
867
868fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
869    if node.start_byte() >= class_end {
870        return false;
871    }
872    node.end_byte() <= class_end
873        || cpp_nested_stray_close_brace(node, source)
874            .is_some_and(|close| close.start_byte() == class_end)
875}
876
877/// A class whose head tree-sitter kept in one malformed container while the
878/// members after the first and the closing `};` scattered across that
879/// container's following siblings.
880struct FragmentedClassRecovery<'tree> {
881    declaration_node: Node<'tree>,
882    name: String,
883    raw_supertypes: Vec<String>,
884    body: FragmentedExportBody,
885}
886
887/// Recover a class whose opening prefix is retained in one ERROR node while one
888/// or more nested class closes and the outer close are displaced to sibling
889/// `}`/`;` nodes. All boundaries come from tree-sitter nodes: the direct class
890/// tokens establish nesting depth and the displaced close nodes terminate it.
891///
892/// Two head spellings reach the ERROR arm. A plain `class Name ... {` keeps the
893/// `class` keyword as the container's first token. A function-like export macro
894/// (`class BOTAN_PUBLIC_API(2, 0) Name : public virtual Base {`) is reduced to a
895/// body-less `class_specifier` named after the macro followed by the
896/// invocation's `(`, arguments and `)`, with the real class name and the base
897/// clause left as bare tokens after it (#2924).
898fn fragmented_class_body<'tree>(
899    node: Node<'tree>,
900    source: &str,
901) -> Option<FragmentedClassRecovery<'tree>> {
902    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
903        return Some(recovered);
904    }
905    let supported_container = node.kind() == "ERROR"
906        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
907    if !supported_container {
908        return None;
909    }
910    let mut cursor = node.walk();
911    let children = node.children(&mut cursor).collect::<Vec<_>>();
912    if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
913        return Some(recovered);
914    }
915    let keyword = children.first()?;
916    if !matches!(keyword.kind(), "class" | "struct" | "union") {
917        return None;
918    }
919    let name_node = children
920        .iter()
921        .copied()
922        .skip(1)
923        .find(|child| child.is_named())?;
924    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
925        return None;
926    }
927    let name = normalize_cpp_whitespace(node_text(name_node, source));
928    if name.is_empty() || cpp_export_macro_token(&name) {
929        return None;
930    }
931    let open_index = children.iter().position(|child| child.kind() == "{")?;
932    Some(FragmentedClassRecovery {
933        declaration_node: node,
934        name,
935        raw_supertypes: extract_cpp_supertypes(node, source),
936        body: fragmented_displaced_class_body(node, &children, open_index, source)?,
937    })
938}
939
940/// The fragmented body of a function-like export-macro class head that
941/// tree-sitter kept in one declaration-scope `ERROR` together with the body's
942/// opening `{` (Botan's `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey :
943/// public virtual EC_PublicKey {`, #2924).
944///
945/// The class name is read positionally by [`recovered_export_head_name`] -- the
946/// last identifier before the head ends at `final`, at the base clause `:`, or
947/// at the body `{` -- so no macro spelling is interpreted, and a base clause
948/// spelled with `virtual`, with `final`, or with qualified base names needs no
949/// arm of its own.
950fn fragmented_export_macro_class_body<'tree>(
951    node: Node<'tree>,
952    children: &[Node<'tree>],
953    source: &str,
954) -> Option<FragmentedClassRecovery<'tree>> {
955    let class_node = *children.first()?;
956    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
957        return None;
958    }
959    // The identifier tree-sitter took for the class name is the export macro, a
960    // function-like invocation when the `(` of its argument list follows it
961    // directly. Spelling plays no part (#2557).
962    class_node
963        .child_by_field_name("name")
964        .and_then(|name| direct_identifier_name(name, source))?;
965    let invocation = *children.get(1)?;
966    if invocation.is_named() || invocation.kind() != "(" {
967        return None;
968    }
969    let open_index = children
970        .iter()
971        .position(|child| !child.is_named() && child.kind() == "{")?;
972    let open = children[open_index];
973    let name_node = recovered_export_head_name(node, open, source)?;
974    let name = normalize_cpp_whitespace(node_text(name_node, source));
975    if name.is_empty() || cpp_export_macro_token(&name) {
976        return None;
977    }
978    Some(FragmentedClassRecovery {
979        declaration_node: node,
980        name,
981        raw_supertypes: recovered_export_head_bases(
982            node,
983            name_node.end_byte(),
984            open.start_byte(),
985            source,
986        ),
987        body: fragmented_displaced_class_body(node, children, open_index, source)
988            .or_else(|| fragmented_container_close_class_body(node, open, source))?,
989    })
990}
991
992/// The class body a fragmented head opened at `children[open_index]` but could
993/// not close: tree-sitter scattered the remaining members and the closing `};`
994/// across the container's following siblings. The direct class tokens after the
995/// opening brace say how many closes the nested classes consume before this
996/// class's own, and the displaced `}`/`;` sibling pair terminates it.
997fn fragmented_displaced_class_body(
998    node: Node<'_>,
999    children: &[Node<'_>],
1000    open_index: usize,
1001    source: &str,
1002) -> Option<FragmentedExportBody> {
1003    let open = children[open_index];
1004    let nested_class_opens = children[open_index + 1..]
1005        .iter()
1006        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1007        .count();
1008    let mut closes_remaining = 1 + nested_class_opens;
1009    let mut sibling = node.next_named_sibling();
1010    while let Some(candidate) = sibling {
1011        let next = candidate.next_named_sibling();
1012        if cpp_is_stray_close_brace(candidate, source) {
1013            closes_remaining -= 1;
1014            if closes_remaining == 0 {
1015                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
1016                if open.end_byte() >= candidate.start_byte() {
1017                    return None;
1018                }
1019                return Some(FragmentedExportBody {
1020                    reparse_start: open.end_byte(),
1021                    reparse_end: candidate.start_byte(),
1022                    class_range: Range {
1023                        start_byte: node.start_byte(),
1024                        end_byte: semicolon.end_byte(),
1025                        start_line: node.start_position().row + 1,
1026                        end_line: semicolon.end_position().row + 1,
1027                    },
1028                });
1029            }
1030        }
1031        sibling = next;
1032    }
1033    None
1034}
1035
1036/// The body of a fragmented export-macro class head whose close tree-sitter
1037/// spent on the enclosing container instead: Botan's
1038/// `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey : public virtual
1039/// EC_PublicKey {` leaves every member as a `declaration_list` sibling and ends
1040/// `namespace Botan` on the class's own brace, so nothing is displaced to a
1041/// sibling `}` and [`fragmented_displaced_class_body`] finds no terminator
1042/// (#2924).
1043fn fragmented_container_close_class_body(
1044    node: Node<'_>,
1045    open: Node<'_>,
1046    source: &str,
1047) -> Option<FragmentedExportBody> {
1048    let parent = node.parent()?;
1049    if !matches!(
1050        parent.kind(),
1051        "declaration_list" | "field_declaration_list" | "compound_statement"
1052    ) {
1053        return None;
1054    }
1055    let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1056    // The container's close is this class's close only when the body's own
1057    // brace balance says so. Without that cross-check -- the one issue #1524
1058    // already uses on a mis-closed wrapper body -- a class that really does end
1059    // earlier would claim every later container-level declaration as a member.
1060    if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1061        || open.end_byte() >= close.start_byte()
1062    {
1063        return None;
1064    }
1065    Some(FragmentedExportBody {
1066        reparse_start: open.end_byte(),
1067        reparse_end: close.start_byte(),
1068        class_range: Range {
1069            start_byte: node.start_byte(),
1070            end_byte: close.end_byte(),
1071            start_line: node.start_position().row + 1,
1072            end_line: close.end_position().row + 1,
1073        },
1074    })
1075}
1076
1077pub(crate) fn recovered_fragmented_class_has_body(
1078    node: Node<'_>,
1079    source: &str,
1080    expected_name: &str,
1081    expected_range: &Range,
1082) -> bool {
1083    fragmented_class_body(node, source).is_some_and(|recovered| {
1084        recovered.name == expected_name
1085            && recovered.body.class_range.start_byte == expected_range.start_byte
1086            && recovered.body.class_range.end_byte == expected_range.end_byte
1087    })
1088}
1089
1090/// Recover a plain class whose parser-visible body ends inside a malformed
1091/// inline member. Tree-sitter then attaches either the next real member
1092/// declarator or the unfinished `else` branch directly to the outer function
1093/// definition and leaves the class's actual `};` among later siblings. Those
1094/// structured continuations and the close/semicolon siblings establish the
1095/// complete body envelope without interpreting source text.
1096fn fragmented_plain_class_declaration_body<'tree>(
1097    node: Node<'tree>,
1098    source: &str,
1099) -> Option<FragmentedClassRecovery<'tree>> {
1100    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1101        return None;
1102    }
1103    let class_node = node.child_by_field_name("type")?;
1104    if !matches!(
1105        class_node.kind(),
1106        "class_specifier" | "struct_specifier" | "union_specifier"
1107    ) {
1108        return None;
1109    }
1110    let name_node = class_node.child_by_field_name("name")?;
1111    let name = normalize_cpp_whitespace(node_text(name_node, source));
1112    if name.is_empty() || cpp_export_macro_token(&name) {
1113        return None;
1114    }
1115    let body = cpp_body_node(class_node)?;
1116    if body.kind() != "field_declaration_list" {
1117        return None;
1118    }
1119    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1120        if declarator.start_byte() < class_node.end_byte() {
1121            return None;
1122        }
1123        let mut cursor = node.walk();
1124        node.named_children(&mut cursor).any(|child| {
1125            if child.kind() != "ERROR"
1126                || child.start_byte() < class_node.end_byte()
1127                || child.end_byte() > declarator.start_byte()
1128            {
1129                return false;
1130            }
1131            let mut cursor = child.walk();
1132            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1133            let Some((return_type, attributes)) = components.split_last() else {
1134                return false;
1135            };
1136            matches!(
1137                return_type.kind(),
1138                "identifier"
1139                    | "type_identifier"
1140                    | "primitive_type"
1141                    | "decltype"
1142                    | "placeholder_type_specifier"
1143            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1144                && attributes.iter().all(|attribute| {
1145                    matches!(attribute.kind(), "identifier" | "type_identifier")
1146                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1147                            *attribute, source,
1148                        )))
1149                })
1150        })
1151    } else {
1152        let mut cursor = node.walk();
1153        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1154        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1155            if same_node(*candidate_class, class_node)
1156                && continuation.kind() == "identifier"
1157                && node_text(*continuation, source) == "else"
1158                && continuation_body.kind() == "compound_statement"
1159                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1160                && continuation_body
1161                    .child(continuation_body.child_count().saturating_sub(1))
1162                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1163    };
1164    if !displaced_member {
1165        return None;
1166    }
1167    let open = body
1168        .children(&mut body.walk())
1169        .find(|child| child.kind() == "{")?;
1170    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1171    let ordinary_boundary =
1172        siblings
1173            .iter()
1174            .copied()
1175            .enumerate()
1176            .find_map(|(close_index, close)| {
1177                cpp_is_stray_close_brace(close, source)
1178                    .then(|| {
1179                        siblings
1180                            .get(close_index + 1)
1181                            .copied()
1182                            .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1183                            .map(|semicolon| (close, semicolon))
1184                    })
1185                    .flatten()
1186            });
1187    let (close, semicolon) =
1188        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1189            (boundary.class_close, boundary.class_semicolon)
1190        } else {
1191            ordinary_boundary?
1192        };
1193    if open.end_byte() >= close.start_byte() {
1194        return None;
1195    }
1196    Some(FragmentedClassRecovery {
1197        declaration_node: class_node,
1198        name,
1199        raw_supertypes: extract_cpp_supertypes(class_node, source),
1200        body: FragmentedExportBody {
1201            reparse_start: open.end_byte(),
1202            reparse_end: close.start_byte(),
1203            class_range: Range {
1204                start_byte: class_node.start_byte(),
1205                end_byte: semicolon.end_byte(),
1206                start_line: class_node.start_position().row + 1,
1207                end_line: semicolon.end_position().row + 1,
1208            },
1209        },
1210    })
1211}
1212
1213fn displaced_export_function_namespace_shape<'tree>(
1214    declaration: Node<'tree>,
1215    source: &str,
1216) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1217    let mut nested = Vec::new();
1218    for index in (0..declaration.named_child_count()).rev() {
1219        nested.push(declaration.named_child(index)?);
1220    }
1221    while let Some(current) = nested.pop() {
1222        // A recovered export class nested in this class can consume the first
1223        // parser-visible namespace close itself. In that shape the existing
1224        // later-class boundary logic already distinguishes the nested and
1225        // namespace-sibling owners; do not mistake the nested close for this
1226        // class's terminator.
1227        if recover_exported_class_function_definition(current, source).is_some() {
1228            return None;
1229        }
1230        for index in (0..current.named_child_count()).rev() {
1231            nested.push(current.named_child(index)?);
1232        }
1233    }
1234    let mut same_envelope_sibling = declaration.next_named_sibling();
1235    while let Some(current) = same_envelope_sibling {
1236        if recover_exported_class_function_definition(current, source).is_some() {
1237            return None;
1238        }
1239        same_envelope_sibling = current.next_named_sibling();
1240    }
1241    let declaration_list = declaration.parent()?;
1242    if declaration_list.kind() != "declaration_list" {
1243        return None;
1244    }
1245    let namespace = declaration_list.parent()?;
1246    if namespace.kind() != "namespace_definition"
1247        || namespace.child_by_field_name("body") != Some(declaration_list)
1248    {
1249        return None;
1250    }
1251    let class_close = direct_close_brace(declaration_list)?;
1252    let trailing_semicolon = namespace.next_named_sibling()?;
1253    if trailing_semicolon.kind() != "expression_statement"
1254        || trailing_semicolon.named_child_count() != 0
1255    {
1256        return None;
1257    }
1258    // A chain of malformed export classes can consume one parser-visible
1259    // namespace close per class. Walk through the enclosing sibling levels so
1260    // the later real namespace close remains the structural boundary; a
1261    // direct next-sibling walk stops at the first collapsed namespace and
1262    // incorrectly makes its intervening items members of this class.
1263    let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1264    let trailing_index = siblings
1265        .iter()
1266        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1267    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1268        recover_exported_class_function_definition(*candidate, source).is_some()
1269    }) {
1270        // Consecutive recovered classes already have an exact sibling-class
1271        // boundary. Preserve that established path, including nested export
1272        // classes, instead of interpreting the first class close as a
1273        // collapsed namespace boundary.
1274        return None;
1275    }
1276    let mut namespace_items = Vec::new();
1277    let mut nested_fragment_end = 0;
1278    for current in siblings.into_iter().skip(trailing_index + 1) {
1279        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1280        {
1281            return Some(DisplacedFragmentNamespaceBoundary {
1282                class_close,
1283                class_semicolon: trailing_semicolon,
1284                namespace_items,
1285            });
1286        }
1287        if current.start_byte() >= nested_fragment_end
1288            && let Some(recovered) = fragmented_class_body(current, source)
1289        {
1290            nested_fragment_end = recovered.body.class_range.end_byte;
1291        } else if current.start_byte() >= nested_fragment_end
1292            && recover_exported_class_function_definition(current, source).is_some()
1293            && let Some(body) = cpp_body_node(current)
1294            && let Some(fragmented) =
1295                fragmented_export_function_body_region(current, body, source, None)
1296        {
1297            nested_fragment_end = fragmented.class_range.end_byte;
1298        }
1299        namespace_items.push(current);
1300    }
1301    None
1302}
1303
1304fn displaced_fragment_namespace_boundary<'tree>(
1305    declaration: Node<'tree>,
1306    body: Node<'tree>,
1307    source: &str,
1308) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1309    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1310    let reparse_start = body.start_byte() + 1;
1311    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1312    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1313}
1314
1315/// Recover the class/namespace brace geometry for a declaration whose class
1316/// close tree-sitter consumed as the enclosing namespace close. This proof is
1317/// independent of whether every member in the class body can be reparsed: the
1318/// ordinary-tree fallback can still re-own bounded sibling declarations when
1319/// an unknown macro makes the complete body reparse unsafe.
1320fn displaced_fragment_namespace_geometry<'tree>(
1321    declaration: Node<'tree>,
1322    source: &str,
1323) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1324    // A templated class's malformed function wrapper remains beneath the
1325    // template node even though its later members have escaped to the
1326    // enclosing declaration list. Lift only that exact declaration child.
1327    let envelope = declaration
1328        .parent()
1329        .filter(|parent| {
1330            parent.kind() == "template_declaration"
1331                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1332        })
1333        .unwrap_or(declaration);
1334    let declaration_list = envelope.parent()?;
1335    if declaration_list.kind() != "declaration_list" {
1336        return None;
1337    }
1338    let namespace = declaration_list.parent()?;
1339    if namespace.kind() != "namespace_definition"
1340        || namespace.child_by_field_name("body") != Some(declaration_list)
1341    {
1342        return None;
1343    }
1344    let class_close = direct_close_brace(declaration_list)?;
1345    let trailing_semicolon = namespace.next_named_sibling()?;
1346    if trailing_semicolon.kind() != "expression_statement"
1347        || trailing_semicolon.named_child_count() != 0
1348    {
1349        return None;
1350    }
1351    let mut namespace_items = Vec::new();
1352    let mut sibling = trailing_semicolon.next_named_sibling();
1353    let mut nested_fragment_end = 0;
1354    loop {
1355        let current = sibling?;
1356        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1357        {
1358            break;
1359        }
1360        if current.start_byte() >= nested_fragment_end
1361            && let Some(recovered) = fragmented_class_body(current, source)
1362        {
1363            nested_fragment_end = recovered.body.class_range.end_byte;
1364        }
1365        namespace_items.push(current);
1366        sibling = current.next_named_sibling();
1367    }
1368    Some(DisplacedFragmentNamespaceBoundary {
1369        class_close,
1370        class_semicolon: trailing_semicolon,
1371        namespace_items,
1372    })
1373}
1374
1375/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1376fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1377    (0..node.child_count())
1378        .filter_map(|index| node.child(index))
1379        .find(|child| !child.is_named() && child.kind() == "}")
1380}
1381
1382/// A displaced lone closing brace: the class close that the fragmented multiple-base
1383/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1384fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1385    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1386}
1387
1388/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1389/// text while skipping line/block comments and string/char literals. The
1390/// exported-class recovery needs this when tree-sitter's bogus
1391/// `function_definition` body runs past the class's true closing brace and
1392/// swallows following siblings (issue #1524): the grammar tree carries no
1393/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1394/// close is located textually. Returns `None` when the text is unbalanced or
1395/// contains a construct the scanner deliberately does not interpret (raw
1396/// strings) -- callers treat that as "cannot partition" and keep the
1397/// un-split recovery.
1398fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1399    let bytes = source.as_bytes();
1400    if bytes.get(open_byte) != Some(&b'{') {
1401        return None;
1402    }
1403    let mut depth = 0usize;
1404    let mut i = open_byte;
1405    while i < bytes.len() {
1406        match bytes[i] {
1407            b'{' => depth += 1,
1408            b'}' => {
1409                depth = depth.checked_sub(1)?;
1410                if depth == 0 {
1411                    return Some(i);
1412                }
1413            }
1414            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1415                while i < bytes.len() && bytes[i] != b'\n' {
1416                    i += 1;
1417                }
1418                continue;
1419            }
1420            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1421                i += 2;
1422                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1423                    i += 1;
1424                }
1425                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1426                continue;
1427            }
1428            quote @ (b'"' | b'\'') => {
1429                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1430                // bail out rather than mis-count.
1431                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1432                    return None;
1433                }
1434                i += 1;
1435                while i < bytes.len() && bytes[i] != quote {
1436                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1437                }
1438                if i >= bytes.len() {
1439                    return None;
1440                }
1441            }
1442            _ => {}
1443        }
1444        i += 1;
1445    }
1446    None
1447}
1448
1449fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1450    let mut name = None;
1451    let mut colon_count = 0;
1452    let mut access_count = 0;
1453    for index in 0..node.child_count() {
1454        let child = node.child(index)?;
1455        match child.kind() {
1456            "identifier" | "type_identifier" if child.is_named() => {
1457                if name.is_some() {
1458                    return None;
1459                }
1460                let candidate = normalize_cpp_whitespace(node_text(child, source));
1461                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1462                    return None;
1463                }
1464                name = Some(candidate);
1465            }
1466            "template_function" | "template_type" if child.is_named() => {
1467                if name.is_some() {
1468                    return None;
1469                }
1470                let candidate = child
1471                    .child_by_field_name("name")
1472                    .and_then(|name| direct_identifier_name(name, source))?;
1473                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1474                    return None;
1475                }
1476                name = Some(candidate);
1477            }
1478            ":" if !child.is_named() => colon_count += 1,
1479            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1480            _ => return None,
1481        }
1482    }
1483    (colon_count == 1 && access_count == 1)
1484        .then_some(name)
1485        .flatten()
1486}
1487
1488fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1489    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1490        return false;
1491    }
1492    node.named_child(0)
1493        .and_then(|child| direct_identifier_name(child, source))
1494        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1495}
1496
1497fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1498    (0..node.child_count()).any(|index| {
1499        node.child(index)
1500            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1501    })
1502}
1503
1504fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1505    match node.kind() {
1506        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1507            recovered_base_atom(node, source)
1508        }
1509        "template_type" | "template_function" => node
1510            .child_by_field_name("name")
1511            .and_then(|name| recovered_malformed_base_name(name, source)),
1512        "ERROR" => None,
1513        "qualified_identifier" | "scoped_type_identifier" => {
1514            let suffix = node
1515                .child_by_field_name("name")
1516                .and_then(|name| recovered_malformed_base_name(name, source))?;
1517            let scope = node
1518                .child_by_field_name("scope")
1519                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1520            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1521                malformed_qualified_prefix(node, source)?
1522            } else {
1523                if malformed_qualified_prefix(node, source).is_some() {
1524                    return None;
1525                }
1526                scope
1527            };
1528            Some(format!("{prefix}::{suffix}"))
1529        }
1530        _ => None,
1531    }
1532}
1533
1534fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1535    if !matches!(
1536        node.kind(),
1537        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1538    ) {
1539        return None;
1540    }
1541    let name = normalize_cpp_whitespace(node_text(node, source));
1542    (!name.is_empty()).then_some(name)
1543}
1544
1545fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1546    let mut prefix = None;
1547    let mut cursor = node.walk();
1548    for error in node
1549        .named_children(&mut cursor)
1550        .filter(|child| child.kind() == "ERROR")
1551    {
1552        if prefix.is_some() {
1553            return None;
1554        }
1555        // `public virtual Botan::EC_PublicKey` reduces the access specifier to
1556        // the qualified name's scope and leaves `virtual` beside the real
1557        // qualifier inside the `ERROR`. `virtual` is a base specifier, never a
1558        // component of the base's name (#2924).
1559        let mut error_cursor = error.walk();
1560        let atoms = error
1561            .named_children(&mut error_cursor)
1562            .map(|child| recovered_base_atom(child, source))
1563            .collect::<Option<Vec<_>>>()?;
1564        let [atom] = atoms
1565            .iter()
1566            .filter(|atom| atom.as_str() != "virtual")
1567            .collect::<Vec<_>>()[..]
1568        else {
1569            return None;
1570        };
1571        prefix = Some(atom.clone());
1572    }
1573    prefix
1574}
1575
1576/// One declaration an attribute-like macro invocation swallowed into a
1577/// declaration-scope `ERROR`, with the byte range that spells it.
1578/// What [`stranded_declaration_run`] read out of one node.
1579struct StrandedRun<'tree> {
1580    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1581    /// Whether every part of the node read as part of a declaration. False when
1582    /// a part the reader does not understand ended it early, or when the last
1583    /// parts were types with no declarator after them. Callers that index what
1584    /// was found keep the declarations either way; a caller deciding whether a
1585    /// whole region is safe to index requires this.
1586    complete: bool,
1587}
1588
1589struct MacroWrappedDeclaration<'tree> {
1590    declarator: Node<'tree>,
1591    range: Range,
1592    /// Whether the recovered declaration spells `static`. The envelope hides
1593    /// that keyword from the ordinary linkage reader, which looks for it among
1594    /// a declaration node's own children, and internal linkage is what decides
1595    /// whether a header declaration and a body in another file are one symbol.
1596    is_static: bool,
1597}
1598
1599/// Whether `node` stands where declarations live: directly in the translation
1600/// unit, in a `namespace` or `extern "C"` body, or in an `ERROR` that itself
1601/// stands in one of those.
1602///
1603/// An `ERROR` is a grouping failure, never a scope, so what it holds stands
1604/// where it stands. The case that matters is a whole translation unit the
1605/// parser could not close: whisper.cpp's `include/whisper.h` opens
1606/// `extern "C" {` inside `#ifdef __cplusplus` and closes it inside another one,
1607/// so the braces never balance in the token stream tree-sitter sees, the file's
1608/// own root node is an `ERROR`, and every top-level declaration in the file is
1609/// a child of it (#3094). Without this the macro recoveries below read nothing
1610/// in such a file, because none of their envelopes is in a declaration scope
1611/// the parser managed to build.
1612fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1613    declaration_scope_container(node, ancestry).is_some()
1614}
1615
1616/// The container `node` declares in, for [`is_declaration_scope_position`]. Its
1617/// end is where a parse failure that starts at `node` can still be doing
1618/// damage: a declaration scope has no synchronization point of its own, so the
1619/// parser carries such a failure to the scope's close.
1620fn declaration_scope_container<'tree>(
1621    node: Node<'tree>,
1622    ancestry: &ParentIndex<'tree>,
1623) -> Option<Node<'tree>> {
1624    let mut parent = ancestry.parent(node)?;
1625    loop {
1626        match parent.kind() {
1627            "translation_unit" => return Some(parent),
1628            "declaration_list" => {
1629                return ancestry
1630                    .parent(parent)
1631                    .is_some_and(|grandparent| {
1632                        matches!(
1633                            grandparent.kind(),
1634                            "namespace_definition" | "linkage_specification"
1635                        )
1636                    })
1637                    .then_some(parent);
1638            }
1639            "ERROR" => match ancestry.parent(parent) {
1640                Some(grandparent) => parent = grandparent,
1641                // A root `ERROR` is the translation unit the parser could not
1642                // build, so its children stand at file scope.
1643                None => return Some(parent),
1644            },
1645            _ => return None,
1646        }
1647    }
1648}
1649
1650/// Whether `node` is an `ERROR` the parser produced where declarations live.
1651fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1652    node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
1653}
1654
1655/// Whether `node` can only be part of what precedes a declarator -- a type, a
1656/// specifier, or a word of the attribute macro's own text that the lexer left
1657/// as a bare identifier -- so a run of these followed by a declarator is one
1658/// declaration the parser failed to group.
1659fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1660    matches!(
1661        node.kind(),
1662        "identifier"
1663            | "type_identifier"
1664            | "primitive_type"
1665            | "sized_type_specifier"
1666            | "struct_specifier"
1667            | "union_specifier"
1668            | "enum_specifier"
1669            | "type_qualifier"
1670            | "storage_class_specifier"
1671            | "explicit_function_specifier"
1672            | "virtual_function_specifier"
1673            | "qualified_identifier"
1674            | "template_type"
1675            | "dependent_type"
1676            | "placeholder_type_specifier"
1677    )
1678}
1679
1680/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
1681/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
1682/// words the lexer reports as bare identifiers. Anything else in such an
1683/// `ERROR` stops the recovery, so a macro argument that carries structure this
1684/// recovery does not understand is never guessed at.
1685fn is_macro_argument_error(node: Node<'_>) -> bool {
1686    if node.kind() != "ERROR" {
1687        return false;
1688    }
1689    let mut cursor = node.walk();
1690    node.named_children(&mut cursor).all(|child| {
1691        matches!(
1692            child.kind(),
1693            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1694        )
1695    })
1696}
1697
1698/// The end of a recovered declaration: the declarator's end, extended across
1699/// the `;` the grammar left beside it inside the envelope.
1700///
1701/// The envelope's own end is the hard boundary. The parser hands the last
1702/// declaration's `;` to the sibling statement it recovered with, outside the
1703/// envelope, and a range that reached it would no longer lie inside one node --
1704/// which is how every reader (including the resolver's climb from a range to
1705/// the node that declares it) finds a recovered declaration again.
1706fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1707    declarator
1708        .next_sibling()
1709        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1710        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1711}
1712
1713/// The declarations `node` holds after an attribute-like macro cost the parser
1714/// their grouping, in source order.
1715///
1716/// The parser packs the parts into whichever slots it has left. In whisper's
1717/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
1718/// field takes the export macro, the real return type and the *next*
1719/// declaration's declarator both end up inside one sibling `ERROR`, and the
1720/// `declarator` field takes whatever declaration the recovery reached last. In
1721/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
1722/// arrive as bare identifiers and the attributed member and the member after it
1723/// share one declarator node.
1724///
1725/// Both are the same failure, so both get the same reading: flatten the node's
1726/// parts -- splicing each nested `ERROR`'s own children in place, since an
1727/// `ERROR` here is only a grouping failure -- and read the flat run as what it
1728/// spells, a run of type-and-specifier tokens followed by a declarator, over
1729/// and over.
1730///
1731/// Fails closed. A part that is neither a declarator nor something that can
1732/// only precede one ends the recovery there, and the declarations before it are
1733/// kept.
1734fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1735    let mut parts = Vec::new();
1736    let mut cursor = node.walk();
1737    for child in node.named_children(&mut cursor) {
1738        if child.kind() == "ERROR" {
1739            let mut error_cursor = child.walk();
1740            parts.extend(child.named_children(&mut error_cursor));
1741        } else {
1742            parts.push(child);
1743        }
1744    }
1745
1746    let mut declarations = Vec::new();
1747    let mut start = None;
1748    let mut is_static = false;
1749    let mut complete = true;
1750    for part in parts {
1751        if part.kind() == "comment" {
1752            continue;
1753        }
1754        if let Some(declarator) = extract_function_declarator(part) {
1755            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1756            declarations.push(MacroWrappedDeclaration {
1757                declarator,
1758                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1759                is_static,
1760            });
1761            is_static = false;
1762            continue;
1763        }
1764        if !is_recovered_declaration_type_part(part) {
1765            complete = false;
1766            break;
1767        }
1768        is_static |= part.kind() == "storage_class_specifier"
1769            && normalize_cpp_whitespace(node_text(part, source)) == "static";
1770        start.get_or_insert(part.start_byte());
1771    }
1772    StrandedRun {
1773        declarations,
1774        complete: complete && start.is_none(),
1775    }
1776}
1777
1778/// The declarations an attribute-like macro invocation swallowed into a
1779/// declaration-scope `ERROR`, in source order.
1780///
1781/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
1782/// declaration in a macro call:
1783///
1784/// ```text
1785/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
1786///                  struct llama_model * model,
1787///           struct llama_context_params   params),
1788///         "use llama_init_from_model instead");
1789/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
1790/// ```
1791///
1792/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
1793/// holding the macro name, the wrapped declaration as a
1794/// `parameter_declaration`, the hint string as another `ERROR`, and then every
1795/// following declaration as a further `parameter_declaration` until it
1796/// recovers. That is what removed `llama_tokenize` from the index and left the
1797/// seven-argument call in `talk-llama.cpp` with only that file's own
1798/// three-parameter `static` overload to choose from (#2552, and the
1799/// `LLAMA_API` half of #2551).
1800///
1801/// Every part of every swallowed declaration is still a real node; only their
1802/// grouping is lost. This rebuilds the grouping and reads the nodes.
1803fn macro_wrapped_declarations<'tree>(
1804    envelope: Node<'tree>,
1805    source: &str,
1806    ancestry: &ParentIndex<'tree>,
1807) -> Vec<MacroWrappedDeclaration<'tree>> {
1808    let mut declarations = Vec::new();
1809    if !is_declaration_scope_error(envelope, ancestry) {
1810        return declarations;
1811    }
1812    let mut cursor = envelope.walk();
1813    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1814    let [macro_name, arguments @ ..] = children.as_slice() else {
1815        return declarations;
1816    };
1817    if macro_name.kind() != "identifier" {
1818        return declarations;
1819    }
1820    let mut wrapped_declaration_seen = false;
1821    for argument in arguments {
1822        match argument.kind() {
1823            "comment" => {}
1824            "parameter_declaration" => {
1825                let recovered = stranded_declaration_run(*argument, source).declarations;
1826                if recovered.is_empty() {
1827                    break;
1828                }
1829                wrapped_declaration_seen = true;
1830                declarations.extend(recovered);
1831            }
1832            // The hint string, and only that: an argument the recovery cannot
1833            // read as a declaration is admitted before the wrapped declaration
1834            // is found, so a macro whose first argument is not a declaration
1835            // recovers nothing.
1836            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1837            _ => break,
1838        }
1839    }
1840    declarations
1841}
1842
1843/// What one macro invocation swallowed when it collapsed a whole run of
1844/// declarations.
1845struct CollapsedMacroDeclarationRun {
1846    /// The byte just past the `;` that closes the invocation, which is where
1847    /// the declarations it swallowed begin.
1848    invocation_end: usize,
1849    /// The byte this recovery owns to: the close of the enclosing declaration
1850    /// scope when the invocation swallowed the declarations written after it,
1851    /// and the invocation's own end when the parser instead spread the
1852    /// invocation itself across the container's children and swallowed nothing.
1853    region_end: usize,
1854}
1855
1856/// The tokens one collapsed macro invocation spans, in source order: the leaves
1857/// of `node`, then the leaves of the siblings written after it. Comments and
1858/// the parser's own MISSING tokens are skipped; neither is in the source the
1859/// invocation is written in.
1860///
1861/// The parser puts the parts of a failed invocation wherever it has room. It
1862/// keeps them in one `ERROR`; it splits them across an `ERROR` and the
1863/// `expression_statement` it recovered with; it spreads them flat over the
1864/// container's children; it packs the tail of the file into one of the
1865/// arguments. The grouping therefore says nothing, but the token order still
1866/// spells the invocation, which is what this reads.
1867struct MacroInvocationTokens<'tree> {
1868    stack: Vec<Node<'tree>>,
1869    /// The last sibling handed to `stack`, and the point the next one is asked
1870    /// for. Held as the node rather than as its already-computed successor so
1871    /// the successor is asked for only when the walk actually runs off the end
1872    /// of what it has: `Node::next_sibling` recovers the parent first, which
1873    /// re-descends from the root, so it costs the node's position in the tree.
1874    /// Nearly every node this iterator is built for is rejected on its first
1875    /// token, before any sibling is needed, and computing one eagerly made that
1876    /// rejection cost a walk of the whole container (capstone's generated
1877    /// instruction tables, t19).
1878    frontier: Option<Node<'tree>>,
1879}
1880
1881impl<'tree> MacroInvocationTokens<'tree> {
1882    fn new(node: Node<'tree>) -> Self {
1883        Self {
1884            stack: vec![node],
1885            frontier: Some(node),
1886        }
1887    }
1888}
1889
1890impl<'tree> Iterator for MacroInvocationTokens<'tree> {
1891    type Item = Node<'tree>;
1892
1893    fn next(&mut self) -> Option<Node<'tree>> {
1894        loop {
1895            let Some(node) = self.stack.pop() else {
1896                let sibling = self.frontier?.next_sibling()?;
1897                self.frontier = Some(sibling);
1898                self.stack.push(sibling);
1899                continue;
1900            };
1901            if node.child_count() == 0 {
1902                if node.kind() == "comment" || node.is_missing() {
1903                    continue;
1904                }
1905                return Some(node);
1906            }
1907            let mut cursor = node.walk();
1908            let children = node.children(&mut cursor).collect::<Vec<_>>();
1909            self.stack.extend(children.into_iter().rev());
1910        }
1911    }
1912}
1913
1914/// The macro invocation that begins at `node` when the parser could not group
1915/// it, or `None` when `node` does not begin one.
1916///
1917/// whisper.cpp's bundled `llama.h` writes
1918///
1919/// ```text
1920/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
1921///                          const char * path_model,
1922///           struct llama_model_params   params),
1923///         "use llama_model_load_from_file instead");
1924/// ```
1925///
1926/// and its own `include/whisper.h` writes
1927///
1928/// ```text
1929/// WHISPER_DEPRECATED(
1930///     WHISPER_API struct whisper_context * whisper_init_from_file(const char * path_model),
1931///     "use whisper_init_from_file_with_params instead"
1932/// );
1933/// ```
1934///
1935/// tree-sitter cannot know either name is a macro, so it reads the name and its
1936/// `(` as a function declarator whose close it never finds, and carries that
1937/// failure forward over the declarations written after it. In `llama.h` that
1938/// puts 57 KB, from line 481 to line 1535, into one `function_definition`
1939/// (#2551). In `whisper.h` it instead flattens the invocations over the
1940/// container's children -- a bare `identifier`, `(`, `parameter_declaration`,
1941/// `,` and an `ERROR` holding the hint, over and over -- and then packs lines
1942/// 232 to 456 into one `parameter_declaration` (#3094). The envelope is a
1943/// `function_definition` when a brace block falls in a swallowed tail, because
1944/// the parser borrows it for the body the bogus definition needs, and an
1945/// `ERROR` or a bare token when none does. None of that says anything about the
1946/// construct, so the node's kind is not the criterion; the token order is.
1947///
1948/// The invocation ends at the `)` that closes its own `(`, immediately followed
1949/// by `;`. That is unambiguous: a `)` the lexer left inside the hint text is
1950/// balanced by the `(` beside it, and a macro argument list carries no `;` of
1951/// its own, so a `;` reached before the close means this is some other
1952/// construct -- a macro-defined function body, say -- and the ordinary readers
1953/// keep the node.
1954///
1955/// Fails closed everywhere else too. The head must be an export-macro-shaped
1956/// token followed by `(`, the node must stand where declarations live, and an
1957/// invocation the ordinary [`macro_wrapped_declarations`] reader already has --
1958/// one the parser left whole in a declaration-scope `ERROR` and that swallowed
1959/// nothing past it -- is left to that reader.
1960fn collapsed_macro_declaration_run<'tree>(
1961    node: Node<'tree>,
1962    source: &str,
1963    ancestry: &ParentIndex<'tree>,
1964) -> Option<CollapsedMacroDeclarationRun> {
1965    let container_end = declaration_scope_container(node, ancestry)?.end_byte();
1966    let mut tokens = MacroInvocationTokens::new(node);
1967    let name = tokens.next()?;
1968    if !matches!(name.kind(), "identifier" | "type_identifier")
1969        || !cpp_export_macro_token(node_text(name, source))
1970    {
1971        return None;
1972    }
1973    if tokens.next()?.kind() != "(" {
1974        return None;
1975    }
1976    let mut depth = 1usize;
1977    let invocation_end = loop {
1978        let token = tokens.next()?;
1979        match token.kind() {
1980            "(" => depth += 1,
1981            ";" => return None,
1982            ")" => {
1983                depth -= 1;
1984                if depth == 0 {
1985                    let semicolon = tokens.next()?;
1986                    if semicolon.kind() != ";" {
1987                        return None;
1988                    }
1989                    break semicolon.end_byte();
1990                }
1991            }
1992            _ => {}
1993        }
1994    };
1995    // An invocation that reaches the end of its own declaration scope swallowed
1996    // nothing: there is nothing after it left to recover, and the ordinary
1997    // readers have the invocation itself. This is also what stops the scan
1998    // below from re-entering itself, since the region it reparses for one
1999    // invocation ends exactly at that invocation's `;`.
2000    if invocation_end >= container_end {
2001        return None;
2002    }
2003    // Where the damage ends. A node that reaches past its own invocation packed
2004    // real declarations into the invocation's argument list, so the parser was
2005    // still failing when it got there and everything to the close of the scope
2006    // is suspect -- in `whisper.h` the collapse takes the `{` of the enum on
2007    // line 455 with it, and every declaration from there to the end of the file
2008    // is shredded. A node that stops at or before the invocation's `;` holds
2009    // nothing but the invocation, whose own parts the parser spread over the
2010    // siblings after it.
2011    let region_end = if node.end_byte() > invocation_end {
2012        container_end
2013    } else {
2014        invocation_end
2015    };
2016    Some(CollapsedMacroDeclarationRun {
2017        invocation_end,
2018        region_end,
2019    })
2020}
2021
2022/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
2023/// ordinary class body, and the members it swallowed.
2024///
2025/// Botan deprecates members that way:
2026///
2027/// ```text
2028/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
2029/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
2030/// ```
2031///
2032/// The parser reads the macro name as the member's type and its argument list
2033/// as a parenthesized declarator, which then swallows the attributed member
2034/// *and* the member written after it: a `field_declaration` whose `type` is a
2035/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
2036/// opening with an `ERROR` whose first part is a bare identifier -- the first
2037/// word of the string, which the lexer could not keep together.
2038///
2039/// The macro's spelling is not the criterion; that structural shape is. The
2040/// returned declarations are in source order, the first being the attributed
2041/// member and the rest the members the declarator swallowed after it.
2042fn string_attribute_macro_member_declarators<'tree>(
2043    field: Node<'tree>,
2044    source: &str,
2045) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2046    if field.kind() != "field_declaration"
2047        || field
2048            .child_by_field_name("type")
2049            .is_none_or(|type_node| type_node.kind() != "type_identifier")
2050    {
2051        return None;
2052    }
2053    let declarator = field.child_by_field_name("declarator")?;
2054    if declarator.kind() != "parenthesized_declarator" {
2055        return None;
2056    }
2057    let opening = declarator.named_child(0)?;
2058    if opening.kind() != "ERROR"
2059        || opening
2060            .named_child(0)
2061            .is_none_or(|word| word.kind() != "identifier")
2062    {
2063        return None;
2064    }
2065    let declarations = stranded_declaration_run(declarator, source).declarations;
2066    (!declarations.is_empty()).then_some(declarations)
2067}
2068
2069/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
2070/// export-macro class body leaves in front of the members that macro decorated.
2071///
2072/// The reparse reads the class body as statements, so the attribute becomes a
2073/// call statement of its own -- with the `;` the grammar had to invent -- and
2074/// the members after it are stranded in the `ERROR` that follows.
2075fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2076    let Some(call) = (node.kind() == "expression_statement")
2077        .then(|| node.named_child(0))
2078        .flatten()
2079        .filter(|child| child.kind() == "call_expression")
2080    else {
2081        return false;
2082    };
2083    call.child_by_field_name("function")
2084        .is_some_and(|function| function.kind() == "identifier")
2085        && call
2086            .child_by_field_name("arguments")
2087            .is_some_and(|arguments| {
2088                let mut cursor = arguments.walk();
2089                arguments.named_child_count() > 0
2090                    && arguments
2091                        .named_children(&mut cursor)
2092                        .all(|argument| argument.kind() == "string_literal")
2093            })
2094}
2095
2096/// The start byte of the call the region reparse left where an access-labeled
2097/// constructor was written.
2098///
2099/// A constructor is a member only inside a class body. The export-macro class
2100/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
2101/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
2102/// comma-expression under `private:`, with no declarator left in the tree. It
2103/// is still spelled in the source, though, starting at the one call under the
2104/// label whose callee is the class's own name. Botan's private six-parameter
2105/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
2106/// call is an ambiguity this declines, and so is a reparse from that byte that
2107/// does not yield exactly one constructor declarator there.
2108fn cpp_access_label_constructor_call_start(
2109    node: Node<'_>,
2110    class_name: &str,
2111    source: &str,
2112) -> Option<usize> {
2113    if node.kind() != "labeled_statement" {
2114        return None;
2115    }
2116    let label = node.named_child(0)?;
2117    if label.kind() != "statement_identifier"
2118        || !matches!(
2119            node_text(label, source).trim(),
2120            "public" | "private" | "protected"
2121        )
2122    {
2123        return None;
2124    }
2125    let mut starts = Vec::new();
2126    let mut stack = vec![node];
2127    while let Some(current) = stack.pop() {
2128        if current.kind() == "call_expression"
2129            && current
2130                .child_by_field_name("function")
2131                .is_some_and(|function| {
2132                    function.kind() == "identifier"
2133                        && node_text(function, source).trim() == class_name
2134                })
2135        {
2136            starts.push(current.start_byte());
2137        }
2138        let mut cursor = current.walk();
2139        stack.extend(current.named_children(&mut cursor));
2140    }
2141    let [start] = starts.as_slice() else {
2142        return None;
2143    };
2144    Some(*start)
2145}
2146
2147/// The `function_definition` that gives `declarator` a body, following only the
2148/// declarator chain, so a recovered callable knows whether it is a declaration
2149/// or a definition without anyone having to say.
2150fn cpp_declarator_function_definition<'tree>(
2151    declarator: Node<'tree>,
2152    ancestry: &ParentIndex<'tree>,
2153) -> Option<Node<'tree>> {
2154    let mut current = declarator;
2155    while let Some(parent) = ancestry.parent(current) {
2156        match parent.kind() {
2157            "function_definition" if parent.child_by_field_name("body").is_some() => {
2158                return Some(parent);
2159            }
2160            "pointer_declarator"
2161            | "reference_declarator"
2162            | "parenthesized_declarator"
2163            | "array_declarator" => current = parent,
2164            _ => return None,
2165        }
2166    }
2167    None
2168}
2169
2170/// Whether `node` lies in the body of a `namespace_definition` the ordinary
2171/// declaration walk still reaches, so a recovery that runs ahead of that walk
2172/// would name what it finds without the namespace.
2173fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2174    let mut current = node;
2175    while let Some(parent) = ancestry.parent(current) {
2176        if parent.kind() == "namespace_definition"
2177            && parent.child_by_field_name("body") == Some(current)
2178        {
2179            return true;
2180        }
2181        current = parent;
2182    }
2183    false
2184}
2185
2186/// Whether the source a recovered callable's byte range spells is a definition
2187/// (a declarator with a body) rather than a declaration.
2188///
2189/// A callable recovered from a mangled region owns no node of its own in the
2190/// file's tree, so the occurrence-role scan that climbs from the range to the
2191/// node containing it lands on whatever container the parser left and answers
2192/// for that instead -- which called Botan's inline `XMSS_Parameters` private
2193/// constructor a declaration and left its call site with nothing to navigate to
2194/// (#2552). Reparse the range on its own, the same offset-preserving reparse
2195/// extraction used, and read the answer from the one declaration it spells.
2196///
2197/// `None` when the range is not one recovered declaration on its own, which is
2198/// the case for every ordinary declaration, so callers keep their own answer.
2199pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2200    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2201    let root = tree.root_node();
2202    let mut cursor = root.walk();
2203    let items = root
2204        .named_children(&mut cursor)
2205        .filter(|child| child.kind() != "comment")
2206        .collect::<Vec<_>>();
2207    let [item] = items.as_slice() else {
2208        return None;
2209    };
2210    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2211        return None;
2212    }
2213    match item.kind() {
2214        "function_definition" => Some(item.child_by_field_name("body").is_some()),
2215        "declaration" | "field_declaration" => Some(false),
2216        _ => None,
2217    }
2218}
2219
2220/// Whether `node` is an envelope an attribute-like macro invocation left where
2221/// declarations were written: the declaration-scope `ERROR`
2222/// [`macro_wrapped_declarations`] reads, or the collapsed run
2223/// [`collapsed_macro_declaration_run`] reads.
2224///
2225/// Extraction and resolution must read one definition of this shape. The
2226/// resolver climbs from a declaration's recorded byte range to the node that
2227/// declares it, and a recovered declaration's range lies inside one of these
2228/// envelopes rather than inside a `declaration` node, so the climb stops here
2229/// (the same role `is_recovered_exported_class_container` plays for a recovered
2230/// class).
2231pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2232    let ancestry = ParentIndex::unindexed();
2233    !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2234        || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2235}
2236
2237fn recover_exported_class_function_definition<'tree>(
2238    node: Node<'tree>,
2239    source: &str,
2240) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
2241    if node.kind() != "function_definition" {
2242        return None;
2243    }
2244    if let Some(prefix) = node.prev_named_sibling()
2245        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2246        && recovered.range.end_byte == node.end_byte()
2247    {
2248        return Some((node, recovered.name, recovered.raw_supertypes));
2249    }
2250    let type_node = node.child_by_field_name("type")?;
2251    let declarator = node.child_by_field_name("declarator")?;
2252
2253    if matches!(
2254        type_node.kind(),
2255        "class_specifier" | "struct_specifier" | "union_specifier"
2256    ) {
2257        let type_name = type_node
2258            .child_by_field_name("name")
2259            .and_then(|name| direct_identifier_name(name, source));
2260        let exported_macro_type = type_name
2261            .as_ref()
2262            .is_some_and(|name| cpp_export_macro_token(name));
2263        if exported_macro_type {
2264            let mut cursor = node.walk();
2265            let errors_before_declarator = node
2266                .named_children(&mut cursor)
2267                .filter(|child| {
2268                    child.kind() == "ERROR"
2269                        && child.start_byte() >= type_node.end_byte()
2270                        && child.end_byte() <= declarator.start_byte()
2271                })
2272                .collect::<Vec<_>>();
2273            if let Some(name) = errors_before_declarator
2274                .iter()
2275                .find_map(|error| displaced_exported_class_name(*error, source))
2276            {
2277                let raw_supertypes = errors_before_declarator
2278                    .iter()
2279                    .any(|error| malformed_inheritance_syntax(*error))
2280                    .then(|| recovered_malformed_base_name(declarator, source))
2281                    .flatten()
2282                    .map(|base| vec![base]);
2283                return Some((node, name, raw_supertypes));
2284            }
2285            if errors_before_declarator
2286                .iter()
2287                .any(|error| malformed_inheritance_syntax(*error))
2288            {
2289                return None;
2290            }
2291        }
2292        if !exported_macro_type
2293            && let Some(name) = type_name
2294            && !cpp_export_macro_token(&name)
2295            && let Some(base) =
2296                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2297        {
2298            return Some((node, name, Some(vec![base])));
2299        }
2300        if let Some(name) = direct_identifier_name(declarator, source)
2301            && exported_macro_type
2302            && !cpp_export_macro_token(&name)
2303        {
2304            let raw_supertypes = exported_macro_type
2305                .then(|| recovered_single_base_after_declarator(node, declarator, source))
2306                .flatten()
2307                .map(|base| vec![base]);
2308            return Some((node, name, raw_supertypes));
2309        }
2310        if declarator.kind() == "parenthesized_declarator"
2311            && type_node
2312                .child_by_field_name("name")
2313                .and_then(|name| direct_identifier_name(name, source))
2314                .is_some_and(|name| cpp_export_macro_token(&name))
2315        {
2316            if let Some((name, base)) =
2317                recovered_function_like_export_class_owner(declarator, source)
2318            {
2319                return Some((node, name, Some(vec![base])));
2320            }
2321            let body_start = node
2322                .child_by_field_name("body")
2323                .map(|body| body.start_byte())
2324                .unwrap_or(node.end_byte());
2325            let mut cursor = node.walk();
2326            if let Some(name) = node
2327                .named_children(&mut cursor)
2328                .filter(|child| {
2329                    child.kind() == "ERROR"
2330                        && child.start_byte() >= declarator.end_byte()
2331                        && child.end_byte() <= body_start
2332                })
2333                .find_map(|error| declarator_name_from_node(error, source))
2334            {
2335                return Some((node, name, None));
2336            }
2337        }
2338    }
2339
2340    let declarator_text = direct_identifier_name(declarator, source)?;
2341    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2342        return None;
2343    }
2344    class_identifier_before_body(node, source).map(|name| (node, name, None))
2345}
2346
2347fn recovered_function_like_export_class_owner(
2348    declarator: Node<'_>,
2349    source: &str,
2350) -> Option<(String, String)> {
2351    if declarator.kind() != "parenthesized_declarator" {
2352        return None;
2353    }
2354    let mut cursor = declarator.walk();
2355    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2356    let [prefix, base] = children.as_slice() else {
2357        return None;
2358    };
2359    if prefix.kind() != "ERROR"
2360        || !matches!(
2361            base.kind(),
2362            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2363        )
2364    {
2365        return None;
2366    }
2367    let mut identifiers = Vec::new();
2368    let mut prefix_cursor = prefix.walk();
2369    for child in prefix.named_children(&mut prefix_cursor) {
2370        match child.kind() {
2371            "number_literal" | "string_literal" | "char_literal" => {}
2372            "identifier" | "type_identifier" => {
2373                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2374            }
2375            _ => return None,
2376        }
2377    }
2378    let name = match identifiers.as_slice() {
2379        [name] => name.clone(),
2380        [name, final_token] if final_token == "final" => name.clone(),
2381        _ => return None,
2382    };
2383    if name.is_empty() || cpp_export_macro_token(&name) {
2384        return None;
2385    }
2386    let base = recovered_malformed_base_name(*base, source)?;
2387    Some((name, base))
2388}
2389
2390/// Collect the base names tree-sitter scattered across the recovered head of a
2391/// function-like export-macro class. `skip` names the structural children that
2392/// are not bases, such as the class name and the body. A base arrives either as
2393/// a direct sibling identifier or inside the `ERROR` node the grammar produced
2394/// for a `: public Base` fragment. The grammar leaves the `final` specifier and
2395/// the access specifiers in the same position as the bases, so drop them.
2396/// The bases a recovered function-like export-macro class head names between
2397/// `after` (the end of the class name, or of the access specifier that stands
2398/// in for it) and `before` (the start of the body, or of the `init_declarator`
2399/// that carries it). Bases arrive as bare declarator fields and inside `ERROR`
2400/// fragments, and one fragment can also hold the class name and `final`
2401/// (`M1 M2 Name final : public A`), so each part is bounded by position rather
2402/// than by the fragment that holds it.
2403fn recovered_export_head_bases(
2404    node: Node<'_>,
2405    after: usize,
2406    before: usize,
2407    source: &str,
2408) -> Vec<String> {
2409    let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2410    let mut bases = Vec::new();
2411    let mut cursor = node.walk();
2412    for child in node.named_children(&mut cursor) {
2413        if child.kind() == "ERROR" {
2414            let mut error_cursor = child.walk();
2415            bases.extend(
2416                child
2417                    .named_children(&mut error_cursor)
2418                    .filter(within)
2419                    .filter_map(|part| recovered_malformed_base_name(part, source)),
2420            );
2421        } else if within(&child)
2422            && let Some(base) = recovered_malformed_base_name(child, source)
2423        {
2424            bases.push(base);
2425        }
2426    }
2427    bases.retain(|base| {
2428        !matches!(
2429            base.as_str(),
2430            "final" | "public" | "protected" | "private" | "virtual"
2431        )
2432    });
2433    bases
2434}
2435
2436/// Whether `token` is the `final` that closes a recovered class head. The
2437/// grammar keeps it as the `final` keyword when it recognised the head
2438/// (`M1 M2 Name final : public A`) and as an `identifier` spelled `final` when
2439/// it did not (`Name final {`, `OTHER_MACRO Name final : public A`).
2440fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2441    if token.is_named() {
2442        token.kind() == "identifier" && node_text(token, source) == "final"
2443    } else {
2444        token.kind() == "final"
2445    }
2446}
2447
2448/// The class name of a recovered function-like export-macro class head, read by
2449/// position rather than spelling (#2557). `node` is the sibling tree-sitter
2450/// built from the head after `class MACRO(2, 0)`, and `tail` the child that
2451/// carries the body. The head's tokens, in source order with `ERROR` fragments
2452/// flattened, read `macros... name [final] [: bases...]`: the name is the last
2453/// identifier before the head ends, and the head ends at `final`, at the base
2454/// clause `:`, or at `tail`. Every identifier before the name is decoration
2455/// (`class MACRO(2, 0) OTHER_MACRO Name`), and a class named in capitals
2456/// (`X509_CA`) is a class name like any other.
2457///
2458/// A `MISSING` identifier is a zero-width node tree-sitter invented to close a
2459/// rule, not a token of the source, so it is never the name.
2460fn recovered_export_head_name<'tree>(
2461    node: Node<'tree>,
2462    tail: Node<'tree>,
2463    source: &str,
2464) -> Option<Node<'tree>> {
2465    export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2466}
2467
2468/// The class name of a head whose tokens straddle the recovered pair: the
2469/// grammar can keep `class MACRO(3, 6) Name final :` in the prefix fragment and
2470/// only the base list and the body in the sibling (Botan's TPM2 keys, #2924).
2471/// The positional rule is the same, read over the two token runs in source
2472/// order, so a name in either node wins by position rather than by which node
2473/// holds it.
2474fn recovered_export_pair_head_name<'tree>(
2475    prefix: Node<'tree>,
2476    sibling: Node<'tree>,
2477    tail: Node<'tree>,
2478    source: &str,
2479) -> Option<Node<'tree>> {
2480    let mut tokens = export_head_tokens(prefix, None);
2481    tokens.extend(export_head_tokens(sibling, Some(tail)));
2482    export_head_name_from_tokens(&tokens, source)
2483}
2484
2485/// One node's head tokens in source order, with `ERROR` fragments spliced in
2486/// place (an `ERROR` here is a grouping failure, not a construct) and stopping
2487/// before `tail` when the caller names one.
2488fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2489    let mut tokens = Vec::new();
2490    let mut cursor = node.walk();
2491    for child in node.children(&mut cursor) {
2492        if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2493            break;
2494        }
2495        if child.kind() == "ERROR" {
2496            let mut fragment_cursor = child.walk();
2497            tokens.extend(child.children(&mut fragment_cursor));
2498        } else {
2499            tokens.push(child);
2500        }
2501    }
2502    tokens
2503}
2504
2505/// The last identifier before the head ends, per the positional rule.
2506fn export_head_name_from_tokens<'tree>(
2507    tokens: &[Node<'tree>],
2508    source: &str,
2509) -> Option<Node<'tree>> {
2510    let mut name = None;
2511    for token in tokens.iter().copied() {
2512        if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2513        {
2514            break;
2515        }
2516        if token.is_named()
2517            && !token.is_missing()
2518            && matches!(
2519                token.kind(),
2520                "identifier" | "type_identifier" | "field_identifier"
2521            )
2522        {
2523            name = Some(token);
2524        }
2525    }
2526    name
2527}
2528
2529/// The `init_declarator` whose `value` carries the class body when the grammar
2530/// keeps a function-like export-macro class head as one `declaration`.
2531fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2532    let mut cursor = declaration.walk();
2533    declaration
2534        .named_children(&mut cursor)
2535        .find(|child| child.kind() == "init_declarator")
2536}
2537
2538/// Read the bases and the initializer-list body of a `declaration`-shaped tail
2539/// of a function-like export-macro class head. The grammar splits a base list
2540/// across bare declarator fields, `ERROR` fragments, and one trailing
2541/// `init_declarator` whose `value` is the class body. `head_end` is where the
2542/// class identity ends and the bases can begin: the end of the access specifier
2543/// when the class name went to a statement label, and the end of the class name
2544/// itself otherwise.
2545fn recovered_export_declaration_tail<'tree>(
2546    declaration: Node<'tree>,
2547    head_end: usize,
2548    source: &str,
2549) -> Option<(Vec<String>, Node<'tree>)> {
2550    let init = recovered_export_init_declarator(declaration)?;
2551    let body = init.child_by_field_name("value")?;
2552    if body.kind() != "initializer_list" {
2553        return None;
2554    }
2555    let mut bases = recovered_export_head_bases(declaration, head_end, init.start_byte(), source);
2556    bases.extend(recovered_export_head_bases(
2557        init,
2558        init.start_byte(),
2559        body.start_byte(),
2560        source,
2561    ));
2562    Some((bases, body))
2563}
2564
2565/// Whether `node` is the head fragment tree-sitter leaves where
2566/// `class MACRO(args)` was written: the class keyword and the export macro's
2567/// function-like invocation in that order, with nothing else between them.
2568/// Spelling plays no part -- the invocation's `(` following the macro
2569/// identifier directly is what makes it function-like (#2557).
2570///
2571/// Two reductions produce the head. On its own the grammar keeps a body-less
2572/// `class_specifier` named after the macro inside a declaration-scope `ERROR`,
2573/// with the invocation's `(`, arguments and `)` beside it. After object-like
2574/// macro lines -- Botan's `BOTAN_DIAGNOSTIC_PUSH` /
2575/// `BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE` -- it instead keeps a
2576/// `declaration` whose class keyword is demoted to a bare `identifier` and
2577/// whose macro invocation becomes the trailing `init_declarator` (#2924).
2578fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2579    match node.kind() {
2580        "ERROR" => {
2581            let Some(class_node) = first_class_like_child(node) else {
2582                return false;
2583            };
2584            if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2585                return false;
2586            }
2587            if class_node
2588                .child_by_field_name("name")
2589                .and_then(|name| direct_identifier_name(name, source))
2590                .is_none()
2591            {
2592                return false;
2593            }
2594            class_node
2595                .next_sibling()
2596                .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2597        }
2598        "declaration" => {
2599            let mut cursor = node.walk();
2600            let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2601            // The class keyword lost its own node: it survives as a bare
2602            // identifier spelled `class`, `struct` or `union`, which is never a
2603            // declarator or a type in well-formed code. It can sit inside an
2604            // `ERROR` beside the object-like macro names, and an `ERROR` here is
2605            // a grouping failure rather than a construct, so read through it.
2606            let Some(keyword) = children
2607                .iter()
2608                .copied()
2609                .flat_map(|child| {
2610                    let mut cursor = child.walk();
2611                    if child.kind() == "ERROR" {
2612                        child.named_children(&mut cursor).collect::<Vec<_>>()
2613                    } else {
2614                        vec![child]
2615                    }
2616                })
2617                .find(|child| {
2618                    child.kind() == "identifier"
2619                        && matches!(node_text(*child, source), "class" | "struct" | "union")
2620                })
2621            else {
2622                return false;
2623            };
2624            children.last().is_some_and(|init| {
2625                init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2626                    let mut cursor = init.walk();
2627                    let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2628                    matches!(parts.as_slice(), [macro_name, arguments]
2629                        if matches!(macro_name.kind(), "identifier" | "type_identifier")
2630                            && arguments.kind() == "argument_list")
2631                }
2632            })
2633        }
2634        _ => false,
2635    }
2636}
2637
2638fn recover_function_like_export_class_pair(
2639    node: Node<'_>,
2640    source: &str,
2641) -> Option<RecoveredFunctionLikeExportClassPair> {
2642    if !is_function_like_export_class_head(node, source) {
2643        return None;
2644    }
2645    let sibling = node.next_named_sibling()?;
2646    let (name, raw_supertypes, body) = match sibling.kind() {
2647        // At translation-unit scope tree-sitter can leave the malformed class
2648        // head in one ERROR node and parse its body as the adjacent compound
2649        // statement. The head still carries the export invocation, displaced
2650        // class name, and optional `final` token in source order, so recover
2651        // the name by the same positional rule as the other shapes.
2652        "compound_statement" => (
2653            recovered_export_pair_head_name(node, sibling, sibling, source)
2654                .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2655            None,
2656            sibling,
2657        ),
2658        "expression_statement" => {
2659            let compound = sibling.named_child(0)?;
2660            if compound.kind() != "compound_literal_expression" {
2661                return None;
2662            }
2663            let body = compound.child_by_field_name("value")?;
2664            if body.kind() != "initializer_list" {
2665                return None;
2666            }
2667            (
2668                compound
2669                    .child_by_field_name("type")
2670                    .and_then(|name| direct_identifier_name(name, source))?,
2671                None,
2672                body,
2673            )
2674        }
2675        "labeled_statement" => {
2676            let label = sibling.child_by_field_name("label")?;
2677            if label.kind() != "statement_identifier" {
2678                return None;
2679            }
2680            let name = normalize_cpp_whitespace(node_text(label, source));
2681            let declaration = sibling
2682                .named_children(&mut sibling.walk())
2683                .find(|child| child.kind() == "declaration")?;
2684            let access = declaration.child_by_field_name("type")?;
2685            if !matches!(
2686                node_text(access, source),
2687                "public" | "protected" | "private"
2688            ) {
2689                return None;
2690            }
2691            let (bases, body) =
2692                recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
2693            (name, (!bases.is_empty()).then_some(bases), body)
2694        }
2695        // `class MACRO(2, 0) Name final { ... };`,
2696        // `class MACRO(2, 0) Name final : public Base { ... };`, and
2697        // `class MACRO(2, 0) OTHER_MACRO Name { ... };`. The head's identifiers
2698        // spread over the `type` field, the declarator field, and `ERROR`
2699        // fragments beside them; the name is the last one before the head ends.
2700        "function_definition" => {
2701            let body = sibling.child_by_field_name("body")?;
2702            if body.kind() != "compound_statement" {
2703                return None;
2704            }
2705            let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
2706            let bases = recovered_export_head_bases(
2707                sibling,
2708                name_node.end_byte(),
2709                body.start_byte(),
2710                source,
2711            );
2712            (
2713                normalize_cpp_whitespace(node_text(name_node, source)),
2714                (!bases.is_empty()).then_some(bases),
2715                body,
2716            )
2717        }
2718        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
2719        // comma-separated base list makes the grammar keep the whole tail as one
2720        // declaration whose trailing `init_declarator` carries the body.
2721        "declaration" => {
2722            let init = recovered_export_init_declarator(sibling)?;
2723            let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
2724            let (bases, body) =
2725                recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
2726            (
2727                normalize_cpp_whitespace(node_text(name_node, source)),
2728                (!bases.is_empty()).then_some(bases),
2729                body,
2730            )
2731        }
2732        _ => return None,
2733    };
2734    debug_assert!(
2735        !name.is_empty(),
2736        "a recovered class head names its class by an identifier token"
2737    );
2738    let range = Range {
2739        start_byte: node.start_byte(),
2740        end_byte: sibling.end_byte(),
2741        start_line: node.start_position().row + 1,
2742        end_line: sibling.end_position().row + 1,
2743    };
2744    Some(RecoveredFunctionLikeExportClassPair {
2745        name,
2746        raw_supertypes,
2747        range,
2748        fragmented_body: recovered_fragmented_export_body(body, range)?,
2749    })
2750}
2751
2752/// Recover a function-like export-macro class that tree-sitter embedded in a
2753/// larger error after an earlier malformed class body. The grammar still
2754/// preserves every part of the class head: the `class` token, export macro
2755/// identifier and argument list, displaced class identifier, access specifier,
2756/// base field, and initializer-list-shaped body. Match only that complete
2757/// structured sequence and keep each recovered class's exact byte envelope.
2758fn recover_embedded_function_like_export_classes(
2759    node: Node<'_>,
2760    source: &str,
2761) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2762    if !node.is_error() {
2763        return Vec::new();
2764    }
2765
2766    let mut nodes = Vec::new();
2767    let mut class_tokens = Vec::new();
2768    let mut stack = vec![node];
2769    while let Some(current) = stack.pop() {
2770        nodes.push(current);
2771        // An error subtree without an anonymous class token cannot contain
2772        // a recoverable class head. Detect that before sorting or resolving
2773        // kind-id tables; the grammar spelling matches all aliased symbol ids.
2774        if !current.is_named() && current.kind() == "class" {
2775            class_tokens.push(current);
2776        }
2777        push_children_reversed(current, &mut stack);
2778    }
2779    if class_tokens.is_empty() {
2780        return Vec::new();
2781    }
2782    class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2783    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2784
2785    // The scans below ask the same handful of kind questions once per node of
2786    // the error subtree, so the kinds are resolved to symbol ids first (#3097).
2787    let language = node.language();
2788    let class_kind = NodeKindIds::new(&language, "class");
2789    let identifier_kinds = [
2790        NodeKindIds::new(&language, "identifier"),
2791        NodeKindIds::new(&language, "type_identifier"),
2792        NodeKindIds::new(&language, "field_identifier"),
2793    ];
2794    let argument_list_kind = NodeKindIds::new(&language, "argument_list");
2795    let colon_kind = NodeKindIds::new(&language, ":");
2796    let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
2797
2798    let mut recovered = Vec::new();
2799    for class_token in class_tokens {
2800        debug_assert!(class_kind.matches(class_token));
2801        let row = class_token.start_position().row;
2802        // The head after the `class` token reads `MACRO(args) macros... name
2803        // [final] : bases`. The macro is the first identifier, a function-like
2804        // invocation when its argument list is the next thing after it, and the
2805        // name is the last identifier before the head ends at `final` or at the
2806        // base clause `:`. Spelling plays no part (#2557).
2807        let is_identifier = |candidate: &Node<'_>| {
2808            !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
2809        };
2810        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2811            candidate.start_byte() >= class_token.end_byte()
2812                && candidate.start_position().row == row
2813                && is_identifier(candidate)
2814        }) else {
2815            continue;
2816        };
2817        let Some(arguments) = nodes.iter().copied().find(|candidate| {
2818            argument_list_kind.matches(*candidate)
2819                && candidate.start_byte() >= macro_name.end_byte()
2820                && candidate.start_position().row == row
2821        }) else {
2822            continue;
2823        };
2824        if nodes.iter().any(|candidate| {
2825            is_identifier(candidate)
2826                && candidate.start_byte() >= macro_name.end_byte()
2827                && candidate.end_byte() <= arguments.start_byte()
2828        }) {
2829            continue;
2830        }
2831        let Some(head_end) = nodes.iter().copied().find(|candidate| {
2832            candidate.start_byte() >= arguments.end_byte()
2833                && (recovered_export_head_final(*candidate, source)
2834                    || (!candidate.is_named() && colon_kind.matches(*candidate)))
2835        }) else {
2836            continue;
2837        };
2838        let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
2839            is_identifier(candidate)
2840                && candidate.start_byte() >= arguments.end_byte()
2841                && candidate.end_byte() <= head_end.start_byte()
2842                && candidate.start_position().row == row
2843        }) else {
2844            continue;
2845        };
2846        let name = normalize_cpp_whitespace(node_text(name_node, source));
2847        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2848            field_initializer_kind.matches(*candidate)
2849                && candidate.start_byte() >= name_node.end_byte()
2850                && candidate
2851                    .child_by_field_name("field")
2852                    .or_else(|| candidate.named_child(0))
2853                    .is_some()
2854                && candidate
2855                    .child_by_field_name("value")
2856                    .or_else(|| {
2857                        let mut cursor = candidate.walk();
2858                        candidate
2859                            .named_children(&mut cursor)
2860                            .find(|child| child.kind() == "initializer_list")
2861                    })
2862                    .is_some_and(|value| value.kind() == "initializer_list")
2863        }) else {
2864            continue;
2865        };
2866        let has_access = nodes.iter().copied().any(|candidate| {
2867            candidate.start_byte() >= name_node.end_byte()
2868                && candidate.end_byte() <= base_initializer.start_byte()
2869                && matches!(
2870                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2871                    "public" | "protected" | "private"
2872                )
2873        });
2874        if !has_access {
2875            continue;
2876        }
2877        let Some(base_node) = base_initializer
2878            .child_by_field_name("field")
2879            .or_else(|| base_initializer.named_child(0))
2880        else {
2881            continue;
2882        };
2883        let Some(base) = recovered_malformed_base_name(base_node, source) else {
2884            continue;
2885        };
2886        let body = base_initializer
2887            .child_by_field_name("value")
2888            .or_else(|| {
2889                let mut cursor = base_initializer.walk();
2890                base_initializer
2891                    .named_children(&mut cursor)
2892                    .find(|child| child.kind() == "initializer_list")
2893            })
2894            .expect("initializer-list value checked above");
2895        let range = Range {
2896            start_byte: class_token.start_byte(),
2897            end_byte: body.end_byte(),
2898            start_line: class_token.start_position().row + 1,
2899            end_line: body.end_position().row + 1,
2900        };
2901        if recovered
2902            .iter()
2903            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
2904                existing.name == name && existing.range == range
2905            })
2906        {
2907            continue;
2908        }
2909        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
2910            name,
2911            range,
2912            raw_supertypes: vec![base],
2913            fragmented_body: match recovered_fragmented_export_body(body, range) {
2914                Some(fragmented) => fragmented,
2915                None => continue,
2916            },
2917        });
2918    }
2919    recovered
2920}
2921
2922fn lifted_function_like_export_class_namespace<'tree>(
2923    node: Node<'tree>,
2924    source: &str,
2925    ancestry: &ParentIndex<'tree>,
2926) -> Option<String> {
2927    // A long malformed body can embed the next exported class several levels
2928    // below a bogus top-level function_definition. Compare namespace evidence
2929    // against that top-level envelope, not only the recovered ERROR's direct
2930    // parent. The source tree still proves the same boundary: one earlier
2931    // malformed namespace and one later standalone closing brace.
2932    let mut anchor = node;
2933    let parent = loop {
2934        let parent = ancestry.parent(anchor)?;
2935        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
2936            break parent;
2937        }
2938        anchor = parent;
2939    };
2940    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
2941        sibling.start_byte() > anchor.end_byte()
2942            && sibling.kind() == "ERROR"
2943            && sibling.named_child_count() == 0
2944            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
2945    });
2946    if !has_later_close {
2947        return None;
2948    }
2949    let candidates = parent
2950        .named_children(&mut parent.walk())
2951        .filter(|sibling| {
2952            sibling.kind() == "namespace_definition"
2953                && sibling.has_error()
2954                && sibling.end_byte() < anchor.start_byte()
2955        })
2956        .filter_map(|namespace| {
2957            namespace
2958                .child_by_field_name("name")
2959                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
2960                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
2961        })
2962        .collect::<Vec<_>>();
2963    let [namespace] = candidates.as_slice() else {
2964        return None;
2965    };
2966    Some(namespace.clone())
2967}
2968
2969pub(crate) fn recovered_function_like_export_class_pair_has_body(
2970    node: Node<'_>,
2971    source: &str,
2972    identifier: &str,
2973    range: &Range,
2974) -> bool {
2975    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
2976        recovered.name == identifier
2977            && recovered.range.start_byte == range.start_byte
2978            && recovered.range.end_byte == range.end_byte
2979    })
2980}
2981
2982/// One file's embedded export-macro class recovery, resolved once and keyed by
2983/// the `ERROR` node that mints each set.
2984///
2985/// [`recover_embedded_function_like_export_classes`] collects and sorts an
2986/// `ERROR` node's whole subtree, and the declaration-strength question asks it
2987/// once per class-like unit in the file. On a translation unit the parser could
2988/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
2989/// node spans most of the file -- that is one full subtree pass per class,
2990/// quadratic in the file's size, and it was 78% of that file's inverse scan
2991/// (#1496).
2992///
2993/// Keyed by byte span rather than node identity, so one analyzer generation's
2994/// re-parses of the same content share the index. A nested `ERROR` that shares
2995/// its parent's span recovers the same classes: the only node the parent adds
2996/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
2997#[derive(Default)]
2998pub struct CppRecoveredExportClassIndex {
2999    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3000}
3001
3002impl CppRecoveredExportClassIndex {
3003    pub fn build(root: Node<'_>, source: &str) -> Self {
3004        let mut by_error_node: HashMap<
3005            (usize, usize),
3006            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3007        > = HashMap::default();
3008        // One cursor for the whole-file walk, and the error question asked by
3009        // symbol rather than by `kind()` string (#3097).
3010        let mut cursor = root.walk();
3011        let mut stack = vec![root];
3012        while let Some(node) = stack.pop() {
3013            if node.is_error() {
3014                let recovered = recover_embedded_function_like_export_classes(node, source);
3015                if !recovered.is_empty() {
3016                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3017                }
3018            }
3019            stack.extend(node.named_children(&mut cursor));
3020        }
3021        Self { by_error_node }
3022    }
3023
3024    /// The bytes this index holds, for the analyzer cache's weight.
3025    pub fn approximate_size(&self) -> usize {
3026        self.by_error_node
3027            .values()
3028            .fold(0usize, |total, recovered| {
3029                recovered.iter().fold(
3030                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3031                    |acc, class| {
3032                        acc.saturating_add(std::mem::size_of::<
3033                            RecoveredEmbeddedFunctionLikeExportClass,
3034                        >())
3035                        .saturating_add(class.name.len())
3036                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3037                    },
3038                )
3039            })
3040    }
3041
3042    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3043        self.by_error_node
3044            .get(&(node.start_byte(), node.end_byte()))
3045            .is_some_and(|recovered| {
3046                recovered.iter().any(|class| {
3047                    class.name == identifier
3048                        && class.range.start_byte == range.start_byte
3049                        && class.range.end_byte == range.end_byte
3050                })
3051            })
3052    }
3053}
3054
3055// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
3056// `recovered_class_body_at` pops while deciding whether a recovered class shape
3057// claims one declaration range. The count is deterministic for a given source,
3058// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
3059// pins it directly instead of timing the walk, the way #2358 pinned the
3060// `remove_code_unit` scan.
3061#[cfg(any(test, feature = "test-support"))]
3062thread_local! {
3063    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3064        const { std::cell::Cell::new(0) };
3065}
3066
3067/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
3068/// the calling thread since the last
3069/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
3070#[cfg(any(test, feature = "test-support"))]
3071#[doc(hidden)]
3072pub fn recovered_class_body_node_visits_for_test() -> usize {
3073    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3074}
3075
3076/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
3077#[cfg(any(test, feature = "test-support"))]
3078#[doc(hidden)]
3079pub fn reset_recovered_class_body_node_visits_for_test() {
3080    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3081}
3082
3083#[cfg(any(test, feature = "test-support"))]
3084fn record_recovered_class_body_visit() {
3085    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3086}
3087
3088#[cfg(not(any(test, feature = "test-support")))]
3089fn record_recovered_class_body_visit() {}
3090
3091/// Whether a recovered class shape named `identifier` owns `range`, and if so
3092/// whether that shape has a body.
3093///
3094/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
3095/// forward declaration, and `None` means no recovered shape claims the range,
3096/// so the caller reads the plain `class_specifier` family instead. This is the
3097/// single definition of "does a recovered class have a body": the resolver's
3098/// declaration-strength answer and the navigation occurrence role both read it,
3099/// so an export-macro class is a definition on both paths.
3100///
3101/// The walk follows only the nodes whose span covers `range.start_byte`, which
3102/// is every node that can answer. Each recovered shape reports a range that
3103/// starts at the node's own start byte (the function-like export pair, whose
3104/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
3105/// class, which the caller gates on an equal start) or at a token inside the
3106/// node (the embedded export class, keyed on its `class` token, and the
3107/// exported class wrapper, gated on containment here), and every one of them is
3108/// accepted only on an exact match with `range`. Descending everywhere instead
3109/// made one declaration-strength question cost a full pass over the file, so
3110/// asking it once per reference was quadratic in file size: 80% of the 385 s
3111/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
3112/// (#1496).
3113pub(crate) fn recovered_class_body_at(
3114    recovered_export_classes: &CppRecoveredExportClassIndex,
3115    root: Node<'_>,
3116    source: &str,
3117    identifier: &str,
3118    range: &Range,
3119) -> Option<bool> {
3120    let covers_range_start = |node: &Node<'_>| {
3121        node.start_byte() <= range.start_byte
3122            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3123    };
3124    let mut stack = vec![root];
3125    let mut saw_forward = false;
3126    while let Some(node) = stack.pop() {
3127        record_recovered_class_body_visit();
3128        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
3129        // so an unequal start settles it before the recovery reads the node's
3130        // children at all.
3131        if (node.start_byte() == range.start_byte
3132            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3133            || recovered_export_classes.claims(node, identifier, range)
3134            || (node.start_byte() == range.start_byte
3135                && recovered_fragmented_class_has_body(node, source, identifier, range))
3136        {
3137            return Some(true);
3138        }
3139        if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3140            return Some(true);
3141        }
3142        // Macro-decorated exported classes are recovered from a malformed
3143        // function_definition/declaration wrapper. Their indexed class range starts at
3144        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
3145        // may also extend the indexed range beyond the wrapper through trailing class
3146        // fragments. Match the structured container that owns the range start by its
3147        // recovered name instead of requiring identical boundaries.
3148        if node.start_byte() <= range.start_byte
3149            && range.start_byte < node.end_byte()
3150            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3151        {
3152            if has_body {
3153                return Some(true);
3154            }
3155            saw_forward = true;
3156            continue;
3157        }
3158        let mut cursor = node.walk();
3159        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3160    }
3161    saw_forward.then_some(false)
3162}
3163
3164/// Whether the collapsed-aggregate recovery reads `node`'s children as the
3165/// head of `identifier`'s member list at `range`.
3166///
3167/// An aggregate whose member list opens with a field-list macro invocation
3168/// never forms a specifier, so the ordinary strength read finds no specifier
3169/// node at the indexed range and answers `Unknown`. A complete definition
3170/// recovered this way then loses the defining-type choice to a forward
3171/// declaration of the same tag in another header, and every member the
3172/// definition declares becomes unreachable through it: libuv's
3173/// `struct uv_loop_s` in `include/uv.h` lost to `struct uv_loop_s;` in
3174/// `include/uv/unix.h`, hiding `uv_loop_s.wq` (#3098). The recovery already
3175/// proves the body, so it answers for its own ranges.
3176fn recovered_collapsed_aggregate_has_body(
3177    node: Node<'_>,
3178    source: &str,
3179    identifier: &str,
3180    range: &Range,
3181) -> bool {
3182    let claims = |head: &CppCollapsedAggregateHead<'_>| {
3183        head.key.start_byte() == range.start_byte
3184            && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3185    };
3186    if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3187        return true;
3188    }
3189    if !node.is_error() {
3190        return false;
3191    }
3192    let mut cursor = node.walk();
3193    let children = node.children(&mut cursor).collect::<Vec<_>>();
3194    children
3195        .iter()
3196        .enumerate()
3197        .filter(|(_, child)| {
3198            child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3199        })
3200        .any(|(index, _)| {
3201            cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3202        })
3203}
3204
3205/// Whether `node` is the base type displaced into the declarator field of an
3206/// export-macro class that tree-sitter represented as a declaration or
3207/// function definition.
3208///
3209/// Declaration extraction already recovers this exact malformed envelope as a
3210/// class and records the declarator as its base. Reference extraction must use
3211/// the same structural fact instead of treating the node as a function name.
3212pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3213    if !matches!(
3214        node.kind(),
3215        "qualified_identifier" | "scoped_type_identifier" | "template_type"
3216    ) {
3217        return false;
3218    }
3219    if let Some(function) = node.parent().filter(|parent| {
3220        parent.kind() == "function_definition"
3221            && parent
3222                .child_by_field_name("declarator")
3223                .is_some_and(|declarator| same_node(declarator, node))
3224    }) {
3225        return recover_exported_class_function_definition(function, source)
3226            .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
3227    }
3228    let Some(initializer) = node.parent().filter(|parent| {
3229        parent.kind() == "init_declarator"
3230            && parent
3231                .child_by_field_name("declarator")
3232                .is_some_and(|declarator| same_node(declarator, node))
3233    }) else {
3234        return false;
3235    };
3236    initializer
3237        .parent()
3238        .filter(|parent| parent.kind() == "declaration")
3239        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3240        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3241}
3242
3243/// Recover the class item from a region reparse that still carries the
3244/// sentinel's synthetic function envelope.  An unknown class attribute can
3245/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
3246/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
3247/// then nested below that function, so direct class-child lookup is not enough.
3248struct CppSentinelReparsedClass<'tree> {
3249    declaration_node: Node<'tree>,
3250    name: String,
3251    body: Node<'tree>,
3252    raw_supertypes: Option<Vec<String>>,
3253}
3254
3255fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3256    let mut cursor = root.walk();
3257    root.named_children(&mut cursor)
3258        .find(|child| child.kind() != "comment")
3259        .filter(|child| child.kind() == "template_declaration")
3260}
3261
3262fn cpp_sentinel_reparsed_class<'tree>(
3263    root: Node<'tree>,
3264    template_node: Option<Node<'tree>>,
3265    source: &str,
3266    ancestry: &ParentIndex<'tree>,
3267) -> Option<CppSentinelReparsedClass<'tree>> {
3268    let container = template_node.unwrap_or(root);
3269    let mut cursor = container.walk();
3270    for child in container.named_children(&mut cursor) {
3271        if matches!(
3272            child.kind(),
3273            "class_specifier" | "struct_specifier" | "union_specifier"
3274        ) {
3275            let name = class_like_name(child, source, ancestry)?;
3276            let body = cpp_body_node(child)?;
3277            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3278                .then(|| extract_cpp_supertypes(child, source));
3279            return Some(CppSentinelReparsedClass {
3280                declaration_node: child,
3281                name,
3282                body,
3283                raw_supertypes,
3284            });
3285        }
3286        if child.kind() == "declaration"
3287            && let Some(class_node) = first_class_like_child(child)
3288        {
3289            let name = class_like_name(class_node, source, ancestry)?;
3290            let body = cpp_body_node(class_node)?;
3291            let raw_supertypes =
3292                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3293                    .then(|| extract_cpp_supertypes(class_node, source));
3294            return Some(CppSentinelReparsedClass {
3295                declaration_node: class_node,
3296                name,
3297                body,
3298                raw_supertypes,
3299            });
3300        }
3301        // Only when the nested class item carries its own body. A bodyless
3302        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
3303        // a function definition -- is the export-macro shape recovered by the
3304        // next arm, and must fall through to it rather than abort the search.
3305        if child.kind() == "function_definition"
3306            && let Some(class_node) = first_class_like_child(child)
3307            && let Some(body) = cpp_body_node(class_node)
3308            && let Some(name) = class_like_name(class_node, source, ancestry)
3309        {
3310            let raw_supertypes =
3311                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3312                    .then(|| extract_cpp_supertypes(class_node, source));
3313            return Some(CppSentinelReparsedClass {
3314                declaration_node: class_node,
3315                name,
3316                body,
3317                raw_supertypes,
3318            });
3319        }
3320        if child.kind() == "function_definition"
3321            && let Some((_, name, raw_supertypes)) =
3322                recover_exported_class_function_definition(child, source)
3323        {
3324            let body = cpp_body_node(child)?;
3325            return Some(CppSentinelReparsedClass {
3326                declaration_node: child,
3327                name,
3328                body,
3329                raw_supertypes,
3330            });
3331        }
3332    }
3333    None
3334}
3335
3336fn recovered_postfix_export_macro_base(
3337    node: Node<'_>,
3338    type_node: Node<'_>,
3339    declarator: Node<'_>,
3340    source: &str,
3341) -> Option<String> {
3342    let mut cursor = node.walk();
3343    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3344        child.kind() == "ERROR"
3345            && child.start_byte() >= type_node.end_byte()
3346            && child.end_byte() <= declarator.start_byte()
3347            && postfix_export_macro_inheritance(*child, source)
3348    });
3349    malformed_clauses.next()?;
3350    if malformed_clauses.next().is_some() {
3351        return None;
3352    }
3353    recovered_malformed_base_name(declarator, source)
3354}
3355
3356fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3357    let mut macro_count = 0;
3358    let mut colon_count = 0;
3359    let mut access_count = 0;
3360    for index in 0..node.child_count() {
3361        let Some(child) = node.child(index) else {
3362            return false;
3363        };
3364        match child.kind() {
3365            "identifier" | "type_identifier" if child.is_named() => {
3366                let candidate = normalize_cpp_whitespace(node_text(child, source));
3367                if !cpp_export_macro_token(&candidate) {
3368                    return false;
3369                }
3370                macro_count += 1;
3371            }
3372            ":" if !child.is_named() => colon_count += 1,
3373            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3374            _ => return false,
3375        }
3376    }
3377    macro_count == 1 && colon_count == 1 && access_count == 1
3378}
3379
3380fn recovered_single_base_after_declarator(
3381    node: Node<'_>,
3382    declarator: Node<'_>,
3383    source: &str,
3384) -> Option<String> {
3385    let body_start = node
3386        .child_by_field_name("body")
3387        .map(|body| body.start_byte())
3388        .unwrap_or(node.end_byte());
3389    let mut cursor = node.walk();
3390    let mut bases = node
3391        .named_children(&mut cursor)
3392        .filter(|child| {
3393            child.kind() == "ERROR"
3394                && child.start_byte() >= declarator.end_byte()
3395                && child.end_byte() <= body_start
3396        })
3397        .filter_map(|error| displaced_exported_class_name(error, source));
3398    let base = bases.next()?;
3399    bases.next().is_none().then_some(base)
3400}
3401
3402fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3403    (0..node.child_count()).any(|index| {
3404        node.child(index)
3405            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
3406    })
3407}
3408
3409pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3410    recover_exported_class_function_definition(node, source).is_some()
3411}
3412
3413fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3414    matches!(
3415        kind,
3416        "ERROR"
3417            | "preproc_if"
3418            | "preproc_ifdef"
3419            | "preproc_ifndef"
3420            | "preproc_else"
3421            | "preproc_elif"
3422    ) || (kind == "labeled_statement" && in_class_scope)
3423}
3424
3425pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3426    if node.kind() != "declaration" {
3427        return false;
3428    }
3429    let mut ancestor = node.parent();
3430    while let Some(container) = ancestor {
3431        match container.kind() {
3432            "compound_statement" => {
3433                return container.parent().is_some_and(|class_container| {
3434                    is_recovered_exported_class_container(class_container, source)
3435                });
3436            }
3437            // These containers preserve ScopeInfo in visit_node. declaration_list is
3438            // the body container selected for a linkage specification.
3439            "template_declaration" | "linkage_specification" | "declaration_list" => {}
3440            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3441            _ => return false,
3442        }
3443        ancestor = container.parent();
3444    }
3445    false
3446}
3447
3448pub fn recovered_exported_class_has_body(
3449    node: Node<'_>,
3450    source: &str,
3451    expected_name: &str,
3452) -> Option<bool> {
3453    match node.kind() {
3454        "function_definition" => {
3455            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
3456            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3457        }
3458        "declaration" | "field_declaration" => {
3459            let recovered = recover_exported_class_declaration(node, source)?;
3460            (recovered.name == expected_name).then(|| recovered.body.is_some())
3461        }
3462        _ => None,
3463    }
3464}
3465
3466fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3467    let body_start = node
3468        .child_by_field_name("body")
3469        .map(|body| body.start_byte())
3470        .unwrap_or(node.end_byte());
3471    let mut stack = Vec::new();
3472    for index in (0..node.named_child_count()).rev() {
3473        let Some(child) = node.named_child(index) else {
3474            continue;
3475        };
3476        if child.start_byte() >= body_start {
3477            continue;
3478        }
3479        stack.push(child);
3480    }
3481
3482    let mut best = None;
3483    while let Some(current) = stack.pop() {
3484        if matches!(current.kind(), "identifier" | "type_identifier") {
3485            let name = normalize_cpp_whitespace(node_text(current, source));
3486            if !name.is_empty()
3487                && !cpp_export_macro_token(&name)
3488                && !matches!(name.as_str(), "class" | "struct" | "union")
3489            {
3490                best = Some(name);
3491            }
3492            continue;
3493        }
3494
3495        for index in (0..current.named_child_count()).rev() {
3496            if let Some(child) = current.named_child(index)
3497                && child.start_byte() < body_start
3498            {
3499                stack.push(child);
3500            }
3501        }
3502    }
3503    best
3504}
3505
3506fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3507    if node.kind() == "declaration"
3508        && node
3509            .child_by_field_name("type")
3510            .or_else(|| first_class_like_child(node))
3511            .is_some_and(|type_node| {
3512                matches!(
3513                    type_node.kind(),
3514                    "class_specifier" | "struct_specifier" | "union_specifier"
3515                )
3516            })
3517        && let Some(name) = node
3518            .child_by_field_name("declarator")
3519            .and_then(|declarator| declarator_name_from_node(declarator, source))
3520        && !cpp_export_macro_token(&name)
3521    {
3522        return Some(name);
3523    }
3524
3525    if node.kind() == "function_definition"
3526        && node.child_by_field_name("type").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| direct_identifier_name(declarator, source))
3535        && !cpp_export_macro_token(&name)
3536    {
3537        return Some(name);
3538    }
3539
3540    let class_node = if matches!(
3541        node.kind(),
3542        "class_specifier" | "struct_specifier" | "union_specifier"
3543    ) {
3544        node
3545    } else {
3546        first_class_like_child(node)?
3547    };
3548    class_like_name_from_children(class_node, source)
3549}
3550
3551fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3552    if !matches!(
3553        node.kind(),
3554        "identifier" | "field_identifier" | "type_identifier"
3555    ) {
3556        return None;
3557    }
3558    let name = normalize_cpp_whitespace(node_text(node, source));
3559    (!name.is_empty()).then_some(name)
3560}
3561
3562fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3563    match node.kind() {
3564        "identifier" | "field_identifier" | "type_identifier" => {
3565            let name = normalize_cpp_whitespace(node_text(node, source));
3566            (!name.is_empty()).then_some(name)
3567        }
3568        _ => {
3569            let mut cursor = node.walk();
3570            node.named_children(&mut cursor)
3571                .find_map(|child| declarator_name_from_node(child, source))
3572        }
3573    }
3574}
3575
3576fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3577    let mut cursor = node.walk();
3578    node.named_children(&mut cursor).find(|child| {
3579        matches!(
3580            child.kind(),
3581            "class_specifier" | "struct_specifier" | "union_specifier"
3582        )
3583    })
3584}
3585
3586/// Push a container's children as a `Siblings` cursor rather than snapshotting
3587/// them all with one shared scope: children are visited one at a time so a
3588/// `using namespace X;` sibling can affect the scope threaded to the siblings
3589/// that textually follow it (issue #1093).
3590fn push_cpp_container_work<'tree>(
3591    node: Node<'tree>,
3592    scope: ScopeInfo,
3593    stack: &mut Vec<CppWork<'tree>>,
3594) {
3595    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3596}
3597
3598/// Materialize one selected named-child range with a tree-sitter cursor. The
3599/// cursor advances linearly across the parent's concrete children; repeatedly
3600/// asking for `named_child(index)` is quadratic on very wide generated nodes.
3601fn push_cpp_sibling_range<'tree>(
3602    parent: Node<'tree>,
3603    start_index: usize,
3604    end_index: usize,
3605    scope: ScopeInfo,
3606    stack: &mut Vec<CppWork<'tree>>,
3607) {
3608    let mut cursor = parent.walk();
3609    let children = parent
3610        .named_children(&mut cursor)
3611        .skip(start_index)
3612        .take(end_index.saturating_sub(start_index))
3613        .collect::<Vec<_>>()
3614        .into_iter();
3615    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3616}
3617
3618/// Advance a `Siblings` cursor by one child: dispatch the current child under
3619/// the scope accumulated from its *earlier* siblings, then push a
3620/// continuation for the remaining siblings carrying the scope updated for
3621/// *this* child (only `using namespace X;` directives change it). Pushing the
3622/// continuation before the current child's own node work means the current
3623/// child's subtree fully drains (LIFO) before the next sibling is visited,
3624/// preserving left-to-right order.
3625fn advance_cpp_siblings<'tree>(
3626    mut siblings: CppSiblingsWork<'tree>,
3627    source: &str,
3628    stack: &mut Vec<CppWork<'tree>>,
3629) {
3630    let Some(child) = siblings.children.next() else {
3631        return;
3632    };
3633    let current_scope = siblings.scope.clone();
3634    if let Some(namespace) = cpp_using_namespace_target(child, source) {
3635        siblings.scope.visible_using_namespaces.push(namespace);
3636    }
3637    if !siblings.children.as_slice().is_empty() {
3638        stack.push(CppWork::Siblings(siblings));
3639    }
3640    stack.push(CppWork::Node(CppNodeWork {
3641        node: child,
3642        scope: current_scope,
3643    }));
3644}
3645
3646/// The namespace target of a `using namespace X;` directive, or `None` for
3647/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
3648/// kind. Distinguished structurally by the presence of the grammar's literal
3649/// `namespace` keyword token among the node's children -- not by inspecting
3650/// source text -- so it never misreads a member-importing using-declaration
3651/// as a namespace directive.
3652fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3653    if node.kind() != "using_declaration" {
3654        return None;
3655    }
3656    let mut cursor = node.walk();
3657    let is_namespace_directive = node
3658        .children(&mut cursor)
3659        .any(|child| child.kind() == "namespace");
3660    if !is_namespace_directive {
3661        return None;
3662    }
3663    let target = node.named_child(0)?;
3664    // A leading `::` is the explicit-global marker, not part of the namespace
3665    // path (`using namespace ::std::chrono;`). Drop that AST token before
3666    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
3667    // keeps: storing the marker verbatim desynced the legacy package string from
3668    // the FqName bridge, which splits on `::` and drops the empty leading
3669    // component, tripping the package/short boundary assert when a bare-owner
3670    // out-of-line definition borrowed the directive's namespace (#1093 path).
3671    let start = target
3672        .child(0)
3673        .filter(|child| !child.is_named() && child.kind() == "::")
3674        .map_or(target.start_byte(), |marker| marker.end_byte());
3675    let text = normalize_cpp_whitespace(
3676        source
3677            .get(start..target.end_byte())
3678            .expect("using-directive target covers one source range"),
3679    );
3680    (!text.is_empty()).then_some(text)
3681}
3682
3683/// Every `using namespace X;` directive target in a file, in source order, for
3684/// resolution-time consumers that need the file's using-directives without the
3685/// per-position scope threading extraction does. Parses `source` fresh and
3686/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
3687/// keys on the grammar's `namespace` keyword token, not source text), so it
3688/// never misreads a member-importing `using X::Y;` as a namespace directive.
3689///
3690/// This is a whole-file over-approximation of what is in scope at any one point
3691/// (a directive nested inside a `namespace {}` block or a function body is still
3692/// reported), which is exactly what the #1134 identity reconciler wants: extra
3693/// candidate namespaces that no visible class confirms are harmless, and two
3694/// that both confirm are treated as a genuine ambiguity by the reconciler.
3695pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3696    let mut parser = Parser::new();
3697    if parser
3698        .set_language(&tree_sitter_cpp::LANGUAGE.into())
3699        .is_err()
3700    {
3701        return Vec::new();
3702    }
3703    let Some(tree) = parser.parse(source, None) else {
3704        return Vec::new();
3705    };
3706    let mut namespaces = Vec::new();
3707    let mut seen = std::collections::HashSet::new();
3708    let mut stack = vec![tree.root_node()];
3709    while let Some(node) = stack.pop() {
3710        if let Some(namespace) = cpp_using_namespace_target(node, source)
3711            && seen.insert(namespace.clone())
3712        {
3713            namespaces.push(namespace);
3714        }
3715        let mut cursor = node.walk();
3716        stack.extend(node.named_children(&mut cursor));
3717    }
3718    namespaces
3719}
3720
3721pub struct CppVisitor<'a> {
3722    pub file: &'a ProjectFile,
3723    pub source: &'a str,
3724    pub parsed: &'a mut ParsedFile,
3725    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
3726    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
3727    ///
3728    /// C has no nested tag scope: a struct/union/enum tag declared inside
3729    /// another aggregate's member list has the scope of the outer declaration
3730    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
3731    /// therefore declares a file-scope `inner` that a later file-scope
3732    /// `struct inner *p;` legitimately references, where C++ would make the
3733    /// same shape a nested class `outer::inner`. Headers carry no compilation
3734    /// language of their own and keep the conservative C++ interpretation.
3735    pub c_tag_semantics: bool,
3736    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3737    /// Byte regions whose contents were re-owned by a fragmented export-class
3738    /// recovery (#938): the scattered members between the fragmented
3739    /// declaration and its displaced closing brace are indexed as members of
3740    /// the recovered class by the region reparse, so the ordinary sibling walk
3741    /// must not ALSO index them as top-level declarations (that double-indexing
3742    /// made a scattered nested class ambiguous between `Inner` and
3743    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
3744    /// linear scan at visit time is fine.
3745    pub consumed_fragment_regions: Vec<(usize, usize)>,
3746    /// Lexical namespace corrections and brace boundaries for declarations
3747    /// displaced by parse recovery (issues #1537 and #3087). The parsed
3748    /// ancestors can stop short or extend past their true closes; see
3749    /// [`CppVisitor::recovered_namespace_scope`].
3750    pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
3751    /// Owned reparses waiting for the outer work loop. Partitioning another
3752    /// swallowed class in a tail must not grow the Rust call stack.
3753    pub partitioned_regions: Vec<(Tree, std::ops::Range<usize>, ScopeInfo)>,
3754    /// The namespace forward declarations already folded out of each tree this
3755    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
3756    /// Empty until the first question, which the overwhelming majority of files
3757    /// never ask.
3758    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3759    /// Which owners already have field declarations in the parse product, as
3760    /// [`CppVisitor::has_enum_enumerator_units`] needs to know. `None` until
3761    /// the first enum asks, which most files never do (#2786).
3762    pub field_owners: Option<CppFieldOwnerIndex>,
3763    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
3764    /// happen to the declaration set, innermost last. Empty outside a recovery
3765    /// reparse, which is almost always (#2787).
3766    pub recovery_captures: Vec<CppRecoveryCapture>,
3767    /// Object-like field-list macros defined earlier in this source. Their
3768    /// replacements are parsed structurally and materialized under each
3769    /// invoking aggregate; no source-text expansion is used.
3770    pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
3771    /// Names whose active replacement is not a unique structured field list.
3772    /// A later `#undef` resets the ambiguity; another `#define` does not.
3773    pub ambiguous_object_macro_fields: HashSet<String>,
3774}
3775
3776/// One source-order event from an object-like field-list macro environment.
3777///
3778/// The event stream lets an include-closure consumer preserve preprocessor
3779/// invalidation without guessing which parsed header happened to be visited
3780/// first. A conservative consumer may permanently block a name after an
3781/// `undef` when conditional include order is not provable.
3782#[derive(Clone, Debug, PartialEq, Eq)]
3783pub enum ObjectMacroFieldEvent {
3784    Define {
3785        name: String,
3786        replacement: ObjectMacroReplacement,
3787        conditional: bool,
3788    },
3789    Undef {
3790        name: String,
3791        conditional: bool,
3792    },
3793}
3794
3795/// Collect object-like field-list macros from a parsed source in source order.
3796/// Macros with conflicting active replacements are omitted so an include
3797/// closure cannot silently choose one guarded definition over another.
3798pub fn collect_cpp_object_macro_fields<'tree>(
3799    root: Node<'tree>,
3800    source: &str,
3801) -> HashMap<String, ObjectMacroReplacement> {
3802    let mut fields = HashMap::default();
3803    let mut ambiguous = HashSet::default();
3804    for event in collect_cpp_object_macro_field_events(root, source) {
3805        match event {
3806            ObjectMacroFieldEvent::Define {
3807                name,
3808                replacement: value,
3809                conditional,
3810            } => {
3811                if value.is_empty() {
3812                    fields.remove(&name);
3813                    if conditional {
3814                        ambiguous.insert(name);
3815                    } else {
3816                        ambiguous.remove(&name);
3817                    }
3818                } else if ambiguous.contains(&name) {
3819                    // Keep the name blocked until an explicit undef resets it.
3820                } else if let Some(previous) = fields.get(&name) {
3821                    if previous != &value {
3822                        fields.remove(&name);
3823                        ambiguous.insert(name);
3824                    }
3825                } else {
3826                    fields.insert(name, value);
3827                }
3828            }
3829            ObjectMacroFieldEvent::Undef { name, conditional } => {
3830                fields.remove(&name);
3831                if conditional {
3832                    ambiguous.insert(name);
3833                } else {
3834                    ambiguous.remove(&name);
3835                }
3836            }
3837        }
3838    }
3839    fields
3840}
3841
3842/// Collect object-like field-list macro events in source order. Conditional
3843/// branches remain visible in the event stream: callers that cannot prove
3844/// branch selection can invalidate names conservatively instead of inventing
3845/// a replacement from one branch.
3846pub fn collect_cpp_object_macro_field_events<'tree>(
3847    root: Node<'tree>,
3848    source: &str,
3849) -> Vec<ObjectMacroFieldEvent> {
3850    let mut events = Vec::new();
3851    let mut stack = vec![root];
3852    while let Some(node) = stack.pop() {
3853        if node.kind() == "preproc_def"
3854            && let Some(name) = extract_macro_name(node, source)
3855        {
3856            let replacement = object_macro_replacement_of(node, source);
3857            events.push(ObjectMacroFieldEvent::Define {
3858                name,
3859                replacement,
3860                conditional: inside_preprocessor_conditional(node),
3861            });
3862        } else if is_cpp_undef_directive(node, source)
3863            && let Some(argument) = node.child_by_field_name("argument")
3864        {
3865            events.push(ObjectMacroFieldEvent::Undef {
3866                name: node_text(argument, source).trim().to_string(),
3867                conditional: inside_preprocessor_conditional(node),
3868            });
3869        }
3870        let mut cursor = node.walk();
3871        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3872        stack.extend(children.into_iter().rev());
3873    }
3874    events
3875}
3876
3877fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
3878    let mut current = node.parent();
3879    while let Some(parent) = current {
3880        if matches!(
3881            parent.kind(),
3882            "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
3883        ) {
3884            return true;
3885        }
3886        current = parent.parent();
3887    }
3888    false
3889}
3890
3891fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
3892    node.kind() == "preproc_call"
3893        && node
3894            .child_by_field_name("directive")
3895            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
3896}
3897
3898impl<'a> CppVisitor<'a> {
3899    /// Records `code_unit` with the answers the walk carries forward, then adds
3900    /// it to the parse product.
3901    ///
3902    /// Every declaration this walk publishes goes through this family, so the
3903    /// carried-forward answers see each one exactly once: the field ownership
3904    /// index behind [`Self::has_enum_enumerator_units`] (#2786) and the minted
3905    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
3906    fn add_declaration(
3907        &mut self,
3908        code_unit: CodeUnit,
3909        node: Node<'_>,
3910        parent: Option<CodeUnit>,
3911        top_level: Option<CodeUnit>,
3912    ) {
3913        self.note_declaration(&code_unit);
3914        let source = self.source;
3915        self.parsed
3916            .add_code_unit(code_unit, node, source, parent, top_level);
3917    }
3918
3919    /// Range-based form of [`Self::add_declaration`].
3920    fn add_declaration_with_range(
3921        &mut self,
3922        code_unit: CodeUnit,
3923        range: Range,
3924        parent: Option<CodeUnit>,
3925        top_level: Option<CodeUnit>,
3926    ) {
3927        self.note_declaration(&code_unit);
3928        self.parsed
3929            .add_code_unit_with_range(code_unit, range, parent, top_level);
3930    }
3931
3932    /// Deferred-replacement form of [`Self::add_declaration`].
3933    fn replace_declaration_deferred(
3934        &mut self,
3935        code_unit: CodeUnit,
3936        node: Node<'_>,
3937        parent: Option<CodeUnit>,
3938        top_level: Option<CodeUnit>,
3939    ) {
3940        self.note_replaced_declaration(&code_unit);
3941        let source = self.source;
3942        self.parsed
3943            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
3944    }
3945
3946    /// Range-based form of [`Self::replace_declaration_deferred`].
3947    fn replace_declaration_with_range_deferred(
3948        &mut self,
3949        code_unit: CodeUnit,
3950        range: Range,
3951        parent: Option<CodeUnit>,
3952        top_level: Option<CodeUnit>,
3953    ) {
3954        self.note_replaced_declaration(&code_unit);
3955        self.parsed
3956            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
3957    }
3958
3959    /// Notes one declaration about to enter the parse product.
3960    ///
3961    /// A declaration the product already holds is not a creation, so an open
3962    /// recovery capture ignores it -- which is the membership test the set
3963    /// difference it replaces performed. A creation inside a nested recovery
3964    /// belongs to the recoveries around it too, so every open capture takes it.
3965    fn note_declaration(&mut self, code_unit: &CodeUnit) {
3966        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
3967            for capture in &mut self.recovery_captures {
3968                if capture.removed_pre_existing.contains(code_unit) {
3969                    continue;
3970                }
3971                if capture.created_units.insert(code_unit.clone()) {
3972                    capture.created.push(code_unit.clone());
3973                }
3974            }
3975        }
3976        if let Some(field_owners) = self.field_owners.as_mut() {
3977            field_owners.record(code_unit, self.file);
3978        }
3979    }
3980
3981    /// Notes one declaration about to replace an existing one.
3982    ///
3983    /// A deferred replacement of a declaration that already owns children
3984    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
3985    /// a removal is the one thing the field index cannot absorb by addition.
3986    /// Drop it; the next question rebuilds it from the declarations that
3987    /// survive. A replacement of a unit with no children, and a "replacement"
3988    /// of a unit that is not there at all, remove nothing.
3989    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
3990        let removes_children = self.parsed.contains_declaration(code_unit)
3991            && self
3992                .parsed
3993                .children
3994                .get(code_unit)
3995                .is_some_and(|children| !children.is_empty());
3996        if removes_children {
3997            if !self.recovery_captures.is_empty() {
3998                let removed = self.declarations_a_replacement_removes(code_unit);
3999                for capture in &mut self.recovery_captures {
4000                    for unit in &removed {
4001                        // A declaration this capture watched being created is
4002                        // its own; one it did not is a declaration that was
4003                        // already there when the capture opened, so creating it
4004                        // again is a restoration and not a mint.
4005                        if !capture.created_units.contains(unit) {
4006                            capture.removed_pre_existing.insert(unit.clone());
4007                        }
4008                    }
4009                }
4010            }
4011            self.field_owners = None;
4012        }
4013        self.note_declaration(code_unit);
4014    }
4015
4016    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
4017    /// when `code_unit` is replaced: its children, transitively.
4018    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
4019        let mut removed = Vec::new();
4020        let mut seen = HashSet::default();
4021        let mut pending: Vec<CodeUnit> = self
4022            .parsed
4023            .children
4024            .get(code_unit)
4025            .cloned()
4026            .unwrap_or_default();
4027        while let Some(unit) = pending.pop() {
4028            if !seen.insert(unit.clone()) {
4029                continue;
4030            }
4031            if let Some(children) = self.parsed.children.get(&unit) {
4032                pending.extend(children.iter().cloned());
4033            }
4034            removed.push(unit);
4035        }
4036        removed
4037    }
4038
4039    fn visit_function_like_export_class_pair<'tree>(
4040        &mut self,
4041        node: Node<'tree>,
4042        scope: &ScopeInfo,
4043        stack: &mut Vec<CppWork<'tree>>,
4044        ancestry: &ParentIndex<'tree>,
4045    ) -> bool {
4046        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
4047            return false;
4048        };
4049        let member_outcome = self
4050            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
4051        // A malformed class body can escape into several following siblings
4052        // before the next export-macro class head appears. Inspect siblings in
4053        // order and stop at the first envelope that contains such a head. One
4054        // envelope can contain several following classes, all recovered in a
4055        // single bounded traversal.
4056        let mut displaced = node.next_named_sibling();
4057        while let Some(candidate) = displaced {
4058            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
4059                break;
4060            }
4061            displaced = candidate.next_named_sibling();
4062        }
4063        let class_unit = self.visit_named_class_like_shape(
4064            node,
4065            recovered.name,
4066            // The adjacent initializer_list proves the class body envelope,
4067            // but its children are expression-shaped rather than declaration-
4068            // preserving. Index the class identity here; callable definitions
4069            // remain available from their ordinary out-of-line declarations.
4070            None,
4071            true,
4072            Some(recovered.range),
4073            recovered.raw_supertypes,
4074            scope,
4075            stack,
4076            ancestry,
4077        );
4078        self.parsed
4079            .record_materialization(MaterializationRecord::RecoveredDeclaration {
4080                recovery: recovered.range,
4081                unit: class_unit.clone(),
4082            });
4083        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4084            && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4085                tree.root_node(),
4086                class_unit.identifier(),
4087                self.source,
4088            )
4089        {
4090            self.visit_recovered_fragment_constructor(
4091                range,
4092                body,
4093                node,
4094                &class_unit,
4095                scope,
4096                ancestry,
4097            );
4098        }
4099        if let Some(outcome) = member_outcome {
4100            self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4101        }
4102        self.consumed_fragment_regions
4103            .push((node.start_byte(), recovered.range.end_byte));
4104        true
4105    }
4106
4107    fn visit_embedded_function_like_export_classes<'tree>(
4108        &mut self,
4109        node: Node<'tree>,
4110        scope: &ScopeInfo,
4111        stack: &mut Vec<CppWork<'tree>>,
4112        ancestry: &ParentIndex<'tree>,
4113    ) -> bool {
4114        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
4115        let found = !recovered_classes.is_empty();
4116        for recovered in recovered_classes {
4117            let member_outcome = self.reparse_fragmented_export_class_members(
4118                &recovered.fragmented_body,
4119                &recovered.name,
4120            );
4121            let class_unit = self.visit_named_class_like_shape(
4122                node,
4123                recovered.name,
4124                None,
4125                true,
4126                Some(recovered.range),
4127                Some(recovered.raw_supertypes),
4128                scope,
4129                stack,
4130                ancestry,
4131            );
4132            self.parsed
4133                .record_materialization(MaterializationRecord::RecoveredDeclaration {
4134                    recovery: recovered.range,
4135                    unit: class_unit.clone(),
4136                });
4137            if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4138                && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4139                    tree.root_node(),
4140                    class_unit.identifier(),
4141                    self.source,
4142                )
4143            {
4144                self.visit_recovered_fragment_constructor(
4145                    range,
4146                    body,
4147                    node,
4148                    &class_unit,
4149                    scope,
4150                    ancestry,
4151                );
4152            }
4153            if let Some(outcome) = member_outcome {
4154                self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4155            }
4156        }
4157        found
4158    }
4159
4160    /// Walk `node`'s container, answering every ancestor question from
4161    /// `ancestry`.
4162    ///
4163    /// `ancestry` must index the tree `node` belongs to. The caller owns it
4164    /// because one tree can be walked more than once -- a header's C and C++
4165    /// readings are the same tree under different tag semantics -- and the
4166    /// parent relation is a property of the tree, not of the reading.
4167    #[allow(clippy::too_many_arguments)]
4168    pub fn visit_container<'tree>(
4169        &mut self,
4170        node: Node<'tree>,
4171        ancestry: &ParentIndex<'tree>,
4172        package_name: &str,
4173        module: Option<CodeUnit>,
4174        class_unit: Option<CodeUnit>,
4175        template_signature: Option<String>,
4176        visible_using_namespaces: Vec<String>,
4177    ) {
4178        let scope = ScopeInfo {
4179            package_name: package_name.to_string(),
4180            module,
4181            class_unit,
4182            template_signature,
4183            template_metadata: None,
4184            declarations_are_fields: false,
4185            recovered_specialization_member_scope: false,
4186            visible_using_namespaces,
4187        };
4188        // The work loop dispatches a container's children, never the container
4189        // itself, so an `ERROR` container that is the seed does not reach the
4190        // ERROR arm of `visit_node`. A header whose first collapsed aggregate
4191        // breaks the parse leaves the whole translation unit as that seed
4192        // (libuv's `include/uv.h`, issue #2985), which is exactly the shape the
4193        // macro-error-class recovery reads.
4194        if node.is_error() {
4195            self.visit_object_macro_error_classes(node, &scope);
4196        }
4197        self.run_container_work(node, scope, ancestry);
4198        while let Some((tree, range, scope)) = self.partitioned_regions.pop() {
4199            let root = tree.root_node();
4200            let container = root
4201                .descendant_for_byte_range(range.start, range.end)
4202                .expect("the queued container belongs to this tree");
4203            assert_eq!(container.byte_range(), range);
4204            self.run_container_work(container, scope, &ParentIndex::new(root));
4205        }
4206    }
4207
4208    /// Whether a work node lies entirely inside a byte region consumed by a
4209    /// fragmented export-class recovery (#938); such nodes were already indexed
4210    /// as members of the recovered class by the region reparse.
4211    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
4212        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
4213    }
4214
4215    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
4216    /// candidate a recovery is about to reparse but has not yet turned into a
4217    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
4218    /// prior structured recovery.
4219    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
4220        self.consumed_fragment_regions
4221            .iter()
4222            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
4223    }
4224
4225    /// Drive the container work loop from an explicit seed scope to completion. The
4226    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
4227    /// stays alive for the whole traversal.
4228    ///
4229    /// Every ancestor question this walk asks is answered from `ancestry`, which
4230    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
4231    /// node's position in the tree, which made a generated header with thousands
4232    /// of top-level declarations quadratic (#2361). The index is the caller's
4233    /// because it outlives any one walk: the file's tree is walked twice when a
4234    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
4235    /// builds its own index for its own tree.
4236    fn run_container_work<'tree>(
4237        &mut self,
4238        node: Node<'tree>,
4239        scope: ScopeInfo,
4240        ancestry: &ParentIndex<'tree>,
4241    ) {
4242        self.drain_cpp_work(
4243            vec![CppWork::Container(CppContainer { node, scope })],
4244            ancestry,
4245        );
4246    }
4247
4248    /// The work loop itself, from whatever seed the caller built.
4249    ///
4250    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
4251    /// that must walk only part of a reparsed tree seeds it with the sibling
4252    /// range it may walk instead, which keeps the `using namespace X;` scope
4253    /// accumulation `advance_cpp_siblings` performs.
4254    fn drain_cpp_work<'tree>(
4255        &mut self,
4256        mut stack: Vec<CppWork<'tree>>,
4257        ancestry: &ParentIndex<'tree>,
4258    ) {
4259        while let Some(work) = stack.pop() {
4260            match work {
4261                CppWork::Container(container) => {
4262                    push_cpp_container_work(container.node, container.scope, &mut stack);
4263                }
4264                CppWork::Siblings(siblings) => {
4265                    advance_cpp_siblings(siblings, self.source, &mut stack);
4266                }
4267                CppWork::Node(work) => {
4268                    if self.node_is_inside_consumed_fragment(work.node) {
4269                        continue;
4270                    }
4271                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
4272                }
4273            }
4274        }
4275    }
4276
4277    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
4278    /// it only when the entire region is member-shaped. This validation must happen
4279    /// before registering the recovered class because a rejected speculative range
4280    /// must not leak into the ordinary recovery path.
4281    fn reparse_fragmented_export_class_members(
4282        &self,
4283        fragmented: &FragmentedExportBody,
4284        class_name: &str,
4285    ) -> Option<FragmentedExportMembers> {
4286        if fragmented.reparse_start >= fragmented.reparse_end {
4287            return None;
4288        }
4289        let tree = cpp_reparse_fragmented_class_body(
4290            self.source,
4291            fragmented.reparse_start,
4292            fragmented.reparse_end,
4293        )?;
4294        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
4295            return Some(FragmentedExportMembers::Complete(tree));
4296        }
4297        let has_conditional_constructor = {
4298            let root = tree.root_node();
4299            let mut cursor = root.walk();
4300            root.named_children(&mut cursor).any(|child| {
4301                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
4302            })
4303        };
4304        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
4305    }
4306
4307    /// Index an already validated fragmented body as members of `class_unit`. The
4308    /// region reparse keeps each member's exact original byte and line positions.
4309    fn visit_fragmented_export_class_members(
4310        &mut self,
4311        outcome: FragmentedExportMembers,
4312        class_unit: CodeUnit,
4313        scope: &ScopeInfo,
4314    ) -> bool {
4315        let (tree, complete) = match outcome {
4316            FragmentedExportMembers::Complete(tree) => (tree, true),
4317            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
4318        };
4319        let root = tree.root_node();
4320        let class_name = class_unit.identifier().to_string();
4321        let member_scope = ScopeInfo {
4322            // A recovered export-macro class may borrow its namespace from an
4323            // earlier forward declaration even when the malformed node itself
4324            // sits at file scope. Use the recovered class identity as the
4325            // authoritative package for reparsed members as well.
4326            package_name: class_unit.package_name().to_string(),
4327            module: scope.module.clone(),
4328            class_unit: Some(class_unit),
4329            template_signature: scope.template_signature.clone(),
4330            template_metadata: None,
4331            declarations_are_fields: true,
4332            recovered_specialization_member_scope: false,
4333            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4334        };
4335        if !complete {
4336            // A conditional beginning immediately after an access label can
4337            // fragment one constructor declaration while leaving the rest of
4338            // the class body as unsafe statement soup. Recover only that
4339            // structurally proven constructor and leave the outer-tree
4340            // siblings unconsumed for their ordinary walk.
4341            let mut cursor = root.walk();
4342            let constructors = root
4343                .named_children(&mut cursor)
4344                .filter_map(|child| {
4345                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
4346                })
4347                .collect::<Vec<_>>();
4348            // The reparsed region is its own tree, so this drain walks it with
4349            // its own parent index.
4350            let reparsed_ancestry = ParentIndex::new(root);
4351            for constructor in constructors {
4352                let mut stack = Vec::new();
4353                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
4354                while let Some(work) = stack.pop() {
4355                    match work {
4356                        CppWork::Container(container) => {
4357                            push_cpp_container_work(container.node, container.scope, &mut stack);
4358                        }
4359                        CppWork::Siblings(siblings) => {
4360                            advance_cpp_siblings(siblings, self.source, &mut stack);
4361                        }
4362                        CppWork::Node(work) => {
4363                            self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
4364                        }
4365                    }
4366                }
4367            }
4368            return false;
4369        }
4370        // The reparsed region is its own tree, so this walk indexes it itself.
4371        self.run_container_work(root, member_scope, &ParentIndex::new(root));
4372        true
4373    }
4374
4375    fn visit_recovered_fragment_constructor<'tree>(
4376        &mut self,
4377        range: std::ops::Range<usize>,
4378        constructor_body: Node<'tree>,
4379        class_declaration: Node<'tree>,
4380        class_unit: &CodeUnit,
4381        scope: &ScopeInfo,
4382        ancestry: &ParentIndex<'tree>,
4383    ) {
4384        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
4385            return;
4386        };
4387        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
4388            tree.root_node(),
4389            range.start,
4390            class_unit.identifier(),
4391            self.source,
4392        ) else {
4393            return;
4394        };
4395        let member_scope = ScopeInfo {
4396            package_name: class_unit.package_name().to_string(),
4397            module: scope.module.clone(),
4398            class_unit: Some(class_unit.clone()),
4399            template_signature: scope.template_signature.clone(),
4400            template_metadata: None,
4401            declarations_are_fields: true,
4402            recovered_specialization_member_scope: false,
4403            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4404        };
4405        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
4406        else {
4407            return;
4408        };
4409        debug_assert_eq!(function.name, class_unit.identifier());
4410        let code_unit = function.code_unit(self.file.clone());
4411        self.add_declaration_with_range(
4412            code_unit.clone(),
4413            Range {
4414                start_byte: function_declarator.start_byte(),
4415                end_byte: constructor_body.end_byte(),
4416                start_line: function_declarator.start_position().row + 1,
4417                end_line: constructor_body.end_position().row + 1,
4418            },
4419            None,
4420            None,
4421        );
4422        self.parsed.add_signature_with_metadata(
4423            code_unit.clone(),
4424            cpp_signature_metadata(
4425                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
4426                function_declarator,
4427                self.source,
4428                ancestry,
4429            )
4430            .with_declaration_only(false)
4431            .with_callable_linkage(cpp_callable_linkage(
4432                class_declaration,
4433                self.source,
4434                ancestry,
4435            )),
4436        );
4437        self.parsed.add_child(class_unit.clone(), code_unit);
4438    }
4439
4440    fn visit_recovered_fragment_prefix_members<'tree>(
4441        &mut self,
4442        root: Node<'tree>,
4443        constructor_start: usize,
4444        class_unit: &CodeUnit,
4445        scope: &ScopeInfo,
4446        ancestry: &ParentIndex<'tree>,
4447    ) {
4448        let member_scope = ScopeInfo {
4449            package_name: class_unit.package_name().to_string(),
4450            module: scope.module.clone(),
4451            class_unit: Some(class_unit.clone()),
4452            template_signature: scope.template_signature.clone(),
4453            template_metadata: None,
4454            declarations_are_fields: true,
4455            recovered_specialization_member_scope: false,
4456            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4457        };
4458        let mut stack = vec![root];
4459        while let Some(current) = stack.pop() {
4460            if current.kind() == "comment" || current.start_byte() >= constructor_start {
4461                continue;
4462            }
4463            if current.end_byte() <= constructor_start
4464                && current.kind() != "translation_unit"
4465                && current.kind() != "labeled_statement"
4466                && current.kind() != "ERROR"
4467            {
4468                let mut work_stack = Vec::new();
4469                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
4470                while let Some(work) = work_stack.pop() {
4471                    match work {
4472                        CppWork::Container(container) => {
4473                            push_cpp_container_work(
4474                                container.node,
4475                                container.scope,
4476                                &mut work_stack,
4477                            );
4478                        }
4479                        CppWork::Siblings(siblings) => {
4480                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
4481                        }
4482                        CppWork::Node(work) => {
4483                            self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
4484                        }
4485                    }
4486                }
4487                continue;
4488            }
4489            if matches!(
4490                current.kind(),
4491                "translation_unit" | "labeled_statement" | "ERROR"
4492            ) {
4493                let mut cursor = current.walk();
4494                stack.extend(current.named_children(&mut cursor));
4495            }
4496        }
4497    }
4498
4499    fn visit_node<'tree>(
4500        &mut self,
4501        node: Node<'tree>,
4502        scope: &ScopeInfo,
4503        stack: &mut Vec<CppWork<'tree>>,
4504        ancestry: &ParentIndex<'tree>,
4505    ) {
4506        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
4507            self.visit_node(node, &recovered_scope, stack, ancestry);
4508            return;
4509        }
4510        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
4511            self.visit_node(node, &recovered_scope, stack, ancestry);
4512            return;
4513        }
4514        // Fragmented-class recovery below may consume a malformed function
4515        // envelope before the ordinary kind dispatch runs. Recover any
4516        // export-macro class embedded in that envelope first; the strict class
4517        // head/base/body predicate is independent of which later recovery owns
4518        // the surrounding parser fragment.
4519        if node.kind() == "function_definition" && node.has_error() {
4520            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4521        }
4522        if let Some(FragmentedClassRecovery {
4523            declaration_node: class_node,
4524            name,
4525            raw_supertypes,
4526            body: fragmented,
4527        }) = fragmented_class_body(node, self.source)
4528        {
4529            let displaced_namespace_items =
4530                displaced_fragment_namespace_geometry(node, self.source)
4531                    .map(|boundary| boundary.namespace_items)
4532                    .unwrap_or_default();
4533            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
4534            let mut class_stack = Vec::new();
4535            // When the full body cannot be safely reparsed, the original class
4536            // node still proves ownership for its parser-visible prefix.
4537            let parser_visible_body =
4538                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
4539                    .then(|| cpp_body_node(class_node))
4540                    .flatten();
4541            let class_unit = self.visit_named_class_like_shape(
4542                class_node,
4543                name,
4544                parser_visible_body,
4545                true,
4546                Some(fragmented.class_range),
4547                Some(raw_supertypes),
4548                scope,
4549                &mut class_stack,
4550                ancestry,
4551            );
4552            let member_scope = ScopeInfo {
4553                package_name: class_unit.package_name().to_string(),
4554                module: scope.module.clone(),
4555                class_unit: Some(class_unit.clone()),
4556                template_signature: scope.template_signature.clone(),
4557                template_metadata: None,
4558                declarations_are_fields: true,
4559                recovered_specialization_member_scope: false,
4560                visible_using_namespaces: scope.visible_using_namespaces.clone(),
4561            };
4562            let complete = outcome.is_some_and(|outcome| {
4563                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
4564            });
4565            if complete {
4566                self.consumed_fragment_regions
4567                    .push((node.start_byte(), fragmented.class_range.end_byte));
4568            } else {
4569                // A macro-constrained member can make the full body reparse
4570                // unsafe while tree-sitter still exposes later class members
4571                // as bounded siblings up to the displaced `}`/`;`. Keep the
4572                // structurally proven class/base declaration and re-own those
4573                // sibling nodes under it. They retain their original parser
4574                // nodes and exact ranges; the close boundary comes solely from
4575                // `fragmented_class_body`.
4576                // Template wrappers put the escaped members beside the
4577                // template rather than beside its malformed declaration.
4578                for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
4579                    if candidate.start_byte() >= fragmented.reparse_end {
4580                        break;
4581                    }
4582                    if cpp_fragment_sibling_is_class_member(
4583                        candidate,
4584                        fragmented.reparse_end,
4585                        self.source,
4586                    ) {
4587                        self.recovered_class_sibling_scopes
4588                            .insert(candidate.id(), member_scope.clone());
4589                    }
4590                }
4591            }
4592            for item in displaced_namespace_items {
4593                self.recovered_class_sibling_scopes
4594                    .insert(item.id(), scope.clone());
4595            }
4596            stack.extend(class_stack);
4597            return;
4598        }
4599        if self.visit_folded_aggregate(node, scope) {
4600            return;
4601        }
4602        match node.kind() {
4603            "template_declaration" => {
4604                if let Some(recovered) =
4605                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
4606                {
4607                    let mut template_scope = scope.clone();
4608                    template_scope.template_signature =
4609                        cpp_template_signature(node, recovered.declaration_node, self.source);
4610                    template_scope.template_metadata =
4611                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
4612                    let raw_supertypes =
4613                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
4614                    let mut class_stack = Vec::new();
4615                    let class_unit = self.visit_named_class_like_shape(
4616                        recovered.class_node,
4617                        recovered.name,
4618                        Some(recovered.body),
4619                        true,
4620                        Some(recovered.range),
4621                        raw_supertypes,
4622                        &template_scope,
4623                        &mut class_stack,
4624                        ancestry,
4625                    );
4626                    self.parsed.record_materialization(
4627                        MaterializationRecord::RecoveredDeclaration {
4628                            recovery: recovered.range,
4629                            unit: class_unit.clone(),
4630                        },
4631                    );
4632                    let member_scope = ScopeInfo {
4633                        package_name: template_scope.package_name.clone(),
4634                        module: template_scope.module.clone(),
4635                        class_unit: Some(class_unit.clone()),
4636                        template_signature: template_scope.template_signature.clone(),
4637                        template_metadata: None,
4638                        declarations_are_fields: true,
4639                        recovered_specialization_member_scope: recovered
4640                            .class_node
4641                            .child_by_field_name("name")
4642                            .is_some_and(|name| name.kind() == "template_type"),
4643                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
4644                    };
4645                    for tail_member in recovered.tail_members.into_iter().rev() {
4646                        stack.push(CppWork::Node(CppNodeWork {
4647                            node: tail_member,
4648                            scope: member_scope.clone(),
4649                        }));
4650                    }
4651                    stack.extend(class_stack);
4652                    for sibling in recovered.member_siblings {
4653                        self.recovered_class_sibling_scopes
4654                            .insert(sibling.id(), member_scope.clone());
4655                    }
4656                    return;
4657                }
4658                for index in (0..node.named_child_count()).rev() {
4659                    let Some(child) = node.named_child(index) else {
4660                        continue;
4661                    };
4662                    if matches!(
4663                        child.kind(),
4664                        "class_specifier"
4665                            | "struct_specifier"
4666                            | "union_specifier"
4667                            | "enum_specifier"
4668                            | "function_definition"
4669                            | "declaration"
4670                            | "field_declaration"
4671                            | "alias_declaration"
4672                            | "namespace_definition"
4673                    ) {
4674                        let mut template_scope = scope.clone();
4675                        template_scope.template_signature =
4676                            cpp_template_signature(node, child, self.source);
4677                        template_scope.template_metadata =
4678                            cpp_template_metadata(node, child, self.source, ancestry);
4679                        if let Some(recovered) = recover_fragmented_partial_specialization(
4680                            node,
4681                            child,
4682                            self.source,
4683                            ancestry,
4684                        ) {
4685                            let code_unit = self.visit_named_class_like_shape(
4686                                recovered.declaration_node,
4687                                recovered.name,
4688                                None,
4689                                true,
4690                                Some(recovered.range),
4691                                None,
4692                                &template_scope,
4693                                stack,
4694                                ancestry,
4695                            );
4696                            self.parsed.record_materialization(
4697                                MaterializationRecord::RecoveredDeclaration {
4698                                    recovery: recovered.range,
4699                                    unit: code_unit.clone(),
4700                                },
4701                            );
4702                            let mut member_scope = template_scope.clone();
4703                            member_scope.class_unit = Some(code_unit);
4704                            member_scope.declarations_are_fields = true;
4705                            member_scope.recovered_specialization_member_scope = true;
4706                            for prefix_member in recovered.prefix_members.into_iter().rev() {
4707                                stack.push(CppWork::Node(CppNodeWork {
4708                                    node: prefix_member,
4709                                    scope: member_scope.clone(),
4710                                }));
4711                            }
4712                            for sibling in recovered.member_siblings {
4713                                self.recovered_class_sibling_scopes
4714                                    .insert(sibling.id(), member_scope.clone());
4715                            }
4716                            for following in recovered.following_declarations.into_iter().rev() {
4717                                stack.push(CppWork::Node(CppNodeWork {
4718                                    node: following,
4719                                    scope: scope.clone(),
4720                                }));
4721                            }
4722                            return;
4723                        }
4724                        stack.push(CppWork::Node(CppNodeWork {
4725                            node: child,
4726                            scope: template_scope,
4727                        }));
4728                    }
4729                }
4730            }
4731            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4732            "linkage_specification" => {
4733                if let Some(body) = cpp_body_node(node) {
4734                    stack.push(CppWork::Container(CppContainer {
4735                        node: body,
4736                        scope: scope.clone(),
4737                    }));
4738                } else {
4739                    stack.push(CppWork::Container(CppContainer {
4740                        node,
4741                        scope: scope.clone(),
4742                    }));
4743                }
4744            }
4745            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4746                self.visit_class_like(node, scope, stack, ancestry)
4747            }
4748            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4749            // A bare namespace-begin sentinel can make tree-sitter promote the
4750            // wrapped declaration to an ERROR node instead of the usual bogus
4751            // function_definition envelope. Keep the recovery entry point on
4752            // the same structured path for both shapes; ordinary ERROR nodes
4753            // retain their declaration-preserving wrapper traversal when the
4754            // sentinel predicate does not match.
4755            "ERROR" => {
4756                self.visit_object_macro_error_classes(node, scope);
4757                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4758                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4759                    if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
4760                        return;
4761                    }
4762                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4763                        return;
4764                    }
4765                    self.visit_macro_swallowed_function_declarations(node, scope);
4766                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
4767                    self.visit_stranded_class_members(node, scope, ancestry);
4768                    stack.push(CppWork::Container(CppContainer {
4769                        node,
4770                        scope: scope.clone(),
4771                    }));
4772                }
4773            }
4774            "declaration" => {
4775                if node.has_error() {
4776                    // Object-like macro lines before a function-like
4777                    // export-macro class head keep the head in a `declaration`
4778                    // rather than in an `ERROR` (#2924). It is the same head
4779                    // and the same following-sibling body, so it takes the same
4780                    // recovery; without it the head's macro invocation indexes
4781                    // as a field named after the macro.
4782                    if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4783                        return;
4784                    }
4785                    self.visit_prototype_macro_declarations(node, scope);
4786                    if self.node_is_inside_consumed_fragment(node) {
4787                        // The whole declaration was a recovered K&R
4788                        // prototype macro invocation (issue #2932); the
4789                        // reparse above already indexed its Function.
4790                        // Falling through to the ordinary declaration
4791                        // visitor would additionally mint the swapped-field
4792                        // wreckage this recovery exists to replace.
4793                        return;
4794                    }
4795                }
4796                if scope.class_unit.is_some()
4797                    && scope.declarations_are_fields
4798                    && scope.recovered_specialization_member_scope
4799                    && let Some(alias_name) =
4800                        recovered_using_declaration_alias_name(node, self.source)
4801                {
4802                    self.add_type_aliases(node, scope, vec![alias_name], ancestry);
4803                } else {
4804                    self.visit_declaration(
4805                        node,
4806                        scope,
4807                        scope.declarations_are_fields,
4808                        stack,
4809                        ancestry,
4810                    )
4811                }
4812            }
4813            // A K&R prototype macro invocation with a pointer return type and a
4814            // simple argument list parses with no ERROR node at all: tree-sitter
4815            // reads `T *name _(( args ));` as a multiplication of a type by a
4816            // call (`T * name_(...)`), wrapped in an `expression_statement`
4817            // that `has_error()` only because of a `MISSING "::"` inside it
4818            // (issue #2932). Nothing else mints a declaration from an
4819            // `expression_statement` at declaration scope today, so there is
4820            // no existing behavior to preserve here if this node is not the
4821            // K&R shape; the admission gate inside
4822            // `visit_prototype_macro_declarations` (exactly one clean function
4823            // prototype spanning the recovered run) is what keeps this arm
4824            // safe on an ordinary errorful expression statement that happens
4825            // to contain nested parentheses.
4826            "expression_statement" => {
4827                if node.has_error() {
4828                    self.visit_prototype_macro_declarations(node, scope);
4829                }
4830            }
4831            "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4832            "preproc_call" => self.visit_preproc_call(node, scope),
4833            "type_definition" | "alias_declaration" => {
4834                self.visit_type_declaration(node, scope, stack, ancestry)
4835            }
4836            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4837            // `#include` is collected by `collect_cpp_includes` before the
4838            // walk, so a directive the container walk never reaches -- inside
4839            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
4840            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
4841            // include claim.
4842            "preproc_include" => {}
4843            kind if preserves_declaration_scope_through_wrapper(
4844                kind,
4845                scope.class_unit.is_some(),
4846            ) =>
4847            {
4848                // A preprocessor conditional gates every declaration inside it
4849                // on a configuration this analyzer never evaluates; record the
4850                // interval so declaration state can say so (issue #1476). The
4851                // else/elif branches are children of the `preproc_if` node, so
4852                // recording the openers covers every branch.
4853                if kind == "labeled_statement" {
4854                    self.visit_access_label_constructor(node, scope);
4855                }
4856                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4857                    let mut range = cpp_declaration_range(node);
4858                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4859                        range.end_byte = boundary.end_byte;
4860                        range.end_line = boundary.end_line;
4861                    }
4862                    self.parsed.record_materialization(
4863                        MaterializationRecord::ConfigurationConditional { range },
4864                    );
4865                    if node.has_error() {
4866                        // A malformed export-macro class can close the namespace
4867                        // node early while the enclosing include guard still owns
4868                        // the remaining class-head/body pairs. The ordinary walk
4869                        // cannot carry the lost namespace through those promoted
4870                        // siblings. Scan only structured ERROR nodes in this
4871                        // already-malformed conditional; the pair recovery's
4872                        // exact class/macro/body predicate remains the admission
4873                        // gate, and its namespace lifting restores the owner.
4874                        let mut candidates = vec![node];
4875                        while let Some(candidate) = candidates.pop() {
4876                            // An `ERROR` still inside a namespace body is one
4877                            // the ordinary walk reaches with that namespace in
4878                            // scope. Claiming it here registers the recovered
4879                            // class at file scope and the consumed region then
4880                            // suppresses the walk that would have named it
4881                            // correctly -- which is why Botan's `DL_Group` was
4882                            // `DL_Group` and not `Botan.DL_Group` in the real
4883                            // header, where an include guard wraps the
4884                            // namespace, and was right in a fixture without one
4885                            // (#2552).
4886                            if candidate.kind() == "ERROR"
4887                                && !cpp_is_inside_namespace_body(candidate, ancestry)
4888                                && self.visit_function_like_export_class_pair(
4889                                    candidate, scope, stack, ancestry,
4890                                )
4891                            {
4892                                continue;
4893                            }
4894                            for index in (0..candidate.named_child_count()).rev() {
4895                                candidates.push(
4896                                    candidate
4897                                        .named_child(index)
4898                                        .expect("index below the node's own named child count"),
4899                                );
4900                            }
4901                        }
4902                    }
4903                }
4904                stack.push(CppWork::Container(CppContainer {
4905                    node,
4906                    scope: scope.clone(),
4907                }))
4908            }
4909            // A macro invocation the parser could not group leaves its head in
4910            // whatever slot was free. The `function_definition` and `ERROR`
4911            // arms above try the same recovery on the envelopes they get; a
4912            // flattened run leaves a bare `identifier`/`type_identifier`, and a
4913            // swallowed tail can land in a `parameter_declaration` (#3094).
4914            _ => {
4915                self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
4916            }
4917        }
4918    }
4919
4920    fn visit_macro_swallowed_function_declarations<'tree>(
4921        &mut self,
4922        envelope: Node<'tree>,
4923        scope: &ScopeInfo,
4924    ) {
4925        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
4926            || envelope.kind() == "ERROR"
4927                && envelope
4928                    .parent()
4929                    .is_some_and(|parent| parent.kind() == "ERROR")
4930        {
4931            return;
4932        }
4933        let mut stack = (0..envelope.named_child_count())
4934            .filter_map(|index| envelope.named_child(index))
4935            .collect::<Vec<_>>();
4936        while let Some(node) = stack.pop() {
4937            if node.kind() == "function_declarator" {
4938                self.visit_error_swallowed_function_declaration(node, scope);
4939            }
4940            for child in named_children_iter(node) {
4941                stack.push(child);
4942            }
4943        }
4944    }
4945
4946    /// Index the declarations an attribute-like macro invocation swallowed into
4947    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
4948    /// the shape and why the parser produces it.
4949    fn visit_macro_wrapped_declarations<'tree>(
4950        &mut self,
4951        envelope: Node<'tree>,
4952        scope: &ScopeInfo,
4953        ancestry: &ParentIndex<'tree>,
4954    ) {
4955        let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
4956        if recovered.is_empty() {
4957            return;
4958        }
4959        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
4960        self.record_recovered_declarations(recovery, |visitor| {
4961            for declaration in recovered {
4962                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
4963            }
4964        });
4965    }
4966
4967    /// Index the declarations a macro invocation collapsed. See
4968    /// [`collapsed_macro_declaration_run`] for the shape and why the parser
4969    /// produces it. Returns whether it claimed the region beginning at
4970    /// `envelope`.
4971    ///
4972    /// The parser's own nodes cannot be read the way the swallowed tail of the
4973    /// one-line shape can. Of the 214 declarations whisper's `llama.h` hides in
4974    /// its envelope, 57 are shredded to bare identifier and punctuation tokens
4975    /// with no declarator left at all, and the declarators that do survive on
4976    /// the envelope's declarator spine pair one declaration's name with the
4977    /// *next* declaration's parameter list. Reading those would be a guess.
4978    ///
4979    /// The bytes are still ordinary declarations, though, and the collapse is
4980    /// the parser carrying the failure forward from one macro invocation. So
4981    /// reparse the region, walk the items the parser makes of it, and when one
4982    /// item is itself a collapsed run, recover that invocation from its own
4983    /// bytes and resume the scan just past it. Each pass starts later than the
4984    /// last, so the scan is a loop over the invocations that collapse, not over
4985    /// the declarations: `llama.h` needs three passes for 214 declarations.
4986    ///
4987    /// The region can reach past `envelope` into the siblings the parser handed
4988    /// the rest of one invocation, so it is recorded as consumed: those
4989    /// siblings are this recovery's, and the ordinary walk must not read them
4990    /// again. Recording it after the scan, not before, keeps the scan's own
4991    /// reparsed nodes -- which carry their original byte offsets -- visible to
4992    /// the walk it drives.
4993    fn visit_collapsed_macro_declaration_run<'tree>(
4994        &mut self,
4995        envelope: Node<'tree>,
4996        scope: &ScopeInfo,
4997        ancestry: &ParentIndex<'tree>,
4998    ) -> bool {
4999        let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
5000            return false;
5001        };
5002        let start = envelope.start_byte();
5003        let end = run.region_end;
5004        let recovery = cpp_recovery_window(self.source, start, end);
5005        self.record_recovered_declarations(recovery, |visitor| {
5006            let mut position = start;
5007            while position < end {
5008                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
5009                    return;
5010                };
5011                let root = tree.root_node();
5012                // A region reparse is its own tree and needs its own parent
5013                // index; the caller's answers nothing about these nodes.
5014                let ancestry = ParentIndex::new(root);
5015                let mut cursor = root.walk();
5016                let collapsed =
5017                    root.named_children(&mut cursor)
5018                        .enumerate()
5019                        .find_map(|(index, item)| {
5020                            collapsed_macro_declaration_run(item, visitor.source, &ancestry)
5021                                .map(|run| (index, item, run))
5022                        });
5023                // Walk only the items before the collapsed one. It swallowed
5024                // the rest of the region, so handing it to the ordinary walk
5025                // would re-enter this recovery on a region that starts where
5026                // this one did.
5027                let mut stack = Vec::new();
5028                push_cpp_sibling_range(
5029                    root,
5030                    0,
5031                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
5032                    scope.clone(),
5033                    &mut stack,
5034                );
5035                visitor.drain_cpp_work(stack, &ancestry);
5036                let Some((_, item, run)) = collapsed else {
5037                    return;
5038                };
5039                // On its own bytes the invocation is the declaration-scope
5040                // shape `macro_wrapped_declarations` reads, so its wrapped
5041                // declaration needs no reader of its own here.
5042                if let Some(head) =
5043                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
5044                {
5045                    let head_root = head.root_node();
5046                    visitor.run_container_work(
5047                        head_root,
5048                        scope.clone(),
5049                        &ParentIndex::new(head_root),
5050                    );
5051                }
5052                assert!(
5053                    run.invocation_end > position,
5054                    "a collapsed run at {position} must end after the byte the scan resumed \
5055                     from, but ended at {}",
5056                    run.invocation_end
5057                );
5058                position = run.invocation_end;
5059            }
5060        });
5061        self.consumed_fragment_regions.push((start, end));
5062        true
5063    }
5064
5065    /// Index the members a string-argument attribute macro stranded in an
5066    /// `ERROR` inside a class body.
5067    ///
5068    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
5069    /// grouping of the attributed member and of every member written after it
5070    /// until it recovers. The members are still whole `function_declarator`
5071    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
5072    /// export-macro class such as Botan's `DL_Group` was indexed with no
5073    /// members at all (#2552).
5074    fn visit_stranded_class_members<'tree>(
5075        &mut self,
5076        node: Node<'tree>,
5077        scope: &ScopeInfo,
5078        ancestry: &ParentIndex<'tree>,
5079    ) {
5080        if scope.class_unit.is_none() || !scope.declarations_are_fields {
5081            return;
5082        }
5083        for member in stranded_declaration_run(node, self.source).declarations {
5084            self.add_macro_wrapped_declaration(member, scope, ancestry);
5085        }
5086    }
5087
5088    /// Index the constructor an access label swallowed in a reparsed class
5089    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
5090    ///
5091    /// The declarator is recovered by reparsing from that call to the end of
5092    /// the label's own statement, which is the same offset-preserving region
5093    /// reparse every other recovery here uses, so the recovered nodes carry
5094    /// their true source positions.
5095    fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5096        let Some(class_unit) = scope.class_unit.clone() else {
5097            return;
5098        };
5099        if !scope.declarations_are_fields {
5100            return;
5101        }
5102        let class_name = class_unit.identifier().to_string();
5103        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
5104        else {
5105            return;
5106        };
5107        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
5108            return;
5109        };
5110        let root = tree.root_node();
5111        let Some(declarator) =
5112            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
5113        else {
5114            return;
5115        };
5116        let reparsed_ancestry = ParentIndex::new(root);
5117        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
5118        let range = cpp_declaration_range(definition.unwrap_or(declarator));
5119        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
5120        self.record_recovered_declarations(recovery, |visitor| {
5121            visitor.add_macro_wrapped_declaration(
5122                MacroWrappedDeclaration {
5123                    declarator,
5124                    range,
5125                    is_static: false,
5126                },
5127                scope,
5128                &reparsed_ancestry,
5129            );
5130        });
5131    }
5132
5133    fn add_macro_wrapped_declaration<'tree>(
5134        &mut self,
5135        declaration: MacroWrappedDeclaration<'tree>,
5136        scope: &ScopeInfo,
5137        ancestry: &ParentIndex<'tree>,
5138    ) {
5139        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
5140        else {
5141            return;
5142        };
5143        let code_unit =
5144            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
5145        if self.parsed.contains_declaration(&code_unit) {
5146            self.parsed
5147                .record_navigation_range(code_unit, declaration.range);
5148            return;
5149        }
5150        self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
5151        let signature = normalize_cpp_whitespace(
5152            self.source
5153                .get(declaration.range.start_byte..declaration.range.end_byte)
5154                .expect("a recovered declaration range covers one source range"),
5155        );
5156        let linkage = if declaration.is_static {
5157            CallableLinkage::Internal
5158        } else {
5159            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
5160        };
5161        // Whether this is a definition is a property of the recovered node, not
5162        // of the caller: a declarator that a `function_definition` gives a body
5163        // is a definition wherever the recovery found it.
5164        let declaration_only =
5165            cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
5166        self.parsed.add_signature_with_metadata(
5167            code_unit.clone(),
5168            cpp_signature_metadata(signature, declaration.declarator, self.source, ancestry)
5169                .with_declaration_only(declaration_only)
5170                .with_callable_linkage(linkage),
5171        );
5172        if let Some(parent) = &scope.class_unit {
5173            self.parsed.add_child(parent.clone(), code_unit);
5174        } else if let Some(module) = &scope.module {
5175            self.parsed.add_child(module.clone(), code_unit);
5176        }
5177    }
5178
5179    /// Index only anonymous aggregate declarations from an ordinary
5180    /// function body. Function bodies are otherwise outside the declaration
5181    /// walk, but their anonymous aggregate owners must exist so structured
5182    /// local binding inference can name their fields (#2994).
5183    fn visit_anonymous_local_aggregates_in_function<'tree>(
5184        &mut self,
5185        function: Node<'tree>,
5186        scope: &ScopeInfo,
5187        stack: &mut Vec<CppWork<'tree>>,
5188        ancestry: &ParentIndex<'tree>,
5189    ) {
5190        let Some(body) = cpp_body_node(function) else {
5191            return;
5192        };
5193        // A body opens a block scope, so nothing declared in it is a member of
5194        // the class the definition may be written inside. Dropping the
5195        // aggregate carrier gives an inline member function's body the same
5196        // block scope an out-of-line `void C::f()` body already has; without
5197        // it a C++ header, where member bodies are usually inline, would keep
5198        // none of these owners (#3301).
5199        let block_scope;
5200        let scope = if scope.class_unit.is_some() {
5201            block_scope = ScopeInfo {
5202                class_unit: None,
5203                declarations_are_fields: false,
5204                ..scope.clone()
5205            };
5206            &block_scope
5207        } else {
5208            scope
5209        };
5210        let mut pending = vec![body];
5211        while let Some(node) = pending.pop() {
5212            if matches!(node.kind(), "function_definition" | "lambda_expression") {
5213                continue;
5214            }
5215            if node.kind() == "declaration"
5216                && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
5217            {
5218                continue;
5219            }
5220            if matches!(
5221                node.kind(),
5222                "class_specifier" | "struct_specifier" | "union_specifier"
5223            ) {
5224                continue;
5225            }
5226            let mut cursor = node.walk();
5227            let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
5228            children.reverse();
5229            pending.extend(children);
5230        }
5231    }
5232
5233    fn visit_error_swallowed_function_declaration<'tree>(
5234        &mut self,
5235        node: Node<'tree>,
5236        scope: &ScopeInfo,
5237    ) -> bool {
5238        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
5239            return false;
5240        };
5241        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5242            return false;
5243        };
5244        let root = tree.root_node();
5245        let mut cursor = root.walk();
5246        let declarations = root
5247            .named_children(&mut cursor)
5248            .filter(|child| child.kind() != "comment")
5249            .collect::<Vec<_>>();
5250        let [declaration] = declarations.as_slice() else {
5251            return false;
5252        };
5253        if declaration.kind() != "declaration"
5254            || declaration.has_error()
5255            || declaration.start_byte() != start
5256            || declaration.end_byte() != end
5257        {
5258            return false;
5259        }
5260        let recovery = cpp_recovery_window(self.source, start, end);
5261        // The reparsed region is its own tree, so this walk indexes it itself.
5262        let reparsed_ancestry = ParentIndex::new(root);
5263        self.record_recovered_declarations(recovery, |visitor| {
5264            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5265        });
5266        true
5267    }
5268
5269    /// Recover a K&R-compatibility prototype macro invocation such as
5270    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
5271    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
5272    /// defined only in headers outside this workspace (ruby's own
5273    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
5274    /// `name _((args))` and turns each such line into a malformed
5275    /// `declaration` or pointer-expression statement. Before this
5276    /// recovery, the ordinary declaration visitor read that wreckage as a
5277    /// field with the type and name swapped (e.g. a field literally named
5278    /// `VALUE`) and indexed no prototype for the real name at all.
5279    ///
5280    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
5281    /// preserve the declared name, a known one-argument compatibility macro,
5282    /// the nested argument list, and a live terminator. It rejects arbitrary
5283    /// identifiers and bare `ERROR` envelopes that flattened those relations;
5284    /// guessing there previously turned malformed non-macro declarations into
5285    /// invented functions. For each proven candidate this reparses the range
5286    /// before the macro token, the inner parenthesized argument node, and the
5287    /// terminating `;` as one included-range parse. That is the parser-native
5288    /// equivalent of expanding `#define _(args) args` while retaining original
5289    /// byte offsets. The result is admitted only when it is exactly one clean
5290    /// function prototype spanning the recovered run.
5291    fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5292        for candidate in cpp_prototype_macro_candidates(node, self.source) {
5293            let start = candidate.run_start;
5294            let end = candidate.semicolon_end;
5295            if self.byte_range_is_inside_consumed_fragment(start, end) {
5296                continue;
5297            }
5298            let Some(tree) = parse_source_ranges_with_cancellation(
5299                &tree_sitter_cpp::LANGUAGE.into(),
5300                self.source,
5301                &candidate.ranges(),
5302                None,
5303            ) else {
5304                continue;
5305            };
5306            let root = tree.root_node();
5307            let mut cursor = root.walk();
5308            let declarations = root
5309                .named_children(&mut cursor)
5310                .filter(|child| child.kind() != "comment")
5311                .collect::<Vec<_>>();
5312            let [declaration] = declarations.as_slice() else {
5313                continue;
5314            };
5315            if declaration.kind() != "declaration"
5316                || declaration.has_error()
5317                || declaration.start_byte() != start
5318                || declaration.end_byte() != end
5319                || declaration
5320                    .child_by_field_name("declarator")
5321                    .and_then(extract_function_declarator)
5322                    .is_none()
5323            {
5324                continue;
5325            }
5326            let recovery = cpp_recovery_window(self.source, start, end);
5327            // The reparsed region is its own tree, so this walk indexes it
5328            // itself.
5329            let reparsed_ancestry = ParentIndex::new(root);
5330            self.record_recovered_declarations(recovery, |visitor| {
5331                visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5332            });
5333            self.consumed_fragment_regions.push((start, end));
5334        }
5335    }
5336
5337    /// Declare one Module per namespace level of `levels` under `package_name`,
5338    /// each recording the range of the construct that writes it, and return the
5339    /// innermost level's package name and Module. A level an earlier definition
5340    /// already declared is reused, which is also how a reopened namespace keeps
5341    /// the range of the definition that first declared it.
5342    fn declare_namespace_levels(
5343        &mut self,
5344        mut package_name: String,
5345        levels: Vec<(String, Range)>,
5346    ) -> (String, Option<CodeUnit>) {
5347        let mut module = None;
5348        for (component, range) in levels {
5349            let full_name = if package_name.is_empty() {
5350                component
5351            } else {
5352                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
5353            };
5354            let level = CodeUnit::new_fq(
5355                self.file.clone(),
5356                CodeUnitType::Module,
5357                "",
5358                full_name.clone(),
5359                cpp_namespace_fq(&full_name),
5360            );
5361            if !self.parsed.contains_declaration(&level) {
5362                self.add_declaration_with_range(level.clone(), range, None, None);
5363            }
5364            package_name = full_name;
5365            module = Some(level);
5366        }
5367        (package_name, module)
5368    }
5369
5370    /// Restore the complete lexical namespace path when recovery dropped an
5371    /// ancestor or retained it past its real close. Parsed namespaces beginning
5372    /// inside the recovered region still contribute their normal nesting.
5373    /// Crossing that boundary also clears any malformed class owner.
5374    fn recovered_namespace_scope(
5375        &mut self,
5376        node: Node<'_>,
5377        scope: &ScopeInfo,
5378    ) -> Option<ScopeInfo> {
5379        self.orphaned_namespaces.region_at(node.start_byte())?;
5380        // Each level carries the `namespace Name { ... }` construct that writes
5381        // it. Declaring them at `node` instead recorded the range of whichever
5382        // member the walk happened to reach first, so a namespace whose head
5383        // collapsed into an ERROR reported its first inner declaration's lines
5384        // as its own (#3309).
5385        let levels = self
5386            .orphaned_namespaces
5387            .enclosing_namespace_levels(node, self.source);
5388        let package_name = levels
5389            .iter()
5390            .map(|(component, _)| component.as_str())
5391            .collect::<Vec<_>>()
5392            .join(CPP_PACKAGE_SEPARATOR);
5393        if package_name == scope.package_name {
5394            return None;
5395        }
5396        let (package_name, module) = self.declare_namespace_levels(String::new(), levels);
5397        Some(ScopeInfo {
5398            package_name,
5399            module,
5400            // A recovered namespace body is no longer inside the class (or
5401            // function) that tree-sitter accidentally used as its wrapper.
5402            // Keep namespace/module context, but never carry that owner into
5403            // declarations published from the recovered region.
5404            class_unit: None,
5405            template_signature: None,
5406            template_metadata: None,
5407            declarations_are_fields: false,
5408            recovered_specialization_member_scope: false,
5409            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5410        })
5411    }
5412
5413    fn visit_namespace<'tree>(
5414        &mut self,
5415        node: Node<'tree>,
5416        scope: &ScopeInfo,
5417        stack: &mut Vec<CppWork<'tree>>,
5418        ancestry: &ParentIndex<'tree>,
5419    ) {
5420        let name_node = node.child_by_field_name("name");
5421        let Some(name_node) = name_node else {
5422            if let Some(body) = cpp_body_node(node) {
5423                stack.push(CppWork::Container(CppContainer {
5424                    node: body,
5425                    scope: scope.clone(),
5426                }));
5427            }
5428            return;
5429        };
5430        // Diagnostic corpora contain deliberately ill-formed global namespace
5431        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
5432        // the leading global `::` as the first anonymous child. Honor that AST
5433        // boundary instead of appending the name to the lexical namespace;
5434        // appending produced legacy names such as `outer::::outer::inner`, which
5435        // could not round-trip through the structured FqName boundary.
5436        let explicitly_global = name_node
5437            .child(0)
5438            .is_some_and(|child| !child.is_named() && child.kind() == "::");
5439        let components = cpp_namespace_name_components(name_node, self.source);
5440        if components.is_empty() {
5441            return;
5442        }
5443        // One Module per namespace level. C++17's `namespace a::b { ... }` is
5444        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
5445        // shorthand must declare `a` as well as `a::b` -- extracting only the
5446        // innermost level left the enclosing namespace undeclared and made the
5447        // two spellings of one construct disagree (issue #1878).
5448        let package_name = if explicitly_global {
5449            String::new()
5450        } else {
5451            scope.package_name.clone()
5452        };
5453        let definition = node_range(node);
5454        let (package_name, module) = self.declare_namespace_levels(
5455            package_name,
5456            components
5457                .into_iter()
5458                .map(|component| (component, definition))
5459                .collect(),
5460        );
5461
5462        let namespace_scope = ScopeInfo {
5463            package_name,
5464            module,
5465            // C++ never nests a namespace inside a class, so a surviving
5466            // class_unit here is always recovery bleed: a malformed-region
5467            // boundary upstream mis-scoped this namespace block. Keeping the
5468            // owner would mint the namespace's declarations as class members
5469            // under a re-appended package, desyncing the fq boundary assert
5470            // (#2306). Dropping it is identity-neutral for valid code, where
5471            // class_unit is always empty at a namespace definition.
5472            class_unit: None,
5473            template_signature: scope.template_signature.clone(),
5474            template_metadata: scope.template_metadata.clone(),
5475            declarations_are_fields: false,
5476            recovered_specialization_member_scope: false,
5477            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5478        };
5479        let container = cpp_body_node(node).unwrap_or(node);
5480        // A malformed export-macro class body may turn the following class
5481        // into a descendant of a bogus function/labeled/error envelope. Those
5482        // descendants are not declaration containers and the ordinary walk
5483        // intentionally does not descend into them. Scan the namespace tree
5484        // once for the strict embedded class geometry before scheduling its
5485        // normal declarations. When one envelope matches, its helper recovers
5486        // every embedded class and the walk need not inspect its descendants.
5487        let mut candidates = vec![container];
5488        while let Some(candidate) = candidates.pop() {
5489            if matches!(
5490                candidate.kind(),
5491                "ERROR" | "function_definition" | "labeled_statement"
5492            ) && self.visit_embedded_function_like_export_classes(
5493                candidate,
5494                &namespace_scope,
5495                stack,
5496                ancestry,
5497            ) {
5498                continue;
5499            }
5500            for index in (0..candidate.named_child_count()).rev() {
5501                candidates.push(
5502                    candidate
5503                        .named_child(index)
5504                        .expect("index below the node's own named child count"),
5505                );
5506            }
5507        }
5508        stack.push(CppWork::Container(CppContainer {
5509            node: container,
5510            scope: namespace_scope,
5511        }));
5512    }
5513
5514    fn visit_class_like<'tree>(
5515        &mut self,
5516        node: Node<'tree>,
5517        scope: &ScopeInfo,
5518        stack: &mut Vec<CppWork<'tree>>,
5519        ancestry: &ParentIndex<'tree>,
5520    ) {
5521        let Some(name) = class_like_name(node, self.source, ancestry) else {
5522            return;
5523        };
5524        let name = qualified_class_name_chain(node, self.source, scope)
5525            .map(|chain| chain.join("$"))
5526            .unwrap_or(name);
5527        self.visit_named_class_like(node, name, scope, stack, ancestry);
5528    }
5529
5530    fn visit_named_class_like<'tree>(
5531        &mut self,
5532        node: Node<'tree>,
5533        name: String,
5534        scope: &ScopeInfo,
5535        stack: &mut Vec<CppWork<'tree>>,
5536        ancestry: &ParentIndex<'tree>,
5537    ) {
5538        let body = cpp_body_node(node);
5539        let definition_body_present = body.is_some();
5540        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
5541            .then(|| extract_cpp_supertypes(node, self.source));
5542        self.visit_named_class_like_shape(
5543            node,
5544            name,
5545            body,
5546            definition_body_present,
5547            None,
5548            raw_supertypes,
5549            scope,
5550            stack,
5551            ancestry,
5552        );
5553    }
5554
5555    /// Whether this class-like declaration is a C tag that belongs to the
5556    /// enclosing non-aggregate scope rather than to the aggregate it is
5557    /// lexically written inside.
5558    ///
5559    /// `class_specifier` is deliberately excluded: `class` is not C, so text
5560    /// that spells one in a `.c` file is not C code and keeps the C++ reading
5561    /// rather than getting a half-C identity.
5562    fn mints_tag_at_enclosing_c_scope(
5563        &self,
5564        declaration_node: Node<'_>,
5565        scope: &ScopeInfo,
5566        ancestry: &ParentIndex<'_>,
5567    ) -> bool {
5568        self.c_tag_semantics
5569            && scope.class_unit.is_some()
5570            && class_like_name(declaration_node, self.source, ancestry).is_some()
5571            && matches!(
5572                declaration_node.kind(),
5573                "struct_specifier" | "union_specifier" | "enum_specifier"
5574            )
5575    }
5576
5577    #[allow(clippy::too_many_arguments)]
5578    fn visit_named_class_like_shape<'tree>(
5579        &mut self,
5580        declaration_node: Node<'tree>,
5581        name: String,
5582        body: Option<Node<'tree>>,
5583        definition_body_present: bool,
5584        explicit_range: Option<Range>,
5585        raw_supertypes: Option<Vec<String>>,
5586        scope: &ScopeInfo,
5587        stack: &mut Vec<CppWork<'tree>>,
5588        ancestry: &ParentIndex<'tree>,
5589    ) -> CodeUnit {
5590        let displaced_macro_tail = if explicit_range.is_none() {
5591            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
5592        } else {
5593            None
5594        };
5595        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
5596        let recovered_scope = self.scope_for_recovered_exported_class(
5597            declaration_node,
5598            &name,
5599            definition_body_present,
5600            scope,
5601            ancestry,
5602        );
5603        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
5604        // aggregate's member list is declared at the enclosing non-aggregate
5605        // scope, not nested inside the aggregate. `scope.class_unit` is the
5606        // only aggregate carrier in this walk, so dropping it puts the tag at
5607        // the nearest enclosing non-aggregate scope -- the module at file or
5608        // namespace scope, and the same block-scope representation a
5609        // function-local aggregate already gets. The tag's own body scope
5610        // below still owns its members, so fields and enumerators are
5611        // unaffected.
5612        let c_tag_scope;
5613        let scope =
5614            if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
5615                c_tag_scope = ScopeInfo {
5616                    class_unit: None,
5617                    ..recovered_scope.clone()
5618                };
5619                &c_tag_scope
5620            } else {
5621                &recovered_scope
5622            };
5623        let short_name = if let Some(parent) = &scope.class_unit {
5624            cpp_join_nested_short(parent.short_name(), &name)
5625        } else {
5626            name.clone()
5627        };
5628        // A top-level out-of-line qualified class definition (`struct
5629        // Outer::Inner { ... }` inside its namespace, #2246) carries its
5630        // nesting chain as the `$`-joined display name; push one Type/Nested
5631        // segment per class so segment-pop owner navigation keeps working.
5632        // Every other leaf name stays opaque so a literal `$` in a source
5633        // identifier never crosses the split/join boundary (#2140).
5634        let qualified_chain = if scope.class_unit.is_none() {
5635            qualified_class_name_chain(declaration_node, self.source, scope)
5636                .filter(|chain| chain.join("$") == name)
5637        } else {
5638            None
5639        };
5640        let fq = if let Some(chain) = qualified_chain {
5641            let mut fq = FqName::new();
5642            cpp_push_package(&mut fq, &scope.package_name);
5643            let mut first = true;
5644            for component in chain {
5645                let kind = if first {
5646                    SegmentKind::Type
5647                } else {
5648                    SegmentKind::Nested
5649                };
5650                fq.push(cpp_segment(&component, kind));
5651                first = false;
5652            }
5653            fq
5654        } else {
5655            cpp_leaf_fq(
5656                &scope.package_name,
5657                scope.class_unit.as_ref(),
5658                &name,
5659                SegmentKind::Nested,
5660                SegmentKind::Type,
5661            )
5662        };
5663        let code_unit = CodeUnit::with_signature_and_fq(
5664            self.file.clone(),
5665            CodeUnitType::Class,
5666            scope.package_name.clone(),
5667            short_name,
5668            scope.template_signature.clone(),
5669            false,
5670            fq,
5671        );
5672        let has_body = definition_body_present;
5673        if !has_body && self.parsed.contains_declaration(&code_unit) {
5674            self.parsed.record_navigation_range(
5675                code_unit.clone(),
5676                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
5677            );
5678            return code_unit;
5679        }
5680        if has_body {
5681            // A forward declaration above this definition is one of the
5682            // class's declaration sites: C++ injects the class name at the
5683            // forward declaration, so a reference between the two spells this
5684            // class (#3297). Moving the unit to its body must not drop the
5685            // earlier site, exactly as a function prototype and its body stay
5686            // two ranges of one declaration (#1650).
5687            let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
5688            if let Some(range) = explicit_range {
5689                self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
5690            } else {
5691                self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
5692            }
5693            for range in earlier_ranges {
5694                self.parsed.add_declaration_range(&code_unit, range);
5695            }
5696        } else {
5697            self.add_declaration(code_unit.clone(), declaration_node, None, None);
5698        }
5699        if let Some(raw_supertypes) = raw_supertypes {
5700            self.parsed
5701                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
5702        }
5703        self.parsed.add_signature(
5704            code_unit.clone(),
5705            render_cpp_type_signature(
5706                declaration_node,
5707                self.source,
5708                scope.template_signature.as_deref(),
5709            ),
5710        );
5711        if let Some(metadata) = &scope.template_metadata {
5712            let primary_short_name = if let Some(parent) = &scope.class_unit {
5713                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
5714            } else {
5715                metadata.primary_name.clone()
5716            };
5717            let primary_fq_name = CodeUnit::new(
5718                self.file.clone(),
5719                CodeUnitType::Class,
5720                scope.package_name.clone(),
5721                primary_short_name,
5722            )
5723            .fq_name();
5724            let mut metadata = metadata.clone();
5725            metadata.primary_fq_name = primary_fq_name;
5726            self.parsed
5727                .set_cpp_template_metadata(code_unit.clone(), metadata);
5728        }
5729        if let Some(parent) = &scope.class_unit {
5730            self.parsed.add_child(parent.clone(), code_unit.clone());
5731        } else if let Some(module) = &scope.module {
5732            self.parsed.add_child(module.clone(), code_unit.clone());
5733        }
5734
5735        if let Some(body) = body {
5736            let mut nested_scope = scope.clone();
5737            nested_scope.class_unit = Some(code_unit.clone());
5738            nested_scope.template_signature = scope.template_signature.clone();
5739            // Template metadata describes the class just created. It must not
5740            // leak into ordinary nested declarations in that class's body.
5741            // Recovered export-macro specializations carry a separate scope bit
5742            // for their declaration-shaped body members.
5743            nested_scope.template_metadata = None;
5744            // Export-macro class bodies recovered from a function_definition use
5745            // compound_statement children, whose direct fields are declarations.
5746            nested_scope.recovered_specialization_member_scope =
5747                scope.template_metadata.as_ref().is_some_and(|metadata| {
5748                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
5749                });
5750            nested_scope.declarations_are_fields =
5751                is_recovered_exported_class_container(declaration_node, self.source)
5752                    || nested_scope.recovered_specialization_member_scope;
5753            if let Some(displaced) = displaced_macro_tail {
5754                // A macro-shaped field without a source semicolon can make
5755                // tree-sitter consume the real class terminator as an ERROR
5756                // inside that field, then retain following namespace items as
5757                // later field-list children. Drain the proven class prefix
5758                // first and re-own only the structured tail with the outer
5759                // scope. The tail is pushed first because the work stack is
5760                // LIFO.
5761                push_cpp_sibling_range(
5762                    body,
5763                    displaced.split_index,
5764                    usize::MAX,
5765                    scope.clone(),
5766                    stack,
5767                );
5768                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
5769            } else {
5770                stack.push(CppWork::Container(CppContainer {
5771                    node: body,
5772                    scope: nested_scope,
5773                }));
5774            }
5775        }
5776        if declaration_node.kind() == "enum_specifier" {
5777            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
5778            if !self.has_enum_enumerator_units(&code_unit) {
5779                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
5780            }
5781        }
5782        code_unit
5783    }
5784
5785    /// Whether the parse product already holds enumerator fields for `parent`,
5786    /// answered from the walk's carried-forward field ownership index.
5787    ///
5788    /// Built on the first enum's question and advanced by every declaration
5789    /// recorded after it, so a file that declares no enum -- most files -- pays
5790    /// nothing, and one that declares thousands pays a single pass instead of
5791    /// one per enum (#2786).
5792    fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
5793        if self.field_owners.is_none() {
5794            self.field_owners = Some(CppFieldOwnerIndex::of(
5795                self.parsed.declarations().iter(),
5796                self.file,
5797            ));
5798        }
5799        debug_assert_eq!(
5800            parent.source(),
5801            self.file,
5802            "the walk's declarations are declarations of the file it is walking"
5803        );
5804        let carried = self
5805            .field_owners
5806            .as_ref()
5807            .expect("the index was just ensured")
5808            .owns_fields(parent.package_name(), parent.short_name());
5809
5810        #[cfg(debug_assertions)]
5811        assert_eq!(
5812            carried,
5813            cpp_declarations_hold_owned_fields(
5814                self.parsed.declarations(),
5815                self.file,
5816                parent.package_name(),
5817                parent.short_name()
5818            ),
5819            "the carried-forward field index must answer what a fresh declaration scan \
5820             answers for {}",
5821            parent.fq_name()
5822        );
5823
5824        carried
5825    }
5826
5827    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
5828        walk_named_tree_preorder(node, false, |child| {
5829            if child.kind() != "enumerator" {
5830                return WalkControl::Continue;
5831            }
5832            let Some(name_node) = child.child_by_field_name("name") else {
5833                return WalkControl::Continue;
5834            };
5835            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
5836            if name.is_empty() {
5837                return WalkControl::Continue;
5838            }
5839            let code_unit = CodeUnit::new_fq(
5840                self.file.clone(),
5841                CodeUnitType::Field,
5842                scope.package_name.clone(),
5843                cpp_join_member_short(parent.short_name(), &name),
5844                parent
5845                    .fq()
5846                    .clone()
5847                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
5848            );
5849            if self.parsed.contains_declaration(&code_unit) {
5850                return WalkControl::Continue;
5851            }
5852            self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
5853            self.parsed.add_signature(
5854                code_unit,
5855                normalize_cpp_whitespace(node_text(child, self.source)),
5856            );
5857            WalkControl::Continue
5858        });
5859    }
5860
5861    fn visit_enum_enumerators_from_text(
5862        &mut self,
5863        node: Node<'_>,
5864        scope: &ScopeInfo,
5865        parent: &CodeUnit,
5866    ) {
5867        let text = node_text(node, self.source);
5868        let Some((_, body)) = text.split_once('{') else {
5869            return;
5870        };
5871        let Some((body, _)) = body.rsplit_once('}') else {
5872            return;
5873        };
5874        for entry in body.split(',') {
5875            let trimmed = entry.trim();
5876            let name = trimmed
5877                .split('=')
5878                .next()
5879                .unwrap_or("")
5880                .split_whitespace()
5881                .next()
5882                .unwrap_or("");
5883            if name.is_empty() {
5884                continue;
5885            }
5886            let code_unit = CodeUnit::new_fq(
5887                self.file.clone(),
5888                CodeUnitType::Field,
5889                scope.package_name.clone(),
5890                cpp_join_member_short(parent.short_name(), name),
5891                parent
5892                    .fq()
5893                    .clone()
5894                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
5895            );
5896            if self.parsed.contains_declaration(&code_unit) {
5897                continue;
5898            }
5899            self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
5900            self.parsed.add_signature(code_unit, trimmed.to_string());
5901        }
5902    }
5903
5904    fn visit_function_definition<'tree>(
5905        &mut self,
5906        node: Node<'tree>,
5907        scope: &ScopeInfo,
5908        stack: &mut Vec<CppWork<'tree>>,
5909        ancestry: &ParentIndex<'tree>,
5910    ) {
5911        // An attribute-like macro invocation whose argument is a declaration can
5912        // swallow every declaration written after it into one bogus
5913        // `function_definition` (#2551). This owns the whole region, so it runs
5914        // ahead of `visit_macro_swallowed_function_declarations` below, which
5915        // admits the same envelope by its head macro token and reads single
5916        // declarators out of nodes this recovery reparses properly.
5917        if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
5918            return;
5919        }
5920        // A file-scope object-like macro sentinel the parser cannot see (issue
5921        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
5922        // prefixes as a bogus `function_definition` that swallows real namespaces,
5923        // classes, and members. Reparse the swallowed interior as C++ items so the
5924        // ordinary declaration visitors index it with byte/line-exact ownership.
5925        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5926            return;
5927        }
5928        if node.has_error() {
5929            self.visit_macro_swallowed_function_declarations(node, scope);
5930        }
5931        if let Some((class_node, name, raw_supertypes)) =
5932            recover_exported_class_function_definition(node, self.source)
5933        {
5934            if let Some(body) = cpp_body_node(node)
5935                && let Some(close) = self
5936                    .orphaned_namespaces
5937                    .matching_close_brace(body.start_byte())
5938                && close.end_byte < body.end_byte()
5939            {
5940                let class_range = Range {
5941                    start_byte: node.start_byte(),
5942                    end_byte: close.end_byte,
5943                    start_line: node.start_position().row + 1,
5944                    end_line: close.end_line,
5945                };
5946                // Parse the body in class context, preserving the original
5947                // keyword, recovered name and body as included ranges. Parsing
5948                // the interior at file scope turns constructors and access
5949                // labels into another oversized ERROR instead of members.
5950                let mut head = vec![node];
5951                let mut keyword = None;
5952                let mut name_node = None;
5953                while let Some(part) = head.pop() {
5954                    if part.start_byte() >= body.start_byte() || part.is_missing() {
5955                        continue;
5956                    }
5957                    if matches!(part.kind(), "class" | "struct" | "union") {
5958                        keyword = Some(part);
5959                    }
5960                    if matches!(part.kind(), "identifier" | "type_identifier")
5961                        && node_text(part, self.source) == name
5962                    {
5963                        name_node = Some(part);
5964                    }
5965                    let mut cursor = part.walk();
5966                    head.extend(part.children(&mut cursor));
5967                }
5968                if let (Some(keyword), Some(name_node)) = (keyword, name_node)
5969                    && let Some(type_name) = keyword
5970                        .parent()
5971                        .and_then(|parent| parent.child_by_field_name("name"))
5972                    && let Some(tree) = parse_source_ranges_with_cancellation(
5973                        &tree_sitter_cpp::LANGUAGE.into(),
5974                        self.source,
5975                        &[
5976                            (keyword.start_byte(), type_name.start_byte()),
5977                            (name_node.start_byte(), name_node.end_byte()),
5978                            (body.start_byte(), close.end_byte),
5979                        ],
5980                        None,
5981                    )
5982                    && let Some(reparsed_class) = tree.root_node().named_child(0)
5983                    && let Some(class_body) = cpp_body_node(reparsed_class)
5984                    && class_body.start_byte() == body.start_byte()
5985                    && class_body.end_byte() == close.end_byte
5986                    && let Some(tail) =
5987                        cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
5988                {
5989                    let class_unit = self.visit_named_class_like_shape(
5990                        class_node,
5991                        name,
5992                        None,
5993                        true,
5994                        Some(class_range),
5995                        raw_supertypes,
5996                        scope,
5997                        stack,
5998                        ancestry,
5999                    );
6000                    self.parsed.record_materialization(
6001                        MaterializationRecord::RecoveredDeclaration {
6002                            recovery: class_range,
6003                            unit: class_unit.clone(),
6004                        },
6005                    );
6006                    let member_scope = ScopeInfo {
6007                        package_name: class_unit.package_name().to_string(),
6008                        class_unit: Some(class_unit),
6009                        declarations_are_fields: true,
6010                        template_metadata: None,
6011                        recovered_specialization_member_scope: false,
6012                        ..scope.clone()
6013                    };
6014                    let class_body_range = class_body.byte_range();
6015                    let tail_range = tail.root_node().byte_range();
6016                    self.partitioned_regions
6017                        .push((tail, tail_range, scope.clone()));
6018                    self.partitioned_regions
6019                        .push((tree, class_body_range, member_scope));
6020                    return;
6021                }
6022            }
6023            let body = cpp_body_node(class_node);
6024            let displaced_namespace = cpp_body_node(node)
6025                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
6026            let fragmented = cpp_body_node(node).and_then(|body| {
6027                fragmented_export_function_body_region(
6028                    node,
6029                    body,
6030                    self.source,
6031                    displaced_namespace.as_ref(),
6032                )
6033            });
6034            // The recovery tuple's first node is the class-like type when the
6035            // parser exposes one, but the synthetic wrapper owns the compound
6036            // statement that contains the truncated class body. Use the
6037            // wrapper body for fragmented-member detection; retain the
6038            // class-node body for the ordinary (non-fragmented) path below.
6039            if let Some(fragmented) = fragmented {
6040                // The lifted sibling no longer sits below the parser-visible
6041                // namespace node. Restore the current parent scope when the
6042                // ordinary work walk reaches that class.
6043                if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
6044                    .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
6045                {
6046                    let mut boundary_scope = scope.clone();
6047                    for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
6048                        if sibling.start_byte() >= boundary.start_byte() {
6049                            break;
6050                        }
6051                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
6052                            boundary_scope.visible_using_namespaces.push(namespace);
6053                        }
6054                    }
6055                    self.recovered_class_sibling_scopes
6056                        .insert(boundary.id(), boundary_scope);
6057                }
6058                let mut recovered_constructor = None;
6059                let mut recovered_prefix_tree = None;
6060                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
6061                {
6062                    Some(FragmentedExportMembers::Complete(tree)) => {
6063                        if let Some(body) = body
6064                            && let Some(range) =
6065                                cpp_reparsed_synthetic_initializer_constructor_range(
6066                                    tree.root_node(),
6067                                    &name,
6068                                    self.source,
6069                                    body.end_byte(),
6070                                )
6071                        {
6072                            recovered_constructor = Some(range);
6073                            recovered_prefix_tree = Some(tree);
6074                            None
6075                        } else {
6076                            Some(FragmentedExportMembers::Complete(tree))
6077                        }
6078                    }
6079                    outcome => outcome,
6080                };
6081                let mut class_stack = Vec::new();
6082                let class_unit = self.visit_named_class_like_shape(
6083                    class_node,
6084                    name,
6085                    None,
6086                    true,
6087                    Some(fragmented.class_range),
6088                    raw_supertypes,
6089                    scope,
6090                    &mut class_stack,
6091                    ancestry,
6092                );
6093                self.parsed
6094                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
6095                        recovery: fragmented.class_range,
6096                        unit: class_unit.clone(),
6097                    });
6098                let complete = outcome.is_some_and(|outcome| {
6099                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
6100                });
6101                if complete {
6102                    self.consumed_fragment_regions
6103                        .push((node.start_byte(), fragmented.class_range.end_byte));
6104                } else {
6105                    // The reparse can fail when the first constructor or a
6106                    // method body is split into statement-shaped siblings.
6107                    // Keep the recovered class envelope, but do not visit the
6108                    // synthetic wrapper body: its initializer expressions can
6109                    // look like same-named member functions (for example
6110                    // `Token.location(loc)`). Re-own only the original sibling
6111                    // nodes that fall inside the proven class range. Their CST
6112                    // shapes retain the real field/function kinds and ranges.
6113                    let member_scope = ScopeInfo {
6114                        package_name: class_unit.package_name().to_string(),
6115                        module: scope.module.clone(),
6116                        class_unit: Some(class_unit.clone()),
6117                        template_signature: scope.template_signature.clone(),
6118                        template_metadata: None,
6119                        declarations_are_fields: true,
6120                        recovered_specialization_member_scope: false,
6121                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
6122                    };
6123                    for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6124                        if candidate.start_byte() >= fragmented.reparse_end {
6125                            break;
6126                        }
6127                        if cpp_fragment_sibling_is_class_member(
6128                            candidate,
6129                            fragmented.reparse_end,
6130                            self.source,
6131                        ) {
6132                            self.recovered_class_sibling_scopes
6133                                .insert(candidate.id(), member_scope.clone());
6134                        }
6135                    }
6136                    if let Some(range) = recovered_constructor
6137                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
6138                    {
6139                        self.visit_recovered_fragment_prefix_members(
6140                            prefix_tree.root_node(),
6141                            range.start,
6142                            &class_unit,
6143                            scope,
6144                            ancestry,
6145                        );
6146                        self.visit_recovered_fragment_constructor(
6147                            range,
6148                            body,
6149                            class_node,
6150                            &class_unit,
6151                            scope,
6152                            ancestry,
6153                        );
6154                    }
6155                }
6156                if let Some(boundary) = displaced_namespace {
6157                    for item in boundary.namespace_items {
6158                        self.recovered_class_sibling_scopes
6159                            .insert(item.id(), scope.clone());
6160                    }
6161                }
6162                stack.extend(class_stack);
6163                return;
6164            }
6165            let mut stack = Vec::new();
6166            let class_unit = self.visit_named_class_like_shape(
6167                class_node,
6168                name,
6169                body,
6170                body.is_some(),
6171                None,
6172                raw_supertypes,
6173                scope,
6174                &mut stack,
6175                ancestry,
6176            );
6177            self.parsed
6178                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6179                    recovery: cpp_declaration_range(node),
6180                    unit: class_unit,
6181                });
6182            // Issue #1524: the bogus `function_definition` body can run past
6183            // the class's true closing brace (the parse ends it with a
6184            // zero-width `MISSING "}"`), swallowing following namespace-scope
6185            // siblings -- they would index as members of the recovered class.
6186            // When the body's text-balanced close lands before the body's own
6187            // end, re-own the swallowed tail with the outer scope instead.
6188            if let Some(body) = body
6189                && let Some(class_close) = self
6190                    .orphaned_namespaces
6191                    .matching_close_brace(body.start_byte())
6192                && class_close.start_byte < body.end_byte()
6193            {
6194                let split = {
6195                    let mut cursor = body.walk();
6196                    body.named_children(&mut cursor)
6197                        .position(|child| child.start_byte() > class_close.start_byte)
6198                };
6199                if let Some(split) = split {
6200                    // The seeded work is a single Container over the whole
6201                    // body with the class scope; replace it with the bounded
6202                    // head (class scope) plus the swallowed tail (outer
6203                    // scope). Push tail first so the head drains first.
6204                    let seeded = stack.pop();
6205                    match seeded {
6206                        Some(CppWork::Container(container)) => {
6207                            push_cpp_sibling_range(
6208                                body,
6209                                split,
6210                                usize::MAX,
6211                                scope.clone(),
6212                                &mut stack,
6213                            );
6214                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
6215                        }
6216                        // visit_named_class_like_shape always seeds exactly
6217                        // one Container when a body is present.
6218                        _ => unreachable!("exported-class seed is always one Container"),
6219                    }
6220                }
6221            }
6222            while let Some(work) = stack.pop() {
6223                match work {
6224                    CppWork::Container(container) => {
6225                        push_cpp_container_work(container.node, container.scope, &mut stack);
6226                    }
6227                    CppWork::Siblings(siblings) => {
6228                        advance_cpp_siblings(siblings, self.source, &mut stack);
6229                    }
6230                    CppWork::Node(work) => {
6231                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
6232                    }
6233                }
6234            }
6235            return;
6236        }
6237        let recovered_constraint_constructor =
6238            cpp_recovered_template_macro_constructor(node, self.source);
6239        let declarator = recovered_constraint_constructor
6240            .map(|(declarator, _)| declarator)
6241            .or_else(|| node.child_by_field_name("declarator"));
6242        let Some(declarator) = declarator else {
6243            self.visit_malformed_function_definition_container(node, scope, stack);
6244            return;
6245        };
6246        let Some(function_declarator) = extract_function_declarator(declarator) else {
6247            self.visit_malformed_function_definition_container(node, scope, stack);
6248            return;
6249        };
6250        let function = if let Some((_, callable_name)) =
6251            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
6252        {
6253            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
6254        } else {
6255            extract_function_info(function_declarator, self.source, scope)
6256        };
6257        let Some(mut function) = function else {
6258            self.visit_malformed_function_definition_container(node, scope, stack);
6259            return;
6260        };
6261        if let Some((_, template_parameter)) = recovered_constraint_constructor {
6262            function.signature = format!(
6263                "template <{}>{}",
6264                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
6265                function.signature
6266            );
6267        }
6268        let code_unit = function.code_unit(self.file.clone());
6269        // Keep an earlier same-file prototype as another physical occurrence
6270        // of this callable. `CodeUnit` already identifies the role-neutral
6271        // overload, while ranges and signature metadata describe its
6272        // declaration/definition occurrences.
6273        self.add_declaration(code_unit.clone(), node, None, None);
6274        let signature = if recovered_constraint_constructor.is_some() {
6275            normalize_cpp_whitespace(node_text(function_declarator, self.source))
6276        } else {
6277            render_cpp_function_display_signature_from_node(
6278                node,
6279                self.source,
6280                scope.template_signature.as_deref(),
6281                true,
6282                ancestry,
6283            )
6284        };
6285        self.parsed.add_signature_with_metadata(
6286            code_unit.clone(),
6287            cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
6288                .with_declaration_only(false)
6289                .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
6290        );
6291        if let Some(parent) = &scope.class_unit {
6292            self.parsed.add_child(parent.clone(), code_unit);
6293        } else if let Some(module) = &scope.module {
6294            self.parsed.add_child(module.clone(), code_unit);
6295        }
6296        self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
6297    }
6298
6299    /// Recover the namespace lost when tree-sitter promotes an export-macro
6300    /// class definition to a root-level `function_definition`.  Only a
6301    /// body-bearing, top-level recovery may borrow a namespace, and only when
6302    /// one earlier namespace-scope forward declaration proves the identity.
6303    fn scope_for_recovered_exported_class<'tree>(
6304        &mut self,
6305        node: Node<'tree>,
6306        name: &str,
6307        definition_body_present: bool,
6308        scope: &ScopeInfo,
6309        ancestry: &ParentIndex<'tree>,
6310    ) -> ScopeInfo {
6311        if !definition_body_present
6312            || !scope.package_name.is_empty()
6313            || scope.class_unit.is_some()
6314            || !(is_recovered_exported_class_container(node, self.source)
6315                || recover_function_like_export_class_pair(node, self.source).is_some()
6316                || recover_embedded_function_like_export_classes(node, self.source)
6317                    .iter()
6318                    .any(|recovered| recovered.name == name)
6319                || matches!(node.kind(), "declaration" | "field_declaration")
6320                    && recover_exported_class_declaration(node, self.source).is_some()
6321                || matches!(
6322                    node.kind(),
6323                    "class_specifier" | "struct_specifier" | "union_specifier"
6324                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
6325                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
6326                        name_node,
6327                        self.source,
6328                    )))
6329                }) || ancestry.parent(node).is_some_and(|parent| {
6330                    matches!(parent.kind(), "declaration" | "field_declaration")
6331                        && recover_exported_class_declaration(parent, self.source).is_some()
6332                        || is_recovered_exported_class_container(parent, self.source)
6333                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
6334        {
6335            return scope.clone();
6336        }
6337        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
6338        let Some(package_name) = borrowed_namespace
6339            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
6340        else {
6341            return scope.clone();
6342        };
6343
6344        let module = CodeUnit::new_fq(
6345            self.file.clone(),
6346            CodeUnitType::Module,
6347            "",
6348            package_name.clone(),
6349            cpp_namespace_fq(&package_name),
6350        );
6351        let mut recovered = scope.clone();
6352        recovered.package_name = package_name;
6353        recovered.module = Some(module);
6354        recovered
6355    }
6356
6357    /// The unique namespace-scope forward declaration of `name` that precedes
6358    /// `recovered_node`, answered from the walk's carried-forward scan of the
6359    /// tree `recovered_node` belongs to.
6360    ///
6361    /// The scan is built on the first question and advanced by each later one,
6362    /// so a file that never reaches this path -- almost every file -- pays
6363    /// nothing, and one that reaches it thousands of times pays a single pass
6364    /// (#2754).
6365    fn unique_earlier_namespace_forward<'tree>(
6366        &mut self,
6367        recovered_node: Node<'tree>,
6368        name: &str,
6369        ancestry: &ParentIndex<'tree>,
6370    ) -> Option<String> {
6371        let mut root = recovered_node;
6372        while let Some(parent) = ancestry.parent(root) {
6373            root = parent;
6374        }
6375        let source = self.source;
6376        let scan = self
6377            .namespace_forward_scans
6378            .entry(CppTreeIdentity::of(root))
6379            .or_default();
6380        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
6381        let borrowed = scan.unique_earlier_forward(name, recovered_node);
6382
6383        #[cfg(debug_assertions)]
6384        assert_eq!(
6385            borrowed,
6386            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
6387            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
6388             for {name} at byte {}",
6389            recovered_node.start_byte()
6390        );
6391
6392        borrowed
6393    }
6394
6395    fn visit_malformed_function_definition_container<'tree>(
6396        &mut self,
6397        node: Node<'tree>,
6398        scope: &ScopeInfo,
6399        stack: &mut Vec<CppWork<'tree>>,
6400    ) {
6401        let Some(body) = cpp_body_node(node) else {
6402            return;
6403        };
6404        if !cpp_contains_namespace_definition(body) {
6405            return;
6406        }
6407        stack.push(CppWork::Container(CppContainer {
6408            node: body,
6409            scope: scope.clone(),
6410        }));
6411    }
6412
6413    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
6414    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
6415    /// emits for a sentinel-prefixed region, reparse the interior after the
6416    /// sentinel identifier as real C++ items -- confined to the region so
6417    /// every reparsed node keeps its original byte/line position -- and run the
6418    /// ordinary container visitation over the result. Returns `true` when it fired
6419    /// (the caller must then skip normal function processing). Nested sentinel
6420    /// regions recover recursively: the reparsed interior is walked through the
6421    /// same `visit_function_definition` path, so a sentinel inside the region hits
6422    /// this recovery again.
6423    /// Runs `reparse_walk` and records every declaration it mints as a
6424    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
6425    /// `recovery` (issue #1657). A reparsed sentinel region has no single
6426    /// recovered envelope unit: the ordinary visitors mint namespaces,
6427    /// classes, and members directly from the reparsed tree, so the walk's
6428    /// declaration delta is the recovered set. Records are ordered by
6429    /// declaration start byte so the parse product stays deterministic.
6430    fn record_recovered_declarations(
6431        &mut self,
6432        recovery: Range,
6433        reparse_walk: impl FnOnce(&mut Self),
6434    ) {
6435        // The set difference this used to be, kept as the oracle every answer
6436        // is asserted against (#2787).
6437        #[cfg(any(debug_assertions, test))]
6438        let before = self.parsed.declarations().clone();
6439
6440        self.recovery_captures.push(CppRecoveryCapture::default());
6441        reparse_walk(self);
6442        let captured = self
6443            .recovery_captures
6444            .pop()
6445            .expect("the capture this call pushed is the one it pops");
6446
6447        // The capture holds every declaration created while it was open, once
6448        // each and in creation order, so the recovered set costs what the
6449        // recovery made rather than everything the file has declared so far.
6450        // One filter is left to apply: a created declaration that a later
6451        // deferred replacement removed is not in the parse product to report.
6452        let mut minted: Vec<CodeUnit> = captured
6453            .created
6454            .into_iter()
6455            .filter(|unit| self.parsed.contains_declaration(unit))
6456            .collect();
6457        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6458
6459        #[cfg(any(debug_assertions, test))]
6460        {
6461            let mut rediscovered: Vec<CodeUnit> = self
6462                .parsed
6463                .declarations()
6464                .iter()
6465                .filter(|unit| !before.contains(*unit))
6466                .cloned()
6467                .collect();
6468            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6469            assert_eq!(
6470                minted, rediscovered,
6471                "the captured recovered set must be the declaration delta of the reparse \
6472                 walk over {recovery:?}"
6473            );
6474        }
6475
6476        for unit in minted {
6477            self.parsed
6478                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6479                    recovery,
6480                    unit,
6481                });
6482        }
6483    }
6484
6485    /// Where one recovered declaration sorts: by start byte, then by name, so
6486    /// the parse product stays deterministic.
6487    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
6488        let start = self
6489            .parsed
6490            .declaration_ranges(unit)
6491            .first()
6492            .map(|range| range.start_byte)
6493            .unwrap_or(usize::MAX);
6494        (start, unit.fq_name().to_string())
6495    }
6496
6497    fn visit_sentinel_macro_region<'tree>(
6498        &mut self,
6499        node: Node<'tree>,
6500        scope: &ScopeInfo,
6501        stack: &mut Vec<CppWork<'tree>>,
6502        ancestry: &ParentIndex<'tree>,
6503    ) -> bool {
6504        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
6505            return true;
6506        }
6507        if let Some((
6508            reparse_start,
6509            class_start,
6510            body_start,
6511            class_close_start,
6512            class_close_end,
6513            class_close_line,
6514        )) = cpp_sentinel_macro_class_region(node, self.source)
6515        {
6516            let Some(class_tree) =
6517                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
6518            else {
6519                return false;
6520            };
6521            let class_root = class_tree.root_node();
6522            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
6523            // A region reparse is its own tree and needs its own parent index.
6524            let class_ancestry = ParentIndex::new(class_root);
6525            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
6526                class_root,
6527                template_node,
6528                self.source,
6529                &class_ancestry,
6530            ) else {
6531                return false;
6532            };
6533            let class_node = reparsed_class.declaration_node;
6534            let name = reparsed_class.name;
6535            let mut class_scope = scope.clone();
6536            if let Some(template_node) = template_node {
6537                class_scope.template_signature =
6538                    cpp_template_signature(template_node, class_node, self.source);
6539                class_scope.template_metadata =
6540                    cpp_template_metadata(template_node, class_node, self.source, ancestry);
6541            }
6542            let Some(body_tree) =
6543                cpp_reparse_region_items(self.source, body_start, class_close_start)
6544            else {
6545                return false;
6546            };
6547            let raw_supertypes = reparsed_class.raw_supertypes;
6548            let class_range = Range {
6549                start_byte: class_start,
6550                end_byte: class_close_end,
6551                start_line: class_node.start_position().row + 1,
6552                end_line: class_close_line,
6553            };
6554            let class_scope = self.scope_for_recovered_exported_class(
6555                class_node,
6556                &name,
6557                true,
6558                &class_scope,
6559                ancestry,
6560            );
6561            let mut class_stack = Vec::new();
6562            let class_unit = self.visit_named_class_like_shape(
6563                class_node,
6564                name,
6565                None,
6566                true,
6567                Some(class_range),
6568                raw_supertypes,
6569                &class_scope,
6570                &mut class_stack,
6571                ancestry,
6572            );
6573            self.parsed
6574                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6575                    recovery: class_range,
6576                    unit: class_unit.clone(),
6577                });
6578            let member_scope = ScopeInfo {
6579                package_name: class_scope.package_name.clone(),
6580                module: class_scope.module.clone(),
6581                class_unit: Some(class_unit),
6582                template_signature: class_scope.template_signature.clone(),
6583                template_metadata: None,
6584                declarations_are_fields: true,
6585                recovered_specialization_member_scope: false,
6586                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
6587            };
6588            // The padded body reparse is its own tree, so it indexes itself.
6589            let body_root = body_tree.root_node();
6590            self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
6591            // Register only after the padded body reparse: its nodes deliberately
6592            // retain offsets inside the consumed region and must be visited first.
6593            self.consumed_fragment_regions
6594                .push((node.start_byte(), class_close_end));
6595            // An ERROR envelope can hold real sibling declarations after the
6596            // recovered class's close (the suffix-reparse boundary in
6597            // `cpp_sentinel_macro_class_region` partitions, it does not
6598            // consume). Walk the envelope's remaining children normally; the
6599            // consumed region above keeps the recovered class from being
6600            // indexed twice.
6601            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
6602                stack.push(CppWork::Container(CppContainer {
6603                    node,
6604                    scope: scope.clone(),
6605                }));
6606            }
6607            return true;
6608        }
6609        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
6610            return false;
6611        };
6612        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6613            return false;
6614        };
6615        let root = tree.root_node();
6616        if !cpp_reparsed_items_are_indexable(root, self.source) {
6617            return false;
6618        }
6619        let recovery = cpp_recovery_window(self.source, start, end);
6620        // The reparsed region is its own tree, so this walk indexes it itself.
6621        let reparsed_ancestry = ParentIndex::new(root);
6622        self.record_recovered_declarations(recovery, |visitor| {
6623            visitor.visit_container(
6624                root,
6625                &reparsed_ancestry,
6626                &scope.package_name,
6627                scope.module.clone(),
6628                scope.class_unit.clone(),
6629                scope.template_signature.clone(),
6630                scope.visible_using_namespaces.clone(),
6631            );
6632        });
6633        if end > node.end_byte() {
6634            self.consumed_fragment_regions
6635                .push((node.start_byte(), end));
6636        } else if node.kind() == "ERROR" && node.end_byte() > end {
6637            // The sentinel region ended at the first recovered class-like item
6638            // but the ERROR envelope keeps real sibling declarations after it
6639            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
6640            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
6641            // envelope's remaining children normally; the consumed region
6642            // keeps the reparsed prefix from being indexed twice.
6643            self.consumed_fragment_regions
6644                .push((node.start_byte(), end));
6645            stack.push(CppWork::Container(CppContainer {
6646                node,
6647                scope: scope.clone(),
6648            }));
6649        }
6650        true
6651    }
6652
6653    /// Re-own complete class declarations from the structured Abseil
6654    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
6655    /// its direct CST children already prove both namespace components and the
6656    /// class bodies, so the ordinary class/member visitor can retain ownership
6657    /// and exact source ranges without admitting unrelated callable bodies.
6658    fn visit_nested_namespace_sentinel<'tree>(
6659        &mut self,
6660        node: Node<'tree>,
6661        scope: &ScopeInfo,
6662        ancestry: &ParentIndex<'tree>,
6663    ) -> bool {
6664        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
6665            return false;
6666        };
6667
6668        let mut package_name = scope.package_name.clone();
6669        let mut module = scope.module.clone();
6670        for component in recovered.namespace_components {
6671            package_name = if package_name.is_empty() {
6672                component
6673            } else {
6674                format!("{package_name}::{component}")
6675            };
6676            let namespace_module = CodeUnit::new_fq(
6677                self.file.clone(),
6678                CodeUnitType::Module,
6679                "",
6680                package_name.clone(),
6681                cpp_namespace_fq(&package_name),
6682            );
6683            if !self.parsed.contains_declaration(&namespace_module) {
6684                self.add_declaration(namespace_module.clone(), recovered.function, None, None);
6685            }
6686            module = Some(namespace_module);
6687        }
6688
6689        let recovered_scope = ScopeInfo {
6690            package_name,
6691            module,
6692            // This scope's package comes from the namespace levels the sentinel
6693            // shape proves, so its owner must come from the same place. C++
6694            // never nests a namespace inside a class, so a surviving
6695            // `class_unit` here is recovery bleed from an earlier malformed
6696            // region, and keeping it would publish declarations whose package
6697            // names one lexical position and whose owner names another -- the
6698            // hybrid identity that trips the package/short boundary assert in
6699            // `CodeUnit::with_signature_and_fq` (#2306, #2979). Dropping it is
6700            // identity-neutral for valid code, where `class_unit` is always
6701            // empty at a namespace.
6702            class_unit: None,
6703            template_signature: scope.template_signature.clone(),
6704            template_metadata: scope.template_metadata.clone(),
6705            declarations_are_fields: false,
6706            recovered_specialization_member_scope: false,
6707            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6708        };
6709        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
6710            recovered.function,
6711            recovered.body,
6712            self.source,
6713            ancestry,
6714        ) {
6715            let mut class_scope = recovered_scope.clone();
6716            if let Some(template_node) = fragmented.template_node {
6717                class_scope.template_signature =
6718                    cpp_template_signature(template_node, fragmented.class_node, self.source);
6719                class_scope.template_metadata = cpp_template_metadata(
6720                    template_node,
6721                    fragmented.class_node,
6722                    self.source,
6723                    ancestry,
6724                );
6725            }
6726            if let Some(outcome) = self
6727                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
6728            {
6729                let mut class_stack = Vec::new();
6730                let class_unit = self.visit_named_class_like_shape(
6731                    fragmented.class_node,
6732                    fragmented.name.clone(),
6733                    None,
6734                    true,
6735                    Some(fragmented.fragmented.class_range),
6736                    fragmented.raw_supertypes.clone(),
6737                    &class_scope,
6738                    &mut class_stack,
6739                    ancestry,
6740                );
6741                self.parsed
6742                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
6743                        recovery: fragmented.fragmented.class_range,
6744                        unit: class_unit.clone(),
6745                    });
6746                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
6747                    self.consumed_fragment_regions.push((
6748                        fragmented.consumed_start,
6749                        fragmented.fragmented.class_range.end_byte,
6750                    ));
6751                }
6752            }
6753        }
6754        // The class requirement above is the admission gate; once admitted,
6755        // traverse the whole proven inner namespace body so sibling aliases,
6756        // functions, and variables are not silently discarded. The body is a
6757        // node of the tree being walked, so it reuses that tree's index.
6758        self.run_container_work(recovered.body, recovered_scope, ancestry);
6759        true
6760    }
6761
6762    fn visit_declaration<'tree>(
6763        &mut self,
6764        node: Node<'tree>,
6765        scope: &ScopeInfo,
6766        in_class_body: bool,
6767        stack: &mut Vec<CppWork<'tree>>,
6768        ancestry: &ParentIndex<'tree>,
6769    ) {
6770        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6771            return;
6772        }
6773        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
6774            return;
6775        }
6776        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
6777            !cpp_active_template_type_parameter(
6778                node,
6779                node_text(declarator, self.source),
6780                self.source,
6781                ancestry,
6782            )
6783        }) {
6784            return;
6785        }
6786        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
6787            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
6788            // the following `Imaging image` member into one malformed field.
6789            // The ERROR's identifier is the actual declarator; route it
6790            // through the ordinary field path so its parent, range, and
6791            // signature metadata remain consistent with every other member.
6792            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
6793            return;
6794        }
6795        if in_class_body
6796            && let Some(parent) = scope.class_unit.as_ref()
6797            && let Some(call) =
6798                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
6799        {
6800            self.visit_recovered_macro_qualified_constructor_definition(
6801                node, call, scope, ancestry,
6802            );
6803            return;
6804        }
6805        if in_class_body
6806            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
6807        {
6808            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
6809            return;
6810        }
6811        if in_class_body
6812            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
6813        {
6814            for member in members {
6815                self.add_macro_wrapped_declaration(member, scope, ancestry);
6816            }
6817            return;
6818        }
6819        if in_class_body
6820            && let Some(declarators) =
6821                recovered_macro_qualified_field_declarators(node, self.source)
6822        {
6823            for declarator in declarators {
6824                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6825            }
6826            return;
6827        }
6828        let recovered_alias_names = recovered_type_alias_names(node, self.source);
6829        if !recovered_alias_names.is_empty() {
6830            self.add_type_aliases(node, scope, recovered_alias_names, ancestry);
6831            return;
6832        }
6833        if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
6834            return;
6835        }
6836        if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
6837            return;
6838        }
6839
6840        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
6841            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
6842                // Issue #938: the members tree-sitter scattered out of the fragmented
6843                // multiple-base export node are reparsed from their true body region
6844                // and re-owned as members of the recovered class, with an explicit
6845                // navigation range spanning to the displaced closing brace.
6846                if let Some(outcome) =
6847                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
6848                {
6849                    let consumed_region = (
6850                        recovered.declaration_node.end_byte(),
6851                        fragmented.class_range.end_byte,
6852                    );
6853                    let code_unit = self.visit_named_class_like_shape(
6854                        recovered.declaration_node,
6855                        recovered.name,
6856                        None,
6857                        true,
6858                        Some(fragmented.class_range),
6859                        recovered.raw_supertypes,
6860                        scope,
6861                        stack,
6862                        ancestry,
6863                    );
6864                    self.parsed.record_materialization(
6865                        MaterializationRecord::RecoveredDeclaration {
6866                            recovery: fragmented.class_range,
6867                            unit: code_unit.clone(),
6868                        },
6869                    );
6870                    let consume_fragment =
6871                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
6872                    // Everything between the fragmented declaration and its displaced
6873                    // closing brace now belongs to the recovered class; keep the
6874                    // ordinary walk from re-indexing those scattered siblings at top
6875                    // level. Register the consumed region only after indexing because
6876                    // the reparsed nodes retain byte offsets inside that same region.
6877                    if consume_fragment {
6878                        self.consumed_fragment_regions.push(consumed_region);
6879                    }
6880                    return;
6881                }
6882            }
6883            let uses_initializer_body = recovered.uses_initializer_body;
6884            let definition_body_present = recovered.body.is_some();
6885            let class_unit = self.visit_named_class_like_shape(
6886                recovered.declaration_node,
6887                recovered.name,
6888                recovered.body,
6889                definition_body_present,
6890                None,
6891                recovered.raw_supertypes,
6892                scope,
6893                stack,
6894                ancestry,
6895            );
6896            self.parsed
6897                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6898                    recovery: cpp_declaration_range(node),
6899                    unit: class_unit,
6900                });
6901            if uses_initializer_body {
6902                return;
6903            }
6904        }
6905
6906        // A function-like macro invocation that expands to declarations keeps
6907        // no terminator of its own: the semicolons live in the replacement
6908        // body, which is not part of this tree. Tree-sitter recovers one and
6909        // reads the macro name as the member type and its argument list as a
6910        // parenthesized declarator, so `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)`
6911        // published a field named `ClosedDetect` beside the real constructor
6912        // and split every reference to the constructor's name between the two
6913        // (#3298). The invented terminator is the whole difference from a real
6914        // member: a member terminates itself, and with a terminator the
6915        // grammar reads `T (name);` as a declaration rather than a field.
6916        if in_class_body && cpp_unterminated_macro_invocation_field(node) {
6917            return;
6918        }
6919
6920        let mut handled_function = false;
6921        let mut handled_declarator = false;
6922        let mut cursor = node.walk();
6923        for child in node.named_children(&mut cursor) {
6924            if matches!(
6925                child.kind(),
6926                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6927            ) {
6928                // A named class-like definition remains a declaration even when
6929                // the same statement also declares an object, for example
6930                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
6931                // declaration's type and `kind` as its declarator.  Dropping the
6932                // type here loses both its nested owner and every later lexical
6933                // reference to it.  A body is the structured proof that this is
6934                // a definition rather than an elaborated type use such as
6935                // `class Kind value;`.
6936                if cpp_body_node(child).is_some() {
6937                    self.visit_class_like(child, scope, stack, ancestry);
6938                }
6939                continue;
6940            }
6941        }
6942
6943        let mut cursor = node.walk();
6944        for child in node.children_by_field_name("declarator", &mut cursor) {
6945            if crate::structural::is_recovered_designator_init_declarator(child) {
6946                handled_declarator = true;
6947                continue;
6948            }
6949            if in_class_body
6950                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
6951            {
6952                handled_declarator = true;
6953                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
6954                continue;
6955            }
6956            if let Some(kind) = classify_declarator(child) {
6957                handled_declarator = true;
6958                match kind {
6959                    DeclaratorKind::Function(function_declarator) => {
6960                        handled_function = true;
6961                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
6962                    }
6963                    DeclaratorKind::Variable(variable_declarator) => {
6964                        self.visit_variable_declaration(
6965                            node,
6966                            variable_declarator,
6967                            scope,
6968                            in_class_body,
6969                            ancestry,
6970                        );
6971                    }
6972                }
6973            }
6974        }
6975
6976        if !handled_declarator {
6977            let mut cursor = node.walk();
6978            for child in node.named_children(&mut cursor) {
6979                if crate::structural::is_recovered_designator_init_declarator(child) {
6980                    handled_declarator = true;
6981                    continue;
6982                }
6983                if !is_unfielded_declarator_candidate(child) {
6984                    continue;
6985                }
6986                let Some(kind) = classify_declarator(child) else {
6987                    continue;
6988                };
6989                handled_declarator = true;
6990                match kind {
6991                    DeclaratorKind::Function(function_declarator) => {
6992                        handled_function = true;
6993                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
6994                    }
6995                    DeclaratorKind::Variable(variable_declarator) => {
6996                        self.visit_variable_declaration(
6997                            node,
6998                            variable_declarator,
6999                            scope,
7000                            in_class_body,
7001                            ancestry,
7002                        );
7003                    }
7004                }
7005            }
7006        }
7007
7008        if handled_function {
7009            return;
7010        }
7011
7012        if !handled_declarator {
7013            if in_class_body {
7014                self.visit_class_members_from_declaration(node, scope, ancestry);
7015            } else {
7016                self.visit_global_variables_from_declaration(node, scope, ancestry);
7017            }
7018        }
7019    }
7020
7021    /// Preserve the member structure of an anonymous aggregate.
7022    ///
7023    /// An anonymous union with no declarator promotes its fields into the
7024    /// containing aggregate. An anonymous struct/union followed by a named
7025    /// declarator, such as `struct { T *ops; } sock`, declares both the field
7026    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
7027    /// the declarator's structured nested identity so a later `value.sock.ops`
7028    /// chain can traverse it without parsing a type spelling (#2407).
7029    ///
7030    /// An anonymous aggregate declares no tag, so there is nothing for the C
7031    /// dialect to re-scope and nothing for the C++ dialect to nest: both
7032    /// readings of this shape are the same, and both need these members
7033    /// (#3301).
7034    fn visit_anonymous_aggregate_declaration<'tree>(
7035        &mut self,
7036        node: Node<'tree>,
7037        scope: &ScopeInfo,
7038        in_class_body: bool,
7039        stack: &mut Vec<CppWork<'tree>>,
7040        ancestry: &ParentIndex<'tree>,
7041    ) -> bool {
7042        if !in_class_body || scope.class_unit.is_none() {
7043            return false;
7044        }
7045        let Some(aggregate) = node.child_by_field_name("type") else {
7046            return false;
7047        };
7048        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7049            || aggregate.child_by_field_name("name").is_some()
7050        {
7051            return false;
7052        }
7053        let Some(body) = cpp_body_node(aggregate) else {
7054            return false;
7055        };
7056
7057        let mut cursor = node.walk();
7058        let declarators = node
7059            .children_by_field_name("declarator", &mut cursor)
7060            .filter_map(|declarator| match classify_declarator(declarator) {
7061                Some(DeclaratorKind::Variable(variable)) => Some(variable),
7062                Some(DeclaratorKind::Function(_)) | None => None,
7063            })
7064            .collect::<Vec<_>>();
7065        if declarators.is_empty() {
7066            stack.push(CppWork::Container(CppContainer {
7067                node: body,
7068                scope: scope.clone(),
7069            }));
7070            return true;
7071        }
7072
7073        for declarator in declarators {
7074            let Some(name) = extract_variable_name(declarator, self.source) else {
7075                continue;
7076            };
7077            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
7078            // The receiver type's identity is the declarator's name, so its
7079            // range is the field declaration that carries that name token.
7080            // The inner specifier ends at the closing brace, which leaves the
7081            // name the unit is known by outside its own text (#3287).
7082            self.visit_named_class_like_shape(
7083                aggregate,
7084                name,
7085                Some(body),
7086                true,
7087                Some(cpp_declaration_range(node)),
7088                None,
7089                scope,
7090                stack,
7091                ancestry,
7092            );
7093        }
7094        true
7095    }
7096
7097    /// Give a function-local anonymous aggregate a structured owner so its
7098    /// direct pointer bindings can be typed by the usage graph.  Unlike an
7099    /// anonymous aggregate in a class body, there is no source-level tag or
7100    /// typedef to supply an identity.  The aggregate's CST range is therefore
7101    /// part of a generated, collision-resistant identity; no source spelling
7102    /// is consulted.  The declaration's own variable remains a file-level
7103    /// field projection, while the aggregate body is visited as the generated
7104    /// class's field list.
7105    fn visit_anonymous_local_aggregate_declaration<'tree>(
7106        &mut self,
7107        node: Node<'tree>,
7108        scope: &ScopeInfo,
7109        stack: &mut Vec<CppWork<'tree>>,
7110        ancestry: &ParentIndex<'tree>,
7111    ) -> bool {
7112        if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
7113            return false;
7114        }
7115        let Some(aggregate) = node.child_by_field_name("type") else {
7116            return false;
7117        };
7118        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7119            || aggregate.child_by_field_name("name").is_some()
7120        {
7121            return false;
7122        }
7123        let Some(body) = cpp_body_node(aggregate) else {
7124            return false;
7125        };
7126        let mut cursor = node.walk();
7127        let declarators = node
7128            .children_by_field_name("declarator", &mut cursor)
7129            .filter_map(|declarator| match classify_declarator(declarator) {
7130                Some(DeclaratorKind::Variable(variable)) => Some(variable),
7131                Some(DeclaratorKind::Function(_)) | None => None,
7132            })
7133            .collect::<Vec<_>>();
7134        if declarators.is_empty() {
7135            return false;
7136        }
7137
7138        for declarator in &declarators {
7139            self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
7140        }
7141        let name = format!("<anonymous:{}>", aggregate.start_byte());
7142        self.visit_named_class_like_shape(
7143            aggregate,
7144            name,
7145            Some(body),
7146            true,
7147            None,
7148            None,
7149            scope,
7150            stack,
7151            ancestry,
7152        );
7153        true
7154    }
7155
7156    fn visit_function_declaration<'tree>(
7157        &mut self,
7158        declaration_node: Node<'tree>,
7159        declarator: Node<'tree>,
7160        scope: &ScopeInfo,
7161        ancestry: &ParentIndex<'tree>,
7162    ) {
7163        let Some(function) = extract_function_info(declarator, self.source, scope) else {
7164            return;
7165        };
7166        let code_unit =
7167            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7168        if self.parsed.contains_declaration(&code_unit) {
7169            self.parsed
7170                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7171            return;
7172        }
7173        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7174        let signature = render_cpp_function_display_signature_from_node(
7175            declaration_node,
7176            self.source,
7177            scope.template_signature.as_deref(),
7178            false,
7179            ancestry,
7180        );
7181        self.parsed.add_signature_with_metadata(
7182            code_unit.clone(),
7183            cpp_signature_metadata(signature, declarator, self.source, ancestry)
7184                .with_declaration_only(true)
7185                .with_callable_linkage(cpp_callable_linkage(
7186                    declaration_node,
7187                    self.source,
7188                    ancestry,
7189                )),
7190        );
7191        if let Some(parent) = &scope.class_unit {
7192            self.parsed.add_child(parent.clone(), code_unit);
7193        } else if let Some(module) = &scope.module {
7194            self.parsed.add_child(module.clone(), code_unit);
7195        }
7196    }
7197
7198    fn visit_recovered_macro_qualified_function_declaration<'tree>(
7199        &mut self,
7200        declaration_node: Node<'tree>,
7201        call: Node<'tree>,
7202        scope: &ScopeInfo,
7203        ancestry: &ParentIndex<'tree>,
7204    ) {
7205        let Some(parent) = &scope.class_unit else {
7206            return;
7207        };
7208        let Some(name_node) = call.child_by_field_name("function") else {
7209            return;
7210        };
7211        let Some(arguments) = call.child_by_field_name("arguments") else {
7212            return;
7213        };
7214        let Some((signature, parameter_labels)) =
7215            recovered_macro_qualified_function_parameters(arguments, self.source)
7216        else {
7217            return;
7218        };
7219        let arity = parameter_labels.len();
7220        let function = FunctionInfo {
7221            package_name: scope.package_name.clone(),
7222            owner: Some(CppMemberOwner::Unit(parent.clone())),
7223            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
7224            signature,
7225        };
7226        if function.name.is_empty() {
7227            return;
7228        }
7229        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7230        if self.parsed.contains_declaration(&code_unit) {
7231            self.parsed
7232                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7233            return;
7234        }
7235        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7236        let signature_label = render_cpp_function_display_signature_from_node(
7237            declaration_node,
7238            self.source,
7239            scope.template_signature.as_deref(),
7240            false,
7241            ancestry,
7242        );
7243        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7244            .with_declaration_only(true)
7245            .with_callable_arity(CallableArity::exact(arity))
7246            .with_callable_linkage(cpp_callable_linkage(
7247                declaration_node,
7248                self.source,
7249                ancestry,
7250            ));
7251        self.parsed
7252            .add_signature_with_metadata(code_unit.clone(), metadata);
7253        self.parsed.add_child(parent.clone(), code_unit);
7254    }
7255
7256    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
7257        &mut self,
7258        declaration_node: Node<'tree>,
7259        call: Node<'tree>,
7260        scope: &ScopeInfo,
7261        ancestry: &ParentIndex<'tree>,
7262    ) {
7263        let Some(parent) = &scope.class_unit else {
7264            return;
7265        };
7266        let Some(arguments) = call.child_by_field_name("arguments") else {
7267            return;
7268        };
7269        let Some((mut signature, parameter_labels)) =
7270            recovered_macro_qualified_function_parameters(arguments, self.source)
7271        else {
7272            return;
7273        };
7274        if let Some(template_signature) = &scope.template_signature {
7275            signature = format!("{template_signature}{signature}");
7276        }
7277        let arity = parameter_labels.len();
7278        let function = FunctionInfo {
7279            package_name: scope.package_name.clone(),
7280            owner: Some(CppMemberOwner::Unit(parent.clone())),
7281            name: parent.identifier().to_string(),
7282            signature,
7283        };
7284        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7285        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7286        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
7287        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7288            .with_declaration_only(false)
7289            .with_callable_arity(CallableArity::exact(arity))
7290            .with_callable_linkage(cpp_callable_linkage(
7291                declaration_node,
7292                self.source,
7293                ancestry,
7294            ));
7295        self.parsed
7296            .add_signature_with_metadata(code_unit.clone(), metadata);
7297        self.parsed.add_child(parent.clone(), code_unit);
7298    }
7299
7300    fn visit_variable_declaration<'tree>(
7301        &mut self,
7302        declaration_node: Node<'tree>,
7303        declarator: Node<'tree>,
7304        scope: &ScopeInfo,
7305        in_class_body: bool,
7306        ancestry: &ParentIndex<'tree>,
7307    ) {
7308        let Some(name) = extract_variable_name(declarator, self.source) else {
7309            return;
7310        };
7311        let parent = if in_class_body {
7312            let Some(parent) = &scope.class_unit else {
7313                return;
7314            };
7315            Some(parent)
7316        } else {
7317            None
7318        };
7319        let short_name = match parent {
7320            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
7321            None => name.clone(),
7322        };
7323        let fq = cpp_leaf_fq(
7324            &scope.package_name,
7325            parent,
7326            &name,
7327            SegmentKind::Member,
7328            SegmentKind::Member,
7329        );
7330        let code_unit = CodeUnit::new_fq(
7331            self.file.clone(),
7332            CodeUnitType::Field,
7333            scope.package_name.clone(),
7334            short_name,
7335            fq,
7336        );
7337        if self.parsed.contains_declaration(&code_unit) {
7338            return;
7339        }
7340        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7341        self.parsed.add_signature_with_metadata(
7342            code_unit.clone(),
7343            SignatureMetadata::new(
7344                render_cpp_field_signature(declaration_node, declarator, self.source),
7345                Vec::new(),
7346            )
7347            .with_cpp_field_linkage(cpp_field_declaration_linkage(
7348                declaration_node,
7349                self.source,
7350                ancestry,
7351            )),
7352        );
7353        if let Some(parent) = &scope.class_unit {
7354            self.parsed.add_child(parent.clone(), code_unit);
7355        } else if let Some(module) = &scope.module {
7356            self.parsed.add_child(module.clone(), code_unit);
7357        }
7358    }
7359
7360    fn visit_class_members_from_declaration<'tree>(
7361        &mut self,
7362        node: Node<'tree>,
7363        scope: &ScopeInfo,
7364        ancestry: &ParentIndex<'tree>,
7365    ) {
7366        let mut cursor = node.walk();
7367        for child in node.named_children(&mut cursor) {
7368            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
7369                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
7370            } else if child.kind() == "init_declarator"
7371                && let Some(inner) = child.child_by_field_name("declarator")
7372            {
7373                self.visit_variable_declaration(node, inner, scope, true, ancestry);
7374            } else if matches!(
7375                child.kind(),
7376                "identifier"
7377                    | "field_identifier"
7378                    | "pointer_declarator"
7379                    | "reference_declarator"
7380                    | "array_declarator"
7381                    | "parenthesized_declarator"
7382            ) {
7383                self.visit_variable_declaration(node, child, scope, true, ancestry);
7384            }
7385        }
7386    }
7387
7388    fn visit_global_variables_from_declaration<'tree>(
7389        &mut self,
7390        node: Node<'tree>,
7391        scope: &ScopeInfo,
7392        ancestry: &ParentIndex<'tree>,
7393    ) {
7394        let mut cursor = node.walk();
7395        for child in node.named_children(&mut cursor) {
7396            if child.kind() == "init_declarator"
7397                && let Some(inner) = child.child_by_field_name("declarator")
7398            {
7399                self.visit_variable_declaration(node, inner, scope, false, ancestry);
7400            } else if matches!(
7401                child.kind(),
7402                "identifier"
7403                    | "field_identifier"
7404                    | "pointer_declarator"
7405                    | "reference_declarator"
7406                    | "array_declarator"
7407                    | "parenthesized_declarator"
7408            ) {
7409                self.visit_variable_declaration(node, child, scope, false, ancestry);
7410            }
7411        }
7412    }
7413
7414    fn visit_type_declaration<'tree>(
7415        &mut self,
7416        node: Node<'tree>,
7417        scope: &ScopeInfo,
7418        stack: &mut Vec<CppWork<'tree>>,
7419        ancestry: &ParentIndex<'tree>,
7420    ) {
7421        let type_node = node.child_by_field_name("type");
7422        if let Some(type_node) = type_node
7423            && matches!(
7424                type_node.kind(),
7425                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7426            )
7427        {
7428            self.visit_class_like(type_node, scope, stack, ancestry);
7429        }
7430
7431        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
7432            let range = Range {
7433                start_byte: node.start_byte(),
7434                end_byte: recovered.end_node.end_byte(),
7435                start_line: node.start_position().row + 1,
7436                end_line: recovered.end_node.end_position().row + 1,
7437            };
7438            let signature = self
7439                .source
7440                .get(range.start_byte..range.end_byte)
7441                .map(normalize_cpp_whitespace)
7442                .unwrap_or_default();
7443            self.record_type_aliases(
7444                node,
7445                scope,
7446                vec![recovered.name],
7447                signature,
7448                range,
7449                ancestry,
7450            );
7451            return;
7452        }
7453
7454        let alias_names = match node.kind() {
7455            "alias_declaration" => extract_alias_declaration_name(node, self.source)
7456                .into_iter()
7457                .collect::<Vec<_>>(),
7458            "type_definition" => extract_typedef_alias_names(node, self.source),
7459            _ => Vec::new(),
7460        };
7461        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
7462            (type_node, alias_names.as_slice())
7463            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
7464            && type_node.child_by_field_name("name").is_none()
7465        {
7466            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
7467        } else {
7468            None
7469        };
7470        self.add_type_aliases(node, scope, alias_names, ancestry);
7471        if let Some((body, alias_name)) = anonymous_aggregate {
7472            // The typedef alias is also the only user-visible identity of an
7473            // anonymous aggregate. Reuse it as the member owner instead of
7474            // minting a second signatureless class with the same FQN. The
7475            // latter makes forward lookup ambiguous when conditional aliases
7476            // coexist and returns duplicate definitions even without guards.
7477            let signature = normalize_cpp_whitespace(node_text(node, self.source));
7478            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
7479            debug_assert!(self.parsed.contains_declaration(&alias_unit));
7480            let mut nested_scope = scope.clone();
7481            nested_scope.class_unit = Some(alias_unit);
7482            nested_scope.template_signature = scope.template_signature.clone();
7483            nested_scope.template_metadata = None;
7484            nested_scope.declarations_are_fields = false;
7485            nested_scope.recovered_specialization_member_scope = false;
7486            stack.push(CppWork::Container(CppContainer {
7487                node: body,
7488                scope: nested_scope,
7489            }));
7490        }
7491    }
7492
7493    fn add_type_aliases(
7494        &mut self,
7495        node: Node<'_>,
7496        scope: &ScopeInfo,
7497        alias_names: Vec<String>,
7498        ancestry: &ParentIndex<'_>,
7499    ) {
7500        let signature = normalize_cpp_whitespace(node_text(node, self.source));
7501        self.record_type_aliases(
7502            node,
7503            scope,
7504            alias_names,
7505            signature,
7506            cpp_declaration_range(node),
7507            ancestry,
7508        );
7509    }
7510
7511    fn record_type_aliases(
7512        &mut self,
7513        node: Node<'_>,
7514        scope: &ScopeInfo,
7515        alias_names: Vec<String>,
7516        signature: String,
7517        range: Range,
7518        ancestry: &ParentIndex<'_>,
7519    ) {
7520        if signature.is_empty() {
7521            return;
7522        }
7523        let type_name = node
7524            .child_by_field_name("type")
7525            .and_then(|type_node| type_node.child_by_field_name("name"))
7526            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
7527        for alias_name in alias_names {
7528            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
7529                continue;
7530            }
7531            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
7532            // Declaration identity does not include the alias signature. Keep
7533            // each physical range so conditional aliases retain their guards.
7534            self.add_declaration_with_range(code_unit.clone(), range, None, None);
7535            let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
7536            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
7537                cpp_structured_type_identity(type_node, self.source, &lexical_scope)
7538            });
7539            self.parsed.add_signature_with_metadata(
7540                code_unit.clone(),
7541                SignatureMetadata::new(signature.clone(), Vec::new())
7542                    .with_underlying_type_identity(underlying_type_identity),
7543            );
7544            if let Some(metadata) = &scope.template_metadata {
7545                let mut metadata = metadata.clone();
7546                metadata.primary_fq_name = code_unit.fq_name();
7547                self.parsed
7548                    .set_cpp_template_metadata(code_unit.clone(), metadata);
7549            }
7550            if let Some(parent) = &scope.class_unit {
7551                self.parsed.add_child(parent.clone(), code_unit.clone());
7552            } else if let Some(module) = &scope.module {
7553                self.parsed.add_child(module.clone(), code_unit.clone());
7554            }
7555            self.parsed.mark_type_alias(code_unit);
7556        }
7557    }
7558
7559    fn type_alias_unit(
7560        &self,
7561        scope: &ScopeInfo,
7562        alias_name: String,
7563        signature: String,
7564    ) -> CodeUnit {
7565        let short_name = if let Some(parent) = &scope.class_unit {
7566            cpp_join_nested_short(parent.short_name(), &alias_name)
7567        } else {
7568            alias_name.clone()
7569        };
7570        let fq = cpp_leaf_fq(
7571            &scope.package_name,
7572            scope.class_unit.as_ref(),
7573            &alias_name,
7574            SegmentKind::Nested,
7575            SegmentKind::Type,
7576        );
7577        CodeUnit::with_signature_and_fq(
7578            self.file.clone(),
7579            CodeUnitType::Class,
7580            scope.package_name.clone(),
7581            short_name,
7582            Some(signature),
7583            false,
7584            fq,
7585        )
7586    }
7587
7588    fn visit_macro(&mut self, node: Node<'_>) {
7589        // A comment can terminate tree-sitter's preproc_arg before the logical
7590        // directive ends. Its remaining declarations are replacement locals,
7591        // not file-scope fields, even when recovery exposes them as siblings.
7592        if let Some(replacement) =
7593            crate::graph::syntax::function_macro_replacement_span(node, self.source)
7594        {
7595            self.consumed_fragment_regions
7596                .push((replacement.start, replacement.end));
7597        }
7598        let Some(name) = extract_macro_name(node, self.source) else {
7599            return;
7600        };
7601        let signature = node_text(node, self.source).trim_end().to_string();
7602        if signature.is_empty() {
7603            return;
7604        }
7605        let fq = cpp_member_fq("", &name);
7606        // A macro can be undefined and redefined later in the same file. Its
7607        // structured directive is part of the declaration identity so the
7608        // temporal environment can navigate to the definition active at a
7609        // reference instead of collapsing every spelling to the first range.
7610        // The same physical directive parsed through another C/C++ reading
7611        // still produces the same unit and remains deduplicated.
7612        let code_unit = CodeUnit::with_signature_and_fq(
7613            self.file.clone(),
7614            CodeUnitType::Macro,
7615            "",
7616            name.clone(),
7617            Some(signature.clone()),
7618            false,
7619            fq,
7620        );
7621        if !self.parsed.contains_declaration(&code_unit) {
7622            self.add_declaration(code_unit.clone(), node, None, None);
7623            let name_range = node
7624                .child_by_field_name("name")
7625                .map(cpp_declaration_range)
7626                .unwrap_or_else(|| cpp_declaration_range(node));
7627            self.parsed
7628                .record_materialization(MaterializationRecord::GeneratedDeclaration {
7629                    site: cpp_declaration_range(node),
7630                    argument: name_range,
7631                    kind: GenerationKind::PreprocessorDefinition,
7632                    unit: code_unit.clone(),
7633                });
7634            self.parsed.add_signature(code_unit, signature);
7635        }
7636        if node.kind() == "preproc_def" {
7637            update_object_macro_field_environment(
7638                node,
7639                self.source,
7640                &mut self.object_macro_fields,
7641                &mut self.ambiguous_object_macro_fields,
7642            );
7643        } else {
7644            self.object_macro_fields.remove(&name);
7645            self.ambiguous_object_macro_fields.remove(&name);
7646        }
7647    }
7648
7649    fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7650        let Some(directive) = node.child_by_field_name("directive") else {
7651            return;
7652        };
7653        let name = node_text(directive, self.source).trim();
7654        let range = cpp_declaration_range(node);
7655        let fields = object_macro_field_closure(&self.object_macro_fields, name);
7656        self.materialize_object_macro_fields(fields, range, scope);
7657    }
7658
7659    /// Bare object-like field-list macros inside an otherwise well-formed
7660    /// aggregate remain identifier nodes in a field declaration. More than
7661    /// one adjacent invocation can be folded into the same declaration, so
7662    /// inspect all of its structured identifier nodes.
7663    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7664        if !matches!(node.kind(), "declaration" | "field_declaration") {
7665            return false;
7666        }
7667        // Claiming the declaration means its members are accounted for, which
7668        // is only true when there is an owner to materialize them into. A file
7669        // scope reaches this with a declaration that merely *contains* an
7670        // aggregate spelling an invocation (tree-sitter wraps libuv's
7671        // `struct uv_fs_poll_s { UV_HANDLE_FIELDS ... }` in a top-level
7672        // `field_declaration` under a malformed header), and consuming it there
7673        // would discard the aggregate itself (#2985).
7674        if scope.class_unit.is_none() {
7675            return false;
7676        }
7677        let macro_nodes =
7678            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7679        for macro_node in &macro_nodes {
7680            let name = node_text(*macro_node, self.source).trim();
7681            let fields = object_macro_field_closure(&self.object_macro_fields, name);
7682            self.materialize_object_macro_fields(fields, cpp_declaration_range(*macro_node), scope);
7683        }
7684        let Some(last) = macro_nodes.last() else {
7685            return false;
7686        };
7687        // Claiming the declaration accounts for every member it spells, not
7688        // only the donated ones: an invocation folded together with the
7689        // members after it (libuv's `UV_HANDLE_FIELDS ... void* poll_ctx;`)
7690        // leaves those members shapeless, and the grammar recovers them from
7691        // the byte range that follows the invocation.
7692        self.record_collapsed_aggregate_fields(
7693            last.end_byte()..node.end_byte(),
7694            node.start_position().row
7695                + 1
7696                + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
7697            scope,
7698        );
7699        true
7700    }
7701
7702    fn materialize_object_macro_fields(
7703        &mut self,
7704        fields: Vec<MacroReplacementField>,
7705        range: Range,
7706        scope: &ScopeInfo,
7707    ) {
7708        let Some(owner) = scope.class_unit.as_ref() else {
7709            return;
7710        };
7711        for field in fields {
7712            let signature = field.declaration.clone();
7713            let mut fq = owner.fq().clone();
7714            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7715            let short_name = if owner.short_name().is_empty() {
7716                field.name.clone()
7717            } else {
7718                format!("{}.{}", owner.short_name(), field.name)
7719            };
7720            let code_unit = CodeUnit::with_signature_and_fq(
7721                self.file.clone(),
7722                CodeUnitType::Field,
7723                owner.package_name().to_string(),
7724                short_name,
7725                Some(field.declaration),
7726                true,
7727                fq,
7728            );
7729            if self.parsed.contains_declaration(&code_unit) {
7730                continue;
7731            }
7732            self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7733            self.parsed.add_signature(code_unit, signature);
7734        }
7735    }
7736
7737    /// Recover ordinary aggregates whose field-list macro invocations made
7738    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
7739    /// The class markers, aggregate braces, field nodes, and macro identifier
7740    /// nodes are all read from the AST; no source delimiter or token scan is
7741    /// used. A close-brace node inside a malformed field closes the innermost
7742    /// recovered aggregate, preserving nested-owner identity.
7743    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7744        let mut cursor = node.walk();
7745        let children = node.children(&mut cursor).collect::<Vec<_>>();
7746        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember>)>::new();
7747        let mut object_macro_fields = self.object_macro_fields.clone();
7748        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
7749        let mut open = Vec::<usize>::new();
7750        let mut index = 0;
7751        while index < children.len() {
7752            let keyword = children[index];
7753            if update_object_macro_field_environment(
7754                keyword,
7755                self.source,
7756                &mut object_macro_fields,
7757                &mut ambiguous_object_macro_fields,
7758            ) {
7759                index += 1;
7760                continue;
7761            }
7762            if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
7763                let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7764                if !name.is_empty() {
7765                    let parent = open
7766                        .last()
7767                        .and_then(|class| recovered.get(*class))
7768                        .map(|(owner, _, _, _)| owner.clone())
7769                        .or_else(|| scope.class_unit.clone());
7770                    let short_name = parent.as_ref().map_or_else(
7771                        || name.clone(),
7772                        |parent| cpp_join_nested_short(parent.short_name(), &name),
7773                    );
7774                    let fq = cpp_leaf_fq(
7775                        &scope.package_name,
7776                        parent.as_ref(),
7777                        &name,
7778                        SegmentKind::Nested,
7779                        SegmentKind::Type,
7780                    );
7781                    let owner = CodeUnit::with_signature_and_fq(
7782                        self.file.clone(),
7783                        CodeUnitType::Class,
7784                        scope.package_name.clone(),
7785                        short_name,
7786                        None,
7787                        false,
7788                        fq,
7789                    );
7790                    recovered.push((
7791                        owner,
7792                        head.key.start_byte(),
7793                        head.opening.end_byte(),
7794                        Vec::new(),
7795                    ));
7796                    let class_index = recovered.len() - 1;
7797                    match head.folded_members {
7798                        // The ordinary shape leaves the aggregate open: every
7799                        // child that follows belongs to it until a close brace
7800                        // ends it.
7801                        None => open.push(class_index),
7802                        // A folded head carries its own member list and its own
7803                        // closing brace, so it closes here. The invocations that
7804                        // precede the class key are the dangling tail of the
7805                        // replacement that folded it, and belong to whatever
7806                        // aggregate is open around it, not to this one.
7807                        Some(members) => {
7808                            let macro_nodes = object_macro_identifier_nodes_with_environment(
7809                                children[index],
7810                                self.source,
7811                                &mut object_macro_fields,
7812                                &mut ambiguous_object_macro_fields,
7813                            );
7814                            let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7815                                .iter()
7816                                .partition(|node| node.start_byte() < head.key.start_byte());
7817                            if let Some(&enclosing) = open.last() {
7818                                for macro_node in preceding {
7819                                    recovered[enclosing]
7820                                        .3
7821                                        .push(CppCollapsedMember::MacroFields {
7822                                            range: cpp_declaration_range(macro_node),
7823                                            fields: object_macro_field_closure(
7824                                                &object_macro_fields,
7825                                                &normalize_cpp_whitespace(node_text(
7826                                                    macro_node,
7827                                                    self.source,
7828                                                )),
7829                                            ),
7830                                        });
7831                                }
7832                            }
7833                            let closing = cpp_collapsed_aggregate_closing_brace(members);
7834                            recovered[class_index]
7835                                .3
7836                                .extend(cpp_collapsed_aggregate_members(
7837                                    &inner,
7838                                    head.opening.end_byte()..closing,
7839                                    head.opening.end_position().row + 1,
7840                                    self.source,
7841                                    &object_macro_fields,
7842                                ));
7843                            recovered[class_index].2 = members.end_byte();
7844                            for &open_class in &open {
7845                                recovered[open_class].2 =
7846                                    recovered[open_class].2.max(members.end_byte());
7847                            }
7848                        }
7849                    }
7850                    index += head.width;
7851                    continue;
7852                }
7853            }
7854            if let Some(&class_index) = open.last()
7855                && children[index].kind() == "field_declaration"
7856            {
7857                let field = children[index];
7858                let macro_nodes = object_macro_identifier_nodes_with_environment(
7859                    field,
7860                    self.source,
7861                    &mut object_macro_fields,
7862                    &mut ambiguous_object_macro_fields,
7863                );
7864                recovered[class_index]
7865                    .3
7866                    .extend(cpp_collapsed_aggregate_members(
7867                        &macro_nodes,
7868                        field.start_byte()..field.end_byte(),
7869                        field.start_position().row + 1,
7870                        self.source,
7871                        &object_macro_fields,
7872                    ));
7873                let end = field.end_byte();
7874                for &open_class in &open {
7875                    recovered[open_class].2 = recovered[open_class].2.max(end);
7876                }
7877                let closes = count_close_brace_nodes(field);
7878                for _ in 0..closes {
7879                    if let Some(closed) = open.pop() {
7880                        recovered[closed].2 = end;
7881                    }
7882                }
7883            }
7884            index += 1;
7885        }
7886
7887        let mut owners = Vec::with_capacity(recovered.len());
7888        for (owner, start, end, members) in recovered {
7889            let parent = owners
7890                .iter()
7891                .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
7892                .cloned()
7893                .or_else(|| scope.class_unit.clone());
7894            self.declare_collapsed_aggregate(owner.clone(), start..end, parent, members, scope);
7895            owners.push(owner);
7896        }
7897    }
7898
7899    /// Record one recovered aggregate and the members it declares.
7900    fn declare_collapsed_aggregate(
7901        &mut self,
7902        owner: CodeUnit,
7903        span: std::ops::Range<usize>,
7904        parent: Option<CodeUnit>,
7905        members: Vec<CppCollapsedMember>,
7906        scope: &ScopeInfo,
7907    ) {
7908        let range = Range {
7909            start_byte: span.start,
7910            end_byte: span.end,
7911            start_line: self.source.get(..span.start).map_or(1, |source| {
7912                source.bytes().filter(|byte| *byte == b'\n').count() + 1
7913            }),
7914            end_line: self.source.get(..span.end).map_or(1, |source| {
7915                source.bytes().filter(|byte| *byte == b'\n').count() + 1
7916            }),
7917        };
7918        // The tag can already be declared by a typedef or a forward
7919        // declaration, and this range is the definition's. Record it either
7920        // way: a declaration carries every range it is written at, and
7921        // dropping this one leaves nothing able to prove that the aggregate
7922        // has a body (#3098). Recording a range that is already held, or a
7923        // unit that is already declared, changes nothing.
7924        self.add_declaration_with_range(owner.clone(), range, parent, None);
7925        let owner_scope = ScopeInfo {
7926            class_unit: Some(owner),
7927            declarations_are_fields: true,
7928            ..scope.clone()
7929        };
7930        for member in members {
7931            match member {
7932                CppCollapsedMember::MacroFields { range, fields } => {
7933                    self.materialize_object_macro_fields(fields, range, &owner_scope);
7934                }
7935                CppCollapsedMember::Declarations { span, start_line } => {
7936                    self.record_collapsed_aggregate_fields(span, start_line, &owner_scope);
7937                }
7938            }
7939        }
7940    }
7941
7942    /// Recover an aggregate whose head tree-sitter folded into the declaration
7943    /// that precedes it, outside any collapsed `ERROR` container.
7944    ///
7945    /// A comment inside a field-list macro's replacement ends the replacement
7946    /// token, so the rest of the replacement is read at file scope. When the
7947    /// aggregate that follows declares its member list with one invocation,
7948    /// the parser reads the dangling tail as that declaration's type, the
7949    /// class key as an `ERROR`, and the member list as an initializer (#3098).
7950    /// The invocations before the class key belong to the replacement, not to
7951    /// this aggregate, so they go to whatever aggregate encloses it.
7952    fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7953        if node.parent().is_some_and(|parent| parent.is_error()) {
7954            // The container's own recovery reads this child in source order,
7955            // with the preprocessor environment its earlier children build.
7956            return false;
7957        }
7958        let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
7959            return false;
7960        };
7961        let members = head
7962            .folded_members
7963            .expect("a folded aggregate head carries its member list");
7964        let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7965        if name.is_empty() {
7966            return false;
7967        }
7968        let macro_nodes =
7969            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7970        let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7971            .iter()
7972            .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
7973        for macro_node in preceding {
7974            let fields = object_macro_field_closure(
7975                &self.object_macro_fields,
7976                &normalize_cpp_whitespace(node_text(macro_node, self.source)),
7977            );
7978            self.materialize_object_macro_fields(fields, cpp_declaration_range(macro_node), scope);
7979        }
7980        let parent = scope.class_unit.clone();
7981        let short_name = parent.as_ref().map_or_else(
7982            || name.clone(),
7983            |parent| cpp_join_nested_short(parent.short_name(), &name),
7984        );
7985        let fq = cpp_leaf_fq(
7986            &scope.package_name,
7987            parent.as_ref(),
7988            &name,
7989            SegmentKind::Nested,
7990            SegmentKind::Type,
7991        );
7992        let owner = CodeUnit::with_signature_and_fq(
7993            self.file.clone(),
7994            CodeUnitType::Class,
7995            scope.package_name.clone(),
7996            short_name,
7997            None,
7998            false,
7999            fq,
8000        );
8001        let recovered = cpp_collapsed_aggregate_members(
8002            &inner,
8003            head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
8004            head.opening.end_position().row + 1,
8005            self.source,
8006            &self.object_macro_fields,
8007        );
8008        self.declare_collapsed_aggregate(
8009            owner,
8010            head.key.start_byte()..members.end_byte(),
8011            parent,
8012            recovered,
8013            scope,
8014        );
8015        true
8016    }
8017
8018    /// Record the members of a collapsed aggregate region as fields of its
8019    /// recovered owner. The names, declaration text, and byte ranges all come
8020    /// from the grammar's reading of the region, so a region that is not a
8021    /// member list contributes nothing.
8022    fn record_collapsed_aggregate_fields(
8023        &mut self,
8024        span: std::ops::Range<usize>,
8025        start_line: usize,
8026        scope: &ScopeInfo,
8027    ) {
8028        let Some(owner) = scope.class_unit.as_ref() else {
8029            return;
8030        };
8031        for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
8032            let range = Range {
8033                start_byte: field.range.start,
8034                end_byte: field.range.end,
8035                start_line: start_line
8036                    + cpp_line_breaks_between(self.source, span.start, field.range.start),
8037                end_line: start_line
8038                    + cpp_line_breaks_between(self.source, span.start, field.range.end),
8039            };
8040            let mut fq = owner.fq().clone();
8041            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
8042            let short_name = if owner.short_name().is_empty() {
8043                field.name.clone()
8044            } else {
8045                format!("{}.{}", owner.short_name(), field.name)
8046            };
8047            let signature = normalize_cpp_whitespace(&field.declaration);
8048            let code_unit = CodeUnit::with_signature_and_fq(
8049                self.file.clone(),
8050                CodeUnitType::Field,
8051                owner.package_name().to_string(),
8052                short_name,
8053                Some(signature.clone()),
8054                false,
8055                fq,
8056            );
8057            if self.parsed.contains_declaration(&code_unit) {
8058                continue;
8059            }
8060            self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
8061            self.parsed.add_signature(code_unit, signature);
8062        }
8063    }
8064
8065    fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
8066        let Some(_directive) = node.child_by_field_name("directive") else {
8067            return;
8068        };
8069        if is_cpp_undef_directive(node, self.source) {
8070            update_object_macro_field_environment(
8071                node,
8072                self.source,
8073                &mut self.object_macro_fields,
8074                &mut self.ambiguous_object_macro_fields,
8075            );
8076            return;
8077        }
8078        let directly_in_field_list = node
8079            .parent()
8080            .is_some_and(|parent| parent.kind() == "field_declaration_list");
8081        if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
8082            self.visit_object_macro_fields(node, scope);
8083        }
8084    }
8085}
8086
8087/// Every member a field-list macro contributes under `environment`, including
8088/// the members of the field-list macros it composes.
8089///
8090/// Composition is resolved at the invocation, not at the definition, because a
8091/// nested name's active replacement is a property of the environment the
8092/// invocation sees. The traversal is iterative and visits each name once, so a
8093/// self-composing or mutually composing pair terminates.
8094fn object_macro_field_closure(
8095    environment: &HashMap<String, ObjectMacroReplacement>,
8096    name: &str,
8097) -> Vec<MacroReplacementField> {
8098    let mut fields = Vec::new();
8099    let mut visited = HashSet::default();
8100    let mut stack = vec![name.to_string()];
8101    while let Some(current) = stack.pop() {
8102        if !visited.insert(current.clone()) {
8103            continue;
8104        }
8105        let Some(replacement) = environment.get(&current) else {
8106            continue;
8107        };
8108        fields.extend(replacement.fields.iter().cloned());
8109        stack.extend(replacement.nested.iter().rev().cloned());
8110    }
8111    fields
8112}
8113
8114/// The structured replacement an object-like `#define` contributes.
8115///
8116/// The directive's whole logical line is the replacement list, which
8117/// [`object_macro_replacement_span`] recovers, because a comment inside the
8118/// replacement ends tree-sitter's `preproc_arg` token early.
8119fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
8120    crate::graph::syntax::object_macro_replacement_span(node, source)
8121        .and_then(|span| source.get(span))
8122        .map(crate::graph::syntax::object_macro_replacement)
8123        .unwrap_or_default()
8124}
8125
8126/// Apply one preprocessor directive to an object-like field-list environment.
8127/// The environment is intentionally separate from the declaration visitor so
8128/// malformed parser regions can replay directives in source order without
8129/// mutating the live walk's state ahead of those directives.
8130fn update_object_macro_field_environment(
8131    node: Node<'_>,
8132    source: &str,
8133    fields: &mut HashMap<String, ObjectMacroReplacement>,
8134    ambiguous: &mut HashSet<String>,
8135) -> bool {
8136    match node.kind() {
8137        "preproc_def" => {
8138            let Some(name) = extract_macro_name(node, source) else {
8139                return false;
8140            };
8141            let replacement = object_macro_replacement_of(node, source);
8142            if replacement.is_empty() || ambiguous.contains(&name) {
8143                fields.remove(&name);
8144                ambiguous.insert(name);
8145            } else if let Some(previous) = fields.get(&name) {
8146                if previous != &replacement {
8147                    fields.remove(&name);
8148                    ambiguous.insert(name);
8149                }
8150            } else {
8151                fields.insert(name, replacement);
8152            }
8153            true
8154        }
8155        "preproc_call" if is_cpp_undef_directive(node, source) => {
8156            if let Some(argument) = node.child_by_field_name("argument") {
8157                let name = node_text(argument, source).trim();
8158                fields.remove(name);
8159                if inside_preprocessor_conditional(node) {
8160                    ambiguous.insert(name.to_string());
8161                } else {
8162                    ambiguous.remove(name);
8163                }
8164            }
8165            true
8166        }
8167        _ => false,
8168    }
8169}
8170
8171fn object_macro_identifier_nodes<'tree>(
8172    node: Node<'tree>,
8173    source: &str,
8174    fields: &HashMap<String, ObjectMacroReplacement>,
8175) -> Vec<Node<'tree>> {
8176    let mut result = Vec::new();
8177    let mut stack = vec![node];
8178    while let Some(current) = stack.pop() {
8179        if matches!(
8180            current.kind(),
8181            "identifier" | "field_identifier" | "type_identifier"
8182        ) && fields.contains_key(node_text(current, source).trim())
8183        {
8184            result.push(current);
8185        }
8186        let mut cursor = current.walk();
8187        let children = current.children(&mut cursor).collect::<Vec<_>>();
8188        stack.extend(children.into_iter().rev());
8189    }
8190    result.sort_by_key(|node| node.start_byte());
8191    result
8192}
8193
8194/// Collect macro identifiers while replaying any nested preprocessor
8195/// directives in source order. Recovery regions can contain a later `#undef`
8196/// in the same parser error envelope; using the live visitor map for the
8197/// entire envelope would incorrectly materialize invocations after it.
8198fn object_macro_identifier_nodes_with_environment<'tree>(
8199    node: Node<'tree>,
8200    source: &str,
8201    fields: &mut HashMap<String, ObjectMacroReplacement>,
8202    ambiguous: &mut HashSet<String>,
8203) -> Vec<Node<'tree>> {
8204    let mut result = Vec::new();
8205    let mut stack = vec![node];
8206    while let Some(current) = stack.pop() {
8207        if update_object_macro_field_environment(current, source, fields, ambiguous) {
8208            continue;
8209        }
8210        if matches!(
8211            current.kind(),
8212            "identifier" | "field_identifier" | "type_identifier"
8213        ) && fields.contains_key(node_text(current, source).trim())
8214        {
8215            result.push(current);
8216        }
8217        let mut cursor = current.walk();
8218        let children = current.children(&mut cursor).collect::<Vec<_>>();
8219        stack.extend(children.into_iter().rev());
8220    }
8221    result.sort_by_key(|node| node.start_byte());
8222    result
8223}
8224
8225/// One member region of an aggregate whose head and body tree-sitter collapsed
8226/// into an `ERROR` container, in source order.
8227enum CppCollapsedMember {
8228    /// The members a field-list macro invocation donates, all sharing the
8229    /// invocation's range.
8230    MacroFields {
8231        range: Range,
8232        fields: Vec<MacroReplacementField>,
8233    },
8234    /// A byte span whose ordinary member declarations lost their shape with
8235    /// the aggregate body, with the 1-based line the span starts on.
8236    Declarations {
8237        span: std::ops::Range<usize>,
8238        start_line: usize,
8239    },
8240}
8241
8242/// One aggregate head recovered from the children of a collapsed `ERROR`
8243/// container.
8244///
8245/// A member list that opens with an object-like field-list macro invocation
8246/// has no grammar rule, so the aggregate never forms a specifier and its head
8247/// reaches the tree as loose children. Two shapes carry it. Ordinarily the
8248/// class key, the name, and the opening brace are three consecutive children.
8249/// When the aggregate follows the dangling tail of a comment-truncated
8250/// replacement, the parser reads that tail as the declaration's type and folds
8251/// the whole aggregate into it: libuv's `UV_HANDLE_PRIVATE_FIELDS` line ends
8252/// the `UV_HANDLE_FIELDS` replacement token, and the `struct uv_handle_s { ...
8253/// }` that follows becomes one `field_declaration` whose class key is an
8254/// `ERROR`, whose name is a `field_identifier`, and whose member list is an
8255/// `initializer_list` (#3098).
8256struct CppCollapsedAggregateHead<'tree> {
8257    key: Node<'tree>,
8258    name: Node<'tree>,
8259    opening: Node<'tree>,
8260    /// The member list when the head folded it into its own node, which also
8261    /// carries the aggregate's closing brace. `None` leaves the aggregate open
8262    /// for the children that follow it.
8263    folded_members: Option<Node<'tree>>,
8264    /// How many children the head consumes.
8265    width: usize,
8266}
8267
8268/// The aggregate head that starts at `children[index]`, if one does.
8269fn cpp_collapsed_aggregate_head<'tree>(
8270    children: &[Node<'tree>],
8271    index: usize,
8272    source: &str,
8273) -> Option<CppCollapsedAggregateHead<'tree>> {
8274    let key = *children.get(index)?;
8275    if matches!(key.kind(), "struct" | "class" | "union") {
8276        let name = *children.get(index + 1)?;
8277        let opening = *children.get(index + 2)?;
8278        if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
8279            return None;
8280        }
8281        return Some(CppCollapsedAggregateHead {
8282            key,
8283            name,
8284            opening,
8285            folded_members: None,
8286            width: 3,
8287        });
8288    }
8289    cpp_folded_aggregate_head(key, source)
8290}
8291
8292/// The aggregate head tree-sitter folded into one declaration node, reading
8293/// the class key as an `ERROR` and the member list as an initializer.
8294fn cpp_folded_aggregate_head<'tree>(
8295    node: Node<'tree>,
8296    source: &str,
8297) -> Option<CppCollapsedAggregateHead<'tree>> {
8298    if !matches!(node.kind(), "declaration" | "field_declaration") {
8299        return None;
8300    }
8301    let mut cursor = node.walk();
8302    let children = node.children(&mut cursor).collect::<Vec<_>>();
8303    let key_index = children.iter().position(|child| {
8304        child.is_error()
8305            && matches!(
8306                node_text(*child, source).trim(),
8307                "struct" | "class" | "union"
8308            )
8309    })?;
8310    let declarator = *children.get(key_index + 1)?;
8311    // At file scope the grammar has an `init_declarator` for the name and its
8312    // initializer; inside an `ERROR` container the two are loose siblings.
8313    let (name, members) = if declarator.kind() == "init_declarator" {
8314        (
8315            declarator.child_by_field_name("declarator")?,
8316            declarator.child_by_field_name("value")?,
8317        )
8318    } else {
8319        (declarator, *children.get(key_index + 2)?)
8320    };
8321    if !matches!(
8322        name.kind(),
8323        "field_identifier" | "type_identifier" | "identifier"
8324    ) {
8325        return None;
8326    }
8327    if members.kind() != "initializer_list" {
8328        return None;
8329    }
8330    let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
8331    Some(CppCollapsedAggregateHead {
8332        key: children[key_index],
8333        name,
8334        opening,
8335        folded_members: Some(members),
8336        width: 1,
8337    })
8338}
8339
8340/// Where a folded member list ends: at its own closing brace when it has one,
8341/// so the reparse of the declarations it holds sees a member list and not a
8342/// stray brace.
8343fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
8344    let mut cursor = members.walk();
8345    members
8346        .children(&mut cursor)
8347        .filter(|child| child.kind() == "}" && !child.is_missing())
8348        .last()
8349        .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
8350}
8351
8352/// The members one collapsed aggregate region declares, in source order: what
8353/// its field-list macro invocations donate, then the ordinary declarations
8354/// that follow the last invocation and lost their shape with the body.
8355fn cpp_collapsed_aggregate_members(
8356    macro_nodes: &[Node<'_>],
8357    region: std::ops::Range<usize>,
8358    region_start_line: usize,
8359    source: &str,
8360    environment: &HashMap<String, ObjectMacroReplacement>,
8361) -> Vec<CppCollapsedMember> {
8362    let mut members = macro_nodes
8363        .iter()
8364        .map(|macro_node| CppCollapsedMember::MacroFields {
8365            range: cpp_declaration_range(*macro_node),
8366            fields: object_macro_field_closure(
8367                environment,
8368                &normalize_cpp_whitespace(node_text(*macro_node, source)),
8369            ),
8370        })
8371        .collect::<Vec<_>>();
8372    // Whatever follows the last invocation is an ordinary member list that
8373    // lost its declaration shape with the aggregate's body; the grammar
8374    // recovers it from its own byte range.
8375    let declarations_start = macro_nodes
8376        .last()
8377        .map_or(region.start, |macro_node| macro_node.end_byte());
8378    members.push(CppCollapsedMember::Declarations {
8379        span: declarations_start..region.end,
8380        start_line: region_start_line
8381            + cpp_line_breaks_between(source, region.start, declarations_start),
8382    });
8383    members
8384}
8385
8386/// Line breaks in `source[from..to]`, for mapping a recovered byte offset to a
8387/// line without rescanning the file from its start.
8388fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
8389    source.get(from..to).map_or(0, |slice| {
8390        slice.bytes().filter(|byte| *byte == b'\n').count()
8391    })
8392}
8393
8394/// How many enclosing aggregates a collapsed member declaration closes: the
8395/// close braces it carries that its own braces do not balance. A member that
8396/// declares a nested aggregate of its own (`union { ... } active_reqs;` in
8397/// libuv's `uv_loop_s`) closes nothing, so counting every `}` node ended the
8398/// owner early and lost every member after it (#2985).
8399fn count_close_brace_nodes(node: Node<'_>) -> usize {
8400    let mut opened = 0usize;
8401    let mut closed = 0usize;
8402    let mut stack = vec![node];
8403    while let Some(current) = stack.pop() {
8404        if !current.is_missing() {
8405            match current.kind() {
8406                "{" => opened += 1,
8407                "}" => closed += 1,
8408                _ => {}
8409            }
8410        }
8411        let mut cursor = current.walk();
8412        stack.extend(current.children(&mut cursor));
8413    }
8414    closed.saturating_sub(opened)
8415}
8416
8417/// Classify a C++ field while its declaration syntax is already available.
8418///
8419/// The persisted result lets later visibility queries avoid reparsing the
8420/// complete source file only to recover linkage.
8421pub fn cpp_field_declaration_linkage<'tree>(
8422    declaration: Node<'tree>,
8423    source: &str,
8424    ancestry: &ParentIndex<'tree>,
8425) -> CppFieldLinkage {
8426    let mut current = ancestry.parent(declaration);
8427    let mut enclosed_by_class = false;
8428    while let Some(node) = current {
8429        if node.kind() == "namespace_definition"
8430            && node
8431                .child_by_field_name("name")
8432                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8433        {
8434            return CppFieldLinkage::Internal;
8435        }
8436        if matches!(
8437            node.kind(),
8438            "class_specifier" | "struct_specifier" | "union_specifier"
8439        ) && node
8440            .child_by_field_name("name")
8441            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8442        {
8443            return CppFieldLinkage::Internal;
8444        }
8445        if matches!(
8446            node.kind(),
8447            "class_specifier" | "struct_specifier" | "union_specifier"
8448        ) {
8449            enclosed_by_class = true;
8450        }
8451        if matches!(node.kind(), "function_definition" | "lambda_expression") {
8452            return CppFieldLinkage::Internal;
8453        }
8454        current = ancestry.parent(node);
8455    }
8456    if enclosed_by_class {
8457        return CppFieldLinkage::External;
8458    }
8459    let mut cursor = declaration.walk();
8460    let mut has_static = false;
8461    let mut has_extern = false;
8462    let mut has_inline = false;
8463    let mut has_const = false;
8464    let mut has_constexpr = false;
8465    for child in declaration.named_children(&mut cursor) {
8466        let text = normalize_cpp_whitespace(node_text(child, source));
8467        match (child.kind(), text.as_str()) {
8468            ("storage_class_specifier", "static") => has_static = true,
8469            ("storage_class_specifier", "extern") => has_extern = true,
8470            ("storage_class_specifier", "inline") => has_inline = true,
8471            ("storage_class_specifier", "constexpr") => has_constexpr = true,
8472            ("type_qualifier", "const") => has_const = true,
8473            ("type_qualifier", "constexpr") => has_constexpr = true,
8474            _ => {}
8475        }
8476    }
8477    if has_static {
8478        CppFieldLinkage::Internal
8479    } else if has_extern || has_inline {
8480        CppFieldLinkage::External
8481    } else if has_const || has_constexpr {
8482        CppFieldLinkage::InternalUnlessExternalPeer
8483    } else {
8484        CppFieldLinkage::External
8485    }
8486}
8487
8488fn cpp_declaration_range(node: Node<'_>) -> Range {
8489    Range {
8490        start_byte: node.start_byte(),
8491        end_byte: node.end_byte(),
8492        start_line: node.start_position().row + 1,
8493        end_line: node.end_position().row + 1,
8494    }
8495}
8496
8497/// A recovery interval as a [`Range`], for materialization records whose
8498/// window is a byte region rather than one parser node (the sentinel-macro
8499/// region reparses, issue #941/#1657).
8500fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
8501    let line_at = |byte: usize| {
8502        source.as_bytes()[..byte]
8503            .iter()
8504            .filter(|&&b| b == b'\n')
8505            .count()
8506            + 1
8507    };
8508    Range {
8509        start_byte,
8510        end_byte,
8511        start_line: line_at(start_byte),
8512        end_line: line_at(end_byte),
8513    }
8514}
8515
8516/// Every `#include` directive the tree holds, in source order.
8517///
8518/// A preorder sweep rather than a step of the declaration walk: the container
8519/// walk descends only through declaration scopes, so an include written inside
8520/// a class body or a function body would otherwise never be seen, and an
8521/// include is an include wherever it is written.
8522pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
8523    walk_named_tree_preorder(root, true, |node| {
8524        if node.kind() == "preproc_include" {
8525            let raw = normalize_cpp_whitespace(node_text(node, source));
8526            if !raw.is_empty() {
8527                parsed.imports.push(ImportInfo {
8528                    raw_snippet: raw,
8529                    is_wildcard: false,
8530                    is_global: false,
8531                    identifier: None,
8532                    alias: None,
8533                    path: None,
8534                    binder_span: None,
8535                });
8536            }
8537            return WalkControl::SkipChildren;
8538        }
8539        WalkControl::Continue
8540    });
8541}
8542
8543pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
8544    let mut in_block_comment = false;
8545    for line in source.lines() {
8546        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
8547        let trimmed = stripped.trim();
8548        if !looks_like_quoted_include_line(trimmed) {
8549            continue;
8550        }
8551
8552        let raw = normalize_cpp_whitespace(trimmed);
8553        // The tree-sitter walk already recorded every `#include` it could see;
8554        // this line scan only recovers the ones a parse error hid, so skip a
8555        // snippet that is already an import binding.
8556        if parsed
8557            .imports
8558            .iter()
8559            .any(|import| import.raw_snippet == raw)
8560        {
8561            continue;
8562        }
8563
8564        parsed.imports.push(ImportInfo {
8565            raw_snippet: raw,
8566            is_wildcard: false,
8567            is_global: false,
8568            identifier: None,
8569            alias: None,
8570            path: None,
8571            binder_span: None,
8572        });
8573    }
8574}
8575
8576fn looks_like_quoted_include_line(line: &str) -> bool {
8577    let Some(rest) = line.trim_start().strip_prefix('#') else {
8578        return false;
8579    };
8580    let Some(rest) = rest.trim_start().strip_prefix("include") else {
8581        return false;
8582    };
8583    rest.trim_start().starts_with('"')
8584}
8585
8586fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
8587    let mut raw = Vec::new();
8588    let mut cursor = node.walk();
8589    for child in node.named_children(&mut cursor) {
8590        if child.kind() == "base_class_clause" {
8591            collect_cpp_base_nodes(child, source, &mut raw);
8592        }
8593    }
8594    raw
8595}
8596
8597fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
8598    walk_named_tree_preorder(node, false, |child| match child.kind() {
8599        "type_identifier" | "qualified_identifier" | "template_type" => {
8600            let text = normalize_cpp_whitespace(node_text(child, source));
8601            if !text.is_empty() {
8602                raw.push(text);
8603            }
8604            WalkControl::SkipChildren
8605        }
8606        _ => WalkControl::Continue,
8607    });
8608}
8609
8610fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
8611    let mut out = String::new();
8612    let chars: Vec<char> = line.chars().collect();
8613    let mut index = 0;
8614    let mut in_string = false;
8615    let mut in_char = false;
8616    let mut escape = false;
8617
8618    while index < chars.len() {
8619        let ch = chars[index];
8620        let next = chars.get(index + 1).copied();
8621
8622        if *in_block_comment {
8623            if ch == '*' && next == Some('/') {
8624                *in_block_comment = false;
8625                index += 2;
8626            } else {
8627                index += 1;
8628            }
8629            continue;
8630        }
8631
8632        if in_string {
8633            out.push(ch);
8634            if escape {
8635                escape = false;
8636            } else if ch == '\\' {
8637                escape = true;
8638            } else if ch == '"' {
8639                in_string = false;
8640            }
8641            index += 1;
8642            continue;
8643        }
8644
8645        if in_char {
8646            out.push(ch);
8647            if escape {
8648                escape = false;
8649            } else if ch == '\\' {
8650                escape = true;
8651            } else if ch == '\'' {
8652                in_char = false;
8653            }
8654            index += 1;
8655            continue;
8656        }
8657
8658        if ch == '/' && next == Some('/') {
8659            break;
8660        }
8661        if ch == '/' && next == Some('*') {
8662            *in_block_comment = true;
8663            index += 2;
8664            continue;
8665        }
8666        if ch == '"' {
8667            in_string = true;
8668            out.push(ch);
8669            index += 1;
8670            continue;
8671        }
8672        if ch == '\'' {
8673            in_char = true;
8674            out.push(ch);
8675            index += 1;
8676            continue;
8677        }
8678
8679        out.push(ch);
8680        index += 1;
8681    }
8682
8683    out
8684}
8685
8686#[derive(Clone)]
8687struct FunctionInfo {
8688    package_name: String,
8689    owner: Option<CppMemberOwner>,
8690    name: String,
8691    signature: String,
8692}
8693
8694/// Owner of a member function, kept structured so a literal `$` inside a
8695/// source-level class name never crosses a join/split boundary: the legacy
8696/// `$`-joined owner string was re-split at fq construction, dropping a leading
8697/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
8698/// and tripping the package/short boundary assert -- the #2140 corruption one
8699/// level up (#2362).
8700#[derive(Clone)]
8701enum CppMemberOwner {
8702    /// Source-level owner class chain from a qualified declarator-id, one
8703    /// class name per component (`Outer::Inner::method` -> `["Outer",
8704    /// "Inner"]`); each component may itself contain a literal `$`.
8705    Chain(Vec<String>),
8706    /// The lexically enclosing or recovered class unit; the member fq extends
8707    /// its fq directly instead of re-splitting its `$`-joined short chain.
8708    Unit(CodeUnit),
8709}
8710
8711impl CppMemberOwner {
8712    /// The legacy `$`-joined owner chain used in the member's short name.
8713    fn short_chain(&self) -> String {
8714        match self {
8715            Self::Chain(chain) => chain.join("$"),
8716            Self::Unit(parent) => parent.short_name().to_string(),
8717        }
8718    }
8719}
8720
8721enum DeclaratorKind<'a> {
8722    Function(Node<'a>),
8723    Variable(Node<'a>),
8724}
8725
8726impl FunctionInfo {
8727    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
8728        self.code_unit_with_synthetic(file, false)
8729    }
8730
8731    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
8732        let short_name = match &self.owner {
8733            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
8734            None => self.name.clone(),
8735        };
8736        let fq = match &self.owner {
8737            Some(CppMemberOwner::Chain(chain)) => {
8738                debug_assert!(
8739                    !chain.is_empty(),
8740                    "an empty owner chain is no owner; producers return None instead"
8741                );
8742                let mut fq = FqName::new();
8743                cpp_push_package(&mut fq, &self.package_name);
8744                let mut first = true;
8745                for component in chain {
8746                    let kind = if first {
8747                        SegmentKind::Type
8748                    } else {
8749                        SegmentKind::Nested
8750                    };
8751                    fq.push(cpp_segment(component, kind));
8752                    first = false;
8753                }
8754                fq.push(cpp_segment(&self.name, SegmentKind::Member));
8755                fq
8756            }
8757            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
8758                .fq()
8759                .clone()
8760                .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
8761            // An anonymous parent (empty short chain) contributes no owner
8762            // segment -- the same guard as cpp_join_member_short above.
8763            Some(CppMemberOwner::Unit(_)) | None => {
8764                let mut fq = FqName::new();
8765                cpp_push_package(&mut fq, &self.package_name);
8766                fq.push(cpp_segment(&self.name, SegmentKind::Member));
8767                fq
8768            }
8769        };
8770        CodeUnit::with_signature_and_fq(
8771            file,
8772            CodeUnitType::Function,
8773            self.package_name.clone(),
8774            short_name,
8775            Some(self.signature.clone()),
8776            synthetic,
8777            fq,
8778        )
8779    }
8780}
8781
8782fn extract_function_info(
8783    declarator: Node<'_>,
8784    source: &str,
8785    scope: &ScopeInfo,
8786) -> Option<FunctionInfo> {
8787    let parameters_node = declarator.child_by_field_name("parameters")?;
8788    let declarator_name_node = declarator
8789        .child_by_field_name("declarator")
8790        .or_else(|| parameters_node.prev_named_sibling())?;
8791    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
8792}
8793
8794fn extract_function_info_from_name(
8795    declarator: Node<'_>,
8796    declarator_name_node: Node<'_>,
8797    source: &str,
8798    scope: &ScopeInfo,
8799) -> Option<FunctionInfo> {
8800    let parameters_node = declarator.child_by_field_name("parameters")?;
8801    let parameters_text = cpp_parameter_signature(parameters_node, source);
8802    let recovered_specialization_member = scope
8803        .recovered_specialization_member_scope
8804        .then(|| {
8805            let terminal = declarator_name_node
8806                .child_by_field_name("name")
8807                .unwrap_or(declarator_name_node);
8808            let name = canonical_cpp_qualified_component(terminal, source)?.name;
8809            let owner = scope.class_unit.as_ref()?;
8810            Some((
8811                Some(CppMemberOwner::Unit(owner.clone())),
8812                name,
8813                scope.package_name.clone(),
8814            ))
8815        })
8816        .flatten();
8817    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
8818        parts
8819    } else if let Some(parts) =
8820        split_structured_templated_cpp_name(declarator_name_node, source, scope)
8821    {
8822        parts
8823    } else {
8824        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
8825            declarator_name_node,
8826            source,
8827        )?);
8828        if raw_name.is_empty() {
8829            return None;
8830        }
8831        split_cpp_name(&raw_name, scope)
8832    };
8833    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
8834    let mut signature = if suffix.is_empty() {
8835        parameters_text
8836    } else {
8837        format!("{parameters_text} {suffix}")
8838    };
8839    if let Some(template_signature) = &scope.template_signature {
8840        signature = format!("{template_signature}{signature}");
8841    }
8842
8843    Some(FunctionInfo {
8844        package_name,
8845        owner,
8846        name,
8847        signature,
8848    })
8849}
8850
8851/// Recover the semantic return type and callable name when a declaration macro
8852/// occupies a function definition's `type` field. Tree-sitter either exposes a
8853/// scalar return as the declarator's apparent name and the callable as the sole
8854/// identifier in an `ERROR`, or joins a template return and callable into a
8855/// qualified identifier with a missing `::`. Both shapes retain the complete
8856/// parameter list and body; a concrete separator remains an out-of-line member.
8857fn cpp_macro_displaced_callable_parts<'tree>(
8858    function_declarator: Node<'tree>,
8859    source: &str,
8860    ancestry: &ParentIndex<'tree>,
8861) -> Option<(Node<'tree>, Node<'tree>)> {
8862    let definition = ancestry.parent(function_declarator)?;
8863    if definition.kind() != "function_definition"
8864        || definition.child_by_field_name("declarator") != Some(function_declarator)
8865        || definition
8866            .child_by_field_name("body")
8867            .is_none_or(|body| body.kind() != "compound_statement")
8868    {
8869        return None;
8870    }
8871    let macro_type = definition.child_by_field_name("type")?;
8872    if macro_type.kind() != "type_identifier"
8873        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8874    {
8875        return None;
8876    }
8877
8878    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
8879    if apparent_return_type.kind() == "qualified_identifier"
8880        && let (Some(return_type), Some(callable_name)) = (
8881            apparent_return_type.child_by_field_name("scope"),
8882            apparent_return_type.child_by_field_name("name"),
8883        )
8884        && return_type.kind() == "template_type"
8885        && matches!(callable_name.kind(), "identifier" | "field_identifier")
8886        && (0..apparent_return_type.child_count())
8887            .filter_map(|index| apparent_return_type.child(index))
8888            .any(|child| child.kind() == "::" && child.is_missing())
8889        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
8890        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8891    {
8892        return Some((return_type, callable_name));
8893    }
8894    if !matches!(
8895        apparent_return_type.kind(),
8896        "identifier" | "field_identifier" | "type_identifier"
8897    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
8898    {
8899        return None;
8900    }
8901    let parameters = function_declarator.child_by_field_name("parameters")?;
8902    let mut cursor = function_declarator.walk();
8903    let between = function_declarator
8904        .named_children(&mut cursor)
8905        .filter(|child| child.kind() != "comment")
8906        .filter(|child| {
8907            child.start_byte() >= apparent_return_type.end_byte()
8908                && child.end_byte() <= parameters.start_byte()
8909                && !same_node(*child, apparent_return_type)
8910                && !same_node(*child, parameters)
8911        })
8912        .collect::<Vec<_>>();
8913    let [name_error] = between.as_slice() else {
8914        return None;
8915    };
8916    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
8917        return None;
8918    }
8919    let callable_name = name_error.named_child(0)?;
8920    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
8921        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8922    {
8923        return None;
8924    }
8925    Some((apparent_return_type, callable_name))
8926}
8927
8928/// The part of a `function_declarator` after its parameter list that belongs to
8929/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
8930/// specification, a trailing return type and a trailing requires-clause.
8931///
8932/// The grammar makes each of these a distinct sibling of the `parameters`
8933/// field, so they are read from the tree. Splitting the declarator's text on
8934/// the parameter list instead silently dropped every qualifier whenever the
8935/// parameter list was spelled with whitespace that normalization rewrote - a
8936/// line break or a double space was enough to make a `const` member definition
8937/// a different logical symbol from its declaration (#1827).
8938///
8939/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
8940/// are deliberately excluded. C++ does not make them part of the signature and
8941/// an out-of-line definition never repeats them, so including them would split
8942/// a declaration from its own definition.
8943fn cpp_declarator_identity_suffix(
8944    declarator: Node<'_>,
8945    parameters_node: Node<'_>,
8946    source: &str,
8947) -> String {
8948    let mut cursor = declarator.walk();
8949    let parts = declarator
8950        .named_children(&mut cursor)
8951        .filter(|child| child.start_byte() >= parameters_node.end_byte())
8952        .filter(|child| {
8953            matches!(
8954                child.kind(),
8955                "type_qualifier"
8956                    | "ref_qualifier"
8957                    | "noexcept"
8958                    | "throw_specifier"
8959                    | "trailing_return_type"
8960                    | "requires_clause"
8961            )
8962        })
8963        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8964        .filter(|text| !text.is_empty())
8965        .collect::<Vec<_>>();
8966    normalize_cpp_qualifier_suffix(&parts.join(" "))
8967}
8968
8969/// The identity suffix of one callable declarator, for a consumer that holds
8970/// the declarator rather than the declaration walk's parts.
8971///
8972/// The persisted signature concatenates the parameter spelling and this suffix,
8973/// so a comparison that must agree on the suffix alone recomputes it here
8974/// instead of splitting the stored string.
8975pub(crate) fn cpp_callable_identity_suffix(
8976    function_declarator: Node<'_>,
8977    source: &str,
8978) -> Option<String> {
8979    let parameters_node = function_declarator.child_by_field_name("parameters")?;
8980    Some(cpp_declarator_identity_suffix(
8981        function_declarator,
8982        parameters_node,
8983        source,
8984    ))
8985}
8986
8987pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
8988    match classify_declarator(node)? {
8989        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
8990        DeclaratorKind::Variable(_) => None,
8991    }
8992}
8993
8994fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
8995    match node.kind() {
8996        "function_declarator" => {
8997            let inner = node
8998                .child_by_field_name("declarator")
8999                .or_else(|| node.child_by_field_name("name"))
9000                .or_else(|| last_named_child(node));
9001            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
9002                Some(DeclaratorKind::Variable(node))
9003            } else {
9004                Some(DeclaratorKind::Function(node))
9005            }
9006        }
9007        "init_declarator"
9008        | "pointer_declarator"
9009        | "reference_declarator"
9010        | "parenthesized_declarator"
9011        | "array_declarator"
9012        | "attributed_declarator"
9013        | "template_function" => node
9014            .child_by_field_name("declarator")
9015            .or_else(|| node.child_by_field_name("name"))
9016            .or_else(|| last_named_child(node))
9017            .and_then(classify_declarator),
9018        "identifier" | "field_identifier" | "qualified_identifier" => {
9019            Some(DeclaratorKind::Variable(node))
9020        }
9021        _ => node
9022            .child_by_field_name("declarator")
9023            .or_else(|| node.child_by_field_name("name"))
9024            .or_else(|| last_named_child(node))
9025            .and_then(classify_declarator),
9026    }
9027}
9028
9029fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
9030    matches!(
9031        node.kind(),
9032        "function_declarator"
9033            | "init_declarator"
9034            | "pointer_declarator"
9035            | "reference_declarator"
9036            | "parenthesized_declarator"
9037            | "array_declarator"
9038            | "attributed_declarator"
9039            | "template_function"
9040            | "identifier"
9041            | "field_identifier"
9042            | "qualified_identifier"
9043    )
9044}
9045
9046fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
9047    let class_like = first_class_like_child(node);
9048    let mut cursor = node.walk();
9049    node.named_children(&mut cursor).any(|child| {
9050        matches!(
9051            child.kind(),
9052            "init_declarator"
9053                | "pointer_declarator"
9054                | "reference_declarator"
9055                | "array_declarator"
9056                | "function_declarator"
9057                | "parenthesized_declarator"
9058                | "attributed_declarator"
9059        ) || matches!(
9060            child.kind(),
9061            "identifier" | "field_identifier" | "qualified_identifier"
9062        ) && class_like.is_none_or(|class_node| {
9063            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
9064        })
9065    })
9066}
9067
9068/// One namespace-scope forward class declaration that a recovered export-macro
9069/// class definition may borrow its identity from.  Tree-sitter can close a
9070/// malformed class at the enclosing namespace's closing brace, leaving the later
9071/// class definitions as root-level recovered `function_definition` nodes.  A
9072/// preceding `class Name;` in the same namespace is the only structured identity
9073/// signal available in that shape.
9074///
9075/// Everything recorded here is a property of the forward declaration alone.
9076/// What depends on the node doing the asking -- that the forward and its
9077/// namespace both close before it, with nothing but recovery trivia between --
9078/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
9079/// tree answers every later question about it.
9080struct CppNamespaceForward {
9081    name: String,
9082    start_byte: usize,
9083    /// Where the malformed namespace that held the forward ended.  No query
9084    /// asks anything else about that node.
9085    namespace_end_byte: usize,
9086    package_name: String,
9087}
9088
9089/// Read `node` as a borrowable namespace forward declaration.
9090///
9091/// The admission is deliberately conservative: only a body-less class specifier
9092/// whose declaration has no declarator, at namespace scope rather than inside a
9093/// function or class body, in a namespace that itself failed to parse.
9094fn cpp_namespace_forward_entry<'tree>(
9095    node: Node<'tree>,
9096    source: &str,
9097    ancestry: &ParentIndex<'tree>,
9098) -> Option<CppNamespaceForward> {
9099    if !matches!(
9100        node.kind(),
9101        "class_specifier" | "struct_specifier" | "union_specifier"
9102    ) || cpp_body_node(node).is_some()
9103    {
9104        return None;
9105    }
9106    let parent = node.parent()?;
9107    if !(parent.kind() == "declaration_list"
9108        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
9109    {
9110        return None;
9111    }
9112    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
9113    // Borrowing is only justified by the parser-recovery shape we are
9114    // repairing: the namespace that held the forward must itself contain a
9115    // syntax error. A clean, unrelated namespace forward is not an identity
9116    // proof.
9117    if !namespace.has_error() {
9118        return None;
9119    }
9120    Some(CppNamespaceForward {
9121        name: class_like_name(node, source, ancestry)?,
9122        start_byte: node.start_byte(),
9123        namespace_end_byte: namespace.end_byte(),
9124        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
9125    })
9126}
9127
9128/// Whether `forward` stands in the recovery relation to the node asking about
9129/// it: it and its malformed namespace both closed before the recovered class,
9130/// and nothing but recovery trivia separates the two.
9131fn cpp_namespace_forward_matches_recovery(
9132    forward: &CppNamespaceForward,
9133    recovered_node: Node<'_>,
9134) -> bool {
9135    forward.start_byte < recovered_node.start_byte()
9136        && forward.namespace_end_byte < recovered_node.start_byte()
9137        && malformed_namespace_is_nearest_recovery_region(
9138            forward.namespace_end_byte,
9139            recovered_node,
9140        )
9141}
9142
9143/// What one open [`CppVisitor::record_recovered_declarations`] has watched
9144/// happen to the declaration set.
9145///
9146/// The recovered set used to be a difference against a clone of the whole
9147/// declaration set, taken once per recovery: O(recoveries x declarations) on
9148/// exactly the error-recovered files that already walk slowest (#2787). The
9149/// walk knows which declarations it creates, so the capture collects them as
9150/// they are made and the difference is never needed.
9151///
9152/// `removed_pre_existing` is what makes that equal to the difference. A
9153/// deferred replacement removes the replaced declaration's children
9154/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
9155/// re-creates them. Creation alone cannot tell that apart from a first mint, so
9156/// a removal of a declaration this capture did not create records that it was
9157/// already there when the capture opened.
9158#[derive(Debug, Default)]
9159pub struct CppRecoveryCapture {
9160    /// Declarations created while this capture was open, in creation order.
9161    created: Vec<CodeUnit>,
9162    /// Membership for `created`.
9163    created_units: HashSet<CodeUnit>,
9164    /// Declarations that predate this capture and have been removed during it.
9165    removed_pre_existing: HashSet<CodeUnit>,
9166}
9167
9168/// Which owners the parse product already holds field declarations for, folded
9169/// in as the walk records them.
9170///
9171/// [`CppVisitor::has_enum_enumerator_units`] asks that question once per enum
9172/// and used to answer it by scanning every declaration accumulated so far:
9173/// O(enums x declarations) on exactly the generated headers that declare many
9174/// of both (#2786). The answer only grows by declaration, so the walk carries
9175/// it. A field's short name names its owner chain, `Owner.member`, so one field
9176/// answers for every dotted prefix of its own short name; an anonymous enum's
9177/// or union's enumerators carry bare short names instead (#2140), which is what
9178/// an empty owner short name asks about.
9179#[derive(Debug, Default)]
9180pub struct CppFieldOwnerIndex {
9181    /// Package name -> the owner short names its fields name.
9182    owners: HashMap<String, HashSet<String>>,
9183    /// Packages holding at least one field that names no owner.
9184    ownerless_packages: HashSet<String>,
9185}
9186
9187impl CppFieldOwnerIndex {
9188    /// The index of the declarations recorded so far, built when the first
9189    /// question arrives.
9190    fn of<'unit>(
9191        declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9192        file: &ProjectFile,
9193    ) -> Self {
9194        let mut index = Self::default();
9195        for declaration in declarations {
9196            index.record(declaration, file);
9197        }
9198        index
9199    }
9200
9201    fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
9202        if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
9203            return;
9204        }
9205        let short_name = code_unit.short_name();
9206        let package_name = code_unit.package_name();
9207        if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
9208            self.ownerless_packages.insert(package_name.to_string());
9209        }
9210        if !short_name.contains('.') {
9211            return;
9212        }
9213        if !self.owners.contains_key(package_name) {
9214            self.owners
9215                .insert(package_name.to_string(), HashSet::default());
9216        }
9217        let owners = self
9218            .owners
9219            .get_mut(package_name)
9220            .expect("the package entry was just ensured");
9221        for (offset, _) in short_name.match_indices('.') {
9222            let owner = &short_name[..offset];
9223            if !owners.contains(owner) {
9224                owners.insert(owner.to_string());
9225            }
9226        }
9227    }
9228
9229    /// Whether a field declaration in `package_name` names `owner_short_name`
9230    /// as its owner. An empty owner asks about ownerless fields instead.
9231    fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
9232        if owner_short_name.is_empty() {
9233            self.ownerless_packages.contains(package_name)
9234        } else {
9235            self.owners
9236                .get(package_name)
9237                .is_some_and(|owners| owners.contains(owner_short_name))
9238        }
9239    }
9240}
9241
9242/// The declaration scan [`CppFieldOwnerIndex`] replaces, kept as the oracle a
9243/// debug build asserts every carried answer against and as the release-mode
9244/// parity tests' reference (#2786).
9245#[cfg(any(debug_assertions, test))]
9246fn cpp_declarations_hold_owned_fields<'unit>(
9247    declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9248    file: &ProjectFile,
9249    package_name: &str,
9250    owner_short_name: &str,
9251) -> bool {
9252    let prefix = format!("{owner_short_name}.");
9253    declarations.into_iter().any(|unit| {
9254        unit.kind() == CodeUnitType::Field
9255            && unit.source() == file
9256            && unit.package_name() == package_name
9257            && if owner_short_name.is_empty() {
9258                // Anonymous enum/union parent: its enumerators carry bare
9259                // short names (#2140), so presence means any ownerless
9260                // field in this file.
9261                !unit.short_name().contains(['.', '$'])
9262            } else {
9263                unit.short_name().starts_with(&prefix)
9264            }
9265    })
9266}
9267
9268/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
9269///
9270/// A region reparse is its own tree and is dropped while the walk that made it
9271/// continues, so a later parse can be allocated at the same address and hand out
9272/// the same node ids.  The root's span and shape pin the identity its address
9273/// alone does not: two roots agreeing on all of this are the same parse of the
9274/// same bytes, and a scan of one is a scan of the other.
9275#[derive(PartialEq, Eq, Hash)]
9276pub struct CppTreeIdentity {
9277    root_id: usize,
9278    start_byte: usize,
9279    end_byte: usize,
9280    kind_id: u16,
9281    child_count: usize,
9282}
9283
9284impl CppTreeIdentity {
9285    fn of(root: Node<'_>) -> Self {
9286        Self {
9287            root_id: root.id(),
9288            start_byte: root.start_byte(),
9289            end_byte: root.end_byte(),
9290            kind_id: root.kind_id(),
9291            child_count: root.child_count(),
9292        }
9293    }
9294}
9295
9296/// The namespace forward declarations one tree's prefix holds, folded in as the
9297/// walk asks about them.
9298///
9299/// `scope_for_recovered_exported_class` asks the same question once per
9300/// recovered class, and the answer depends only on the part of the tree that
9301/// starts before the asking node.  Rescanning that prefix per question is
9302/// quadratic in the file, and a generated header whose parse recovery leaves
9303/// thousands of class-like declarations at file scope pays all of it: 1,904
9304/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
9305/// (#2754).  This carries the scan forward instead.  Each question advances the
9306/// traversal from wherever the last one stopped to the asking node's start byte,
9307/// so a whole walk pays at most one pass over the prefix its furthest question
9308/// reaches, and each question then costs a name lookup.
9309#[derive(Default)]
9310pub struct CppNamespaceForwardScan {
9311    /// Every named node starting before this byte has been folded in.
9312    scanned_through: usize,
9313    forwards: HashMap<String, Vec<CppNamespaceForward>>,
9314}
9315
9316impl CppNamespaceForwardScan {
9317    /// Fold in every named node of `root` that starts at or after the fold
9318    /// watermark and before `cutoff`.
9319    ///
9320    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
9321    /// pass owes are exactly the ones no earlier pass reached, and a question
9322    /// about an earlier byte than one already answered costs nothing.
9323    fn advance_to<'tree>(
9324        &mut self,
9325        root: Node<'tree>,
9326        cutoff: usize,
9327        source: &str,
9328        ancestry: &ParentIndex<'tree>,
9329    ) {
9330        if cutoff <= self.scanned_through {
9331            return;
9332        }
9333        let folded_through = self.scanned_through;
9334        let mut cursor = root.walk();
9335        let mut stack = vec![root];
9336        while let Some(current) = stack.pop() {
9337            if (folded_through..cutoff).contains(&current.start_byte())
9338                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9339            {
9340                self.forwards
9341                    .entry(forward.name.clone())
9342                    .or_default()
9343                    .push(forward);
9344            }
9345            // A subtree ending before the watermark holds only nodes an earlier
9346            // pass already folded, and one starting at or after the cutoff is
9347            // outside the prefix being asked about. Skipping both is what keeps
9348            // the total traversal to one pass.
9349            for child in current.named_children(&mut cursor) {
9350                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
9351                    stack.push(child);
9352                }
9353            }
9354        }
9355        self.scanned_through = cutoff;
9356    }
9357
9358    /// The one namespace `name` was forward declared in before `recovered_node`.
9359    /// More than one matching forward declaration is ambiguous and answers
9360    /// nothing rather than guessing.
9361    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
9362        let mut matching = self
9363            .forwards
9364            .get(name)
9365            .into_iter()
9366            .flatten()
9367            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
9368        let first = matching.next()?;
9369        matching
9370            .next()
9371            .is_none()
9372            .then(|| first.package_name.clone())
9373    }
9374}
9375
9376/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
9377/// debug build checks every answer against (and the one the parity tests drive
9378/// directly).  It walks the whole prefix per question, which is exactly the cost
9379/// #2754 removed from the release path.
9380#[cfg(any(debug_assertions, test))]
9381fn unique_earlier_cpp_namespace_forward<'tree>(
9382    recovered_node: Node<'tree>,
9383    name: &str,
9384    source: &str,
9385    ancestry: &ParentIndex<'tree>,
9386) -> Option<String> {
9387    let mut root = recovered_node;
9388    while let Some(parent) = ancestry.parent(root) {
9389        root = parent;
9390    }
9391
9392    let mut candidates = Vec::new();
9393    let mut stack = vec![root];
9394    while let Some(current) = stack.pop() {
9395        if current.start_byte() < recovered_node.start_byte()
9396            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9397            && forward.name == name
9398            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
9399        {
9400            candidates.push(forward.package_name);
9401        }
9402
9403        let mut cursor = current.walk();
9404        for child in current.named_children(&mut cursor) {
9405            if child.start_byte() < recovered_node.start_byte() {
9406                stack.push(child);
9407            }
9408        }
9409    }
9410
9411    if candidates.len() == 1 {
9412        candidates.pop()
9413    } else {
9414        None
9415    }
9416}
9417
9418fn malformed_namespace_is_nearest_recovery_region(
9419    namespace_end_byte: usize,
9420    recovered_node: Node<'_>,
9421) -> bool {
9422    let mut root = recovered_node;
9423    while let Some(parent) = root.parent() {
9424        root = parent;
9425    }
9426    let mut cursor = root.walk();
9427    root.named_children(&mut cursor)
9428        .filter(|sibling| {
9429            namespace_end_byte <= sibling.start_byte()
9430                && sibling.end_byte() <= recovered_node.start_byte()
9431        })
9432        .all(is_malformed_namespace_recovery_trivia)
9433}
9434
9435fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
9436    matches!(node.kind(), "ERROR" | "comment")
9437        || node.kind().starts_with("preproc_")
9438        || node.kind() == "expression_statement" && node.named_child_count() == 0
9439}
9440
9441/// Return the namespace path for a forward class only when the declaration is
9442/// at namespace scope.  A declaration nested in a function/class body may share
9443/// the same namespace ancestor but cannot identify a top-level class definition.
9444fn cpp_namespace_name_for_forward<'tree>(
9445    node: Node<'tree>,
9446    source: &str,
9447    ancestry: &ParentIndex<'tree>,
9448) -> Option<String> {
9449    cpp_namespace_definition_for_forward(node, ancestry)?;
9450    cpp_lexical_namespace_name(node, source, ancestry)
9451}
9452
9453fn cpp_namespace_definition_for_forward<'tree>(
9454    node: Node<'tree>,
9455    ancestry: &ParentIndex<'tree>,
9456) -> Option<Node<'tree>> {
9457    let declaration = ancestry.parent(node)?;
9458    let mut ancestor = ancestry.parent(declaration);
9459    while let Some(current) = ancestor {
9460        if matches!(
9461            current.kind(),
9462            "compound_statement"
9463                | "field_declaration_list"
9464                | "class_specifier"
9465                | "struct_specifier"
9466                | "union_specifier"
9467                | "function_definition"
9468                | "lambda_expression"
9469        ) {
9470            return None;
9471        }
9472        if current.kind() == "namespace_definition" {
9473            return Some(current);
9474        }
9475        ancestor = ancestry.parent(current);
9476    }
9477    None
9478}
9479
9480fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
9481    match node.kind() {
9482        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9483        "parenthesized_declarator" => node
9484            .child_by_field_name("declarator")
9485            .or_else(|| last_named_child(node))
9486            .is_some_and(is_pointer_wrapper_declarator),
9487        "template_function" => node
9488            .child_by_field_name("name")
9489            .is_some_and(is_function_pointer_like_inner_declarator),
9490        _ => false,
9491    }
9492}
9493
9494fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
9495    match node.kind() {
9496        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9497        "parenthesized_declarator" => node
9498            .child_by_field_name("declarator")
9499            .or_else(|| last_named_child(node))
9500            .is_some_and(is_pointer_wrapper_declarator),
9501        _ => false,
9502    }
9503}
9504
9505fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
9506    let cleaned = raw_name.trim_start_matches("template ").trim();
9507    // A leading `::` is the explicit-global marker, not an empty owner segment.
9508    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
9509    // erroneous macro envelope swallowing the first identifier of an
9510    // out-of-line `X::X` constructor, chromium #1573); without this strip the
9511    // split below yields owner_parts `[""]`, constructing a unit with an empty
9512    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
9513    let cleaned = cleaned.trim_start_matches("::");
9514    // Parser recovery can preserve two adjacent scope operators around a
9515    // missing component (for example `X::/**/::method` in compiler diagnostic
9516    // fixtures). Empty components are syntax-recovery artifacts, never C++
9517    // owners. Keeping one as the final owner constructed `short_name=".method"`
9518    // and violated the structured package/short boundary during a large LLVM
9519    // workspace build. This is the same legacy-string-to-FqName bridge as the
9520    // ordinary split above; discard only components that the delimiter itself
9521    // proves empty.
9522    let parts: Vec<_> = cleaned
9523        .split("::")
9524        .filter(|component| !component.is_empty())
9525        .collect();
9526    if parts.is_empty() {
9527        return (None, cleaned.to_string(), scope.package_name.clone());
9528    }
9529    if parts.len() > 1 {
9530        let name = parts.last().unwrap_or(&cleaned).to_string();
9531        let owner_parts = &parts[..parts.len() - 1];
9532        if let Some(class_unit) = &scope.class_unit {
9533            // Lexically inside a class body: the owner is that class, whatever
9534            // the declarator re-qualifies it as.
9535            return (
9536                Some(CppMemberOwner::Unit(class_unit.clone())),
9537                name,
9538                scope.package_name.clone(),
9539            );
9540        }
9541        if !scope.package_name.is_empty() {
9542            // Out-of-line member definition written *inside* an enclosing
9543            // `namespace {}` block (scope package is that namespace). Every
9544            // owner segment before the terminal member is a class-nesting step
9545            // -- an out-of-line nested-class member `Outer::Inner::method` in
9546            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
9547            // namespace path: `using namespace` never brings nested-class
9548            // access into unqualified scope, so C++ always writes the full
9549            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
9550            // that redundantly re-states the enclosing namespace it already
9551            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
9552            // strip that re-qualifying prefix (which duplicates a suffix of the
9553            // enclosing package path) before treating what remains as the
9554            // nested-class chain, so the redundant spelling still lands on the
9555            // same `log4cxx.Foo.method` identity as its header declaration.
9556            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
9557            let owner = (!nested.is_empty()).then(|| {
9558                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
9559            });
9560            return (owner, name, scope.package_name.clone());
9561        }
9562        // File scope (no enclosing `namespace {}` block, scope package empty).
9563        let (owner, package_name) = if owner_parts.len() > 1 {
9564            // A multi-segment qualifier at file scope with no enclosing
9565            // namespace: treat all but the last owner segment as the namespace
9566            // path and the last as the owning class (`ns1::ns2::Class::method`
9567            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
9568            // is really a namespace or an outer class cannot be told from the
9569            // declarator text alone here, and no enclosing namespace or
9570            // in-index owner is available at per-file extraction to confirm the
9571            // class reading, so the far-more-common namespace interpretation is
9572            // kept rather than guessed away (the nested-class-at-file-scope and
9573            // using-directive-qualified nested-class shapes remain on this
9574            // behavior; see #1121).
9575            (
9576                Some(CppMemberOwner::Chain(vec![
9577                    owner_parts.last().unwrap_or(&"").to_string(),
9578                ])),
9579                owner_parts[..owner_parts.len() - 1].join("::"),
9580            )
9581        } else {
9582            // A bare `Class::member` qualifier at file scope carries no
9583            // namespace segment of its own. The declarator alone cannot say
9584            // which namespace owns `Class` -- but a `using namespace X;`
9585            // directive already in effect at this point in the file (#1093,
9586            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
9587            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
9588            // is the remaining structural signal for it, so fall back to it
9589            // rather than leaving the definition's package empty while its
9590            // header declaration (parsed inside the `namespace {}` block) keeps
9591            // the real one -- an identity split that made the same member
9592            // unresolvable under its own displayed spelling.
9593            (
9594                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
9595                cpp_using_directive_namespace_for_bare_owner(scope),
9596            )
9597        };
9598        return (owner, name, package_name);
9599    }
9600
9601    let package_name = scope.package_name.clone();
9602    let owner = scope
9603        .class_unit
9604        .as_ref()
9605        .map(|parent| CppMemberOwner::Unit(parent.clone()));
9606    (owner, cleaned.to_string(), package_name)
9607}
9608
9609/// Drop the leading owner segments of an out-of-line member qualifier that
9610/// merely re-state the enclosing namespace the definition already sits in, so
9611/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
9612/// definition may redundantly write `a::b::Outer::Inner::method` (or the
9613/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
9614/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
9615/// noise, not class-nesting steps. Returns the owner segments with the longest
9616/// such re-qualifying prefix removed (possibly all of them, when the qualifier
9617/// names only the enclosing namespace before the terminal member -- a
9618/// re-qualified free function). `package_name` is the enclosing namespace path
9619/// in its stored `::`-joined form; both sides are split on the same delimiter
9620/// the namespace walker joined them with, so this compares namespace *segments*
9621/// rather than scanning text.
9622fn strip_redundant_namespace_prefix<'a>(
9623    owner_parts: &'a [&'a str],
9624    package_name: &str,
9625) -> &'a [&'a str] {
9626    if package_name.is_empty() {
9627        return owner_parts;
9628    }
9629    let package_segments: Vec<&str> = package_name.split("::").collect();
9630    let max_prefix = owner_parts.len().min(package_segments.len());
9631    for prefix_len in (1..=max_prefix).rev() {
9632        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
9633        if &owner_parts[..prefix_len] == package_suffix {
9634            return &owner_parts[prefix_len..];
9635        }
9636    }
9637    owner_parts
9638}
9639
9640/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
9641/// class name at file/namespace scope, from the `using namespace` directives
9642/// visible at this point in the file. Several may be in scope at once (a
9643/// primary `using namespace NS;` alongside deeper conveniences like `using
9644/// namespace NS::helpers;`); since the declarator gives no way to tell which
9645/// one actually declares the owner class, prefer the shallowest (fewest
9646/// `::`-separated segments) as the file's most likely "home" namespace,
9647/// breaking ties by declaration order. Returns an empty string (leaving the
9648/// caller's package unqualified, as before) when no using-namespace directive
9649/// is in scope.
9650fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
9651    scope
9652        .visible_using_namespaces
9653        .iter()
9654        .min_by_key(|namespace| namespace.split("::").count())
9655        .cloned()
9656        .unwrap_or_default()
9657}
9658
9659struct CppQualifiedNameComponent {
9660    name: String,
9661    is_template_id: bool,
9662}
9663
9664/// Canonical nested-class chain for an out-of-line class definition written
9665/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
9666/// component per class (`["Outer", "Inner"]`).
9667///
9668/// The enclosing namespace fixes the namespace/class boundary: after an
9669/// optional redundant spelling of that namespace, every component belongs to
9670/// the class chain. File-scope qualified class names remain untouched because
9671/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
9672///
9673/// The components stay structured (rather than being `$`-joined here) so the
9674/// fq construction can push one Type/Nested segment per class; the `$`-joined
9675/// short-name display form is derived at the call sites that need it.
9676fn qualified_class_name_chain(
9677    class_node: Node<'_>,
9678    source: &str,
9679    scope: &ScopeInfo,
9680) -> Option<Vec<String>> {
9681    if scope.package_name.is_empty() || scope.class_unit.is_some() {
9682        return None;
9683    }
9684    let name = class_node.child_by_field_name("name")?;
9685    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
9686    if explicitly_global
9687        || components.len() < 2
9688        || components.iter().any(|component| component.is_template_id)
9689    {
9690        return None;
9691    }
9692    let names = components
9693        .iter()
9694        .map(|component| component.name.as_str())
9695        .collect::<Vec<_>>();
9696    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
9697    if class_chain.is_empty() {
9698        return None;
9699    }
9700    Some(class_chain.iter().map(|name| name.to_string()).collect())
9701}
9702
9703fn structured_cpp_qualified_components(
9704    qualified_name: Node<'_>,
9705    source: &str,
9706) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
9707    if qualified_name.kind() != "qualified_identifier" {
9708        return None;
9709    }
9710
9711    let mut components = Vec::new();
9712    let mut current = qualified_name;
9713    let mut explicitly_global = false;
9714    loop {
9715        if current.kind() == "qualified_identifier" {
9716            if let Some(component) = current.child_by_field_name("scope") {
9717                components.push(canonical_cpp_qualified_component(component, source)?);
9718            } else if components.is_empty() {
9719                explicitly_global = true;
9720            } else {
9721                return None;
9722            }
9723            current = current.child_by_field_name("name")?;
9724        } else {
9725            components.push(canonical_cpp_qualified_component(current, source)?);
9726            break;
9727        }
9728    }
9729    Some((components, explicitly_global))
9730}
9731
9732fn split_structured_templated_cpp_name(
9733    declarator_name: Node<'_>,
9734    source: &str,
9735    scope: &ScopeInfo,
9736) -> Option<(Option<CppMemberOwner>, String, String)> {
9737    let (mut components, explicitly_global) =
9738        structured_cpp_qualified_components(declarator_name, source)?;
9739
9740    let terminal = components.pop()?;
9741    let owner_start = components
9742        .iter()
9743        .position(|component| component.is_template_id)?;
9744    let explicit_package = components[..owner_start]
9745        .iter()
9746        .map(|component| component.name.as_str())
9747        .collect::<Vec<_>>()
9748        .join("::");
9749    let explicit_package_is_empty = explicit_package.is_empty();
9750    let package_name = match (
9751        explicitly_global,
9752        scope.package_name.is_empty(),
9753        explicit_package_is_empty,
9754    ) {
9755        (true, _, _) => explicit_package,
9756        (false, _, true) => scope.package_name.clone(),
9757        (false, true, false) => explicit_package,
9758        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
9759    };
9760    // Same identity-split fallback as `split_cpp_name` (#1093): a template
9761    // specialization's owner class named with no namespace segment of its own
9762    // (`explicit_package` empty) at file scope (`explicitly_global` false)
9763    // with nothing enclosing (`package_name` still empty) has no structural
9764    // signal for its namespace besides an in-scope `using namespace X;`.
9765    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
9766    {
9767        cpp_using_directive_namespace_for_bare_owner(scope)
9768    } else {
9769        package_name
9770    };
9771    let owner_chain = components[owner_start..]
9772        .iter()
9773        .map(|component| component.name.clone())
9774        .collect::<Vec<_>>();
9775    if owner_chain.is_empty() || terminal.name.is_empty() {
9776        return None;
9777    }
9778
9779    Some((
9780        Some(CppMemberOwner::Chain(owner_chain)),
9781        terminal.name,
9782        package_name,
9783    ))
9784}
9785
9786fn canonical_cpp_qualified_component(
9787    mut component: Node<'_>,
9788    source: &str,
9789) -> Option<CppQualifiedNameComponent> {
9790    let mut is_template_id = false;
9791    loop {
9792        match component.kind() {
9793            "template_type" => {
9794                is_template_id = true;
9795                component = component.child_by_field_name("name")?;
9796            }
9797            "dependent_name" => component = component.named_child(0)?,
9798            "identifier"
9799            | "field_identifier"
9800            | "namespace_identifier"
9801            | "type_identifier"
9802            | "operator_name"
9803            | "destructor_name" => {
9804                let name = normalize_cpp_whitespace(node_text(component, source));
9805                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
9806                    name,
9807                    is_template_id,
9808                });
9809            }
9810            _ => component = component.child_by_field_name("name")?,
9811        }
9812    }
9813}
9814
9815fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
9816    if let Some(name) = macro_decorated_unqualified_name(node) {
9817        return extract_declarator_name(name, source);
9818    }
9819    match node.kind() {
9820        "identifier"
9821        | "field_identifier"
9822        | "type_identifier"
9823        | "operator_name"
9824        | "destructor_name"
9825        | "qualified_identifier" => node_text(node, source).to_string(),
9826        "function_declarator"
9827        | "pointer_declarator"
9828        | "reference_declarator"
9829        | "parenthesized_declarator"
9830        | "array_declarator"
9831        | "template_function" => node
9832            .child_by_field_name("declarator")
9833            .or_else(|| node.child_by_field_name("name"))
9834            .or_else(|| last_named_child(node))
9835            .map(|child| extract_declarator_name(child, source))
9836            .unwrap_or_else(|| node_text(node, source).to_string()),
9837        _ => node
9838            .child_by_field_name("name")
9839            .map(|child| extract_declarator_name(child, source))
9840            .unwrap_or_else(|| node_text(node, source).to_string()),
9841    }
9842}
9843
9844/// Extract a callable identity only through declaration-shaped AST nodes.
9845/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
9846/// expose the call's parameter list as a false function declarator; accepting
9847/// arbitrary node text there emitted bogus names such as `.*f`.
9848fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
9849    if let Some(name) = macro_decorated_unqualified_name(node) {
9850        return extract_callable_declarator_name(name, source);
9851    }
9852    match node.kind() {
9853        "identifier"
9854        | "field_identifier"
9855        | "type_identifier"
9856        | "operator_name"
9857        | "destructor_name"
9858        | "qualified_identifier" => Some(node_text(node, source).to_string()),
9859        "function_declarator"
9860        | "pointer_declarator"
9861        | "reference_declarator"
9862        | "parenthesized_declarator"
9863        | "array_declarator"
9864        | "template_function" => node
9865            .child_by_field_name("declarator")
9866            .or_else(|| node.child_by_field_name("name"))
9867            .and_then(|child| extract_callable_declarator_name(child, source)),
9868        _ => None,
9869    }
9870}
9871
9872fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
9873    match node.kind() {
9874        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
9875            let name = node_text(node, source).trim().to_string();
9876            (!name.is_empty()).then_some(name)
9877        }
9878        _ => node
9879            .child_by_field_name("declarator")
9880            .or_else(|| node.child_by_field_name("name"))
9881            .or_else(|| last_named_child(node))
9882            .and_then(|child| extract_variable_name(child, source)),
9883    }
9884}
9885
9886/// Recover a C field whose name is wrapped in the function-like
9887/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
9888/// function declarator, while retaining its argument as a structured child.
9889/// The exact macro name and one-argument shape keep ordinary function-pointer
9890/// fields and unknown malformed declarators fail-closed.
9891#[derive(Clone, Copy)]
9892pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
9893    pub(crate) name: Node<'tree>,
9894    pub(crate) declarator: Node<'tree>,
9895}
9896
9897impl RecoveredFunctionLikeFieldDeclarator<'_> {
9898    pub(crate) fn pointer_depth(self) -> i32 {
9899        let mut depth = 0;
9900        let mut current = self.declarator;
9901        while current.kind() != "function_declarator" {
9902            if current.kind() == "pointer_declarator" {
9903                depth += 1;
9904            }
9905            current = current
9906                .child_by_field_name("declarator")
9907                .expect("recovered field wrapper has an inner declarator");
9908        }
9909        depth
9910    }
9911}
9912
9913/// Whether a class-scope field declaration is really a function-like macro
9914/// invocation whose replacement the parser never saw.
9915///
9916/// The invocation carries no terminator of its own, so tree-sitter invents a
9917/// zero-width `;` and reads the macro name as the member's type and the
9918/// argument list as a `parenthesized_declarator`. That shape is not a member:
9919/// a member terminates itself, and with a real terminator the grammar reads
9920/// `T (name);` as a declaration rather than a field. Only an identifier-shaped
9921/// type can name the macro being invoked; a builtin type keeps whatever
9922/// recovery the ordinary declarator walk makes of it.
9923fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
9924    if node.kind() != "field_declaration" {
9925        return false;
9926    }
9927    let Some(type_node) = node.child_by_field_name("type") else {
9928        return false;
9929    };
9930    if !matches!(type_node.kind(), "identifier" | "type_identifier") {
9931        return false;
9932    }
9933    let Some(declarator) = node.child_by_field_name("declarator") else {
9934        return false;
9935    };
9936    if declarator.kind() != "parenthesized_declarator" {
9937        return false;
9938    }
9939    let mut invented_terminator = false;
9940    for child in children_iter(node) {
9941        if child.kind() != ";" {
9942            continue;
9943        }
9944        if !child.is_missing() {
9945            return false;
9946        }
9947        invented_terminator = true;
9948    }
9949    invented_terminator
9950}
9951
9952pub(crate) fn recovered_function_like_field_declarator<'tree>(
9953    node: Node<'tree>,
9954    source: &str,
9955) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
9956    if node.kind() != "field_declaration" {
9957        return None;
9958    }
9959    let outer_declarator = node.child_by_field_name("declarator")?;
9960    let mut declarator = outer_declarator;
9961    while matches!(
9962        declarator.kind(),
9963        "pointer_declarator"
9964            | "reference_declarator"
9965            | "array_declarator"
9966            | "parenthesized_declarator"
9967    ) {
9968        declarator = declarator.child_by_field_name("declarator")?;
9969    }
9970    if declarator.kind() != "function_declarator" {
9971        return None;
9972    }
9973    let macro_name = declarator.child_by_field_name("declarator")?;
9974    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
9975    {
9976        return None;
9977    }
9978    let parameters = declarator.child_by_field_name("parameters")?;
9979    let mut cursor = parameters.walk();
9980    let mut arguments = parameters.named_children(&mut cursor);
9981    let parameter = arguments.next()?;
9982    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
9983        return None;
9984    }
9985    let name = parameter.child_by_field_name("type").filter(|argument| {
9986        matches!(
9987            argument.kind(),
9988            "identifier" | "field_identifier" | "type_identifier"
9989        )
9990    })?;
9991    Some(RecoveredFunctionLikeFieldDeclarator {
9992        name,
9993        declarator: outer_declarator,
9994    })
9995}
9996
9997fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
9998    let count = node.named_child_count();
9999    if count == 0 {
10000        None
10001    } else {
10002        node.named_child(count - 1)
10003    }
10004}
10005
10006fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
10007    let name_node = node.child_by_field_name("name")?;
10008    let name = normalize_cpp_whitespace(node_text(name_node, source));
10009    (!name.is_empty()).then_some(name)
10010}
10011
10012fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
10013    if node.kind() != "declaration" {
10014        return Vec::new();
10015    }
10016    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
10017        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
10018    }) else {
10019        return Vec::new();
10020    };
10021    let Some(declarator) = node.child_by_field_name("declarator") else {
10022        return Vec::new();
10023    };
10024    if node_text(keyword, source) == "using"
10025        && (declarator.kind() != "init_declarator"
10026            || declarator.child_by_field_name("value").is_none())
10027    {
10028        return Vec::new();
10029    }
10030    if node_text(keyword, source) == "typedef"
10031        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
10032    {
10033        return vec![alias_name];
10034    }
10035    extract_typedef_declarator_name(declarator, source)
10036        .into_iter()
10037        .collect()
10038}
10039
10040fn recovered_typedef_error_alias_name(
10041    declaration: Node<'_>,
10042    declarator: Node<'_>,
10043    source: &str,
10044) -> Option<String> {
10045    // An export macro between `class` and its name can make tree-sitter parse
10046    // the recovered class body as a function body. In that shape,
10047    //
10048    //     typedef spi::Filter BASE_CLASS;
10049    //
10050    // becomes a declaration whose `declarator` is the underlying qualified
10051    // type (`spi::Filter`) and whose actual alias name is displaced into the
10052    // following ERROR node. Do not publish the terminal underlying type
10053    // (`Filter`) as a false class-owned alias.
10054    if declarator.kind() != "qualified_identifier" {
10055        return None;
10056    }
10057    let mut cursor = declaration.walk();
10058    let mut errors = declaration
10059        .named_children(&mut cursor)
10060        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
10061    let error = errors.next()?;
10062    if errors.next().is_some() || error.named_child_count() != 1 {
10063        return None;
10064    }
10065    let name = error.named_child(0)?;
10066    if !matches!(
10067        name.kind(),
10068        "identifier" | "field_identifier" | "type_identifier"
10069    ) {
10070        return None;
10071    }
10072    let name = normalize_cpp_whitespace(node_text(name, source));
10073    (!name.is_empty()).then_some(name)
10074}
10075
10076fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
10077    // A function-like token in the type position can make tree-sitter expose
10078    // its argument as a parenthesized declarator. Do not publish that argument
10079    // as an alias. The macro-specific recovery below handles the proven shape.
10080    if fragmented_parenthesized_typedef_type(node).is_some() {
10081        return Vec::new();
10082    }
10083    let has_function_like_macro_type = node
10084        .child_by_field_name("type")
10085        .filter(|type_node| type_node.kind() == "type_identifier")
10086        .is_some_and(|type_node| {
10087            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10088        });
10089    let mut names = Vec::new();
10090    let mut cursor = node.walk();
10091    for declarator in node.children_by_field_name("declarator", &mut cursor) {
10092        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
10093            continue;
10094        }
10095        if let Some(name) = extract_typedef_declarator_name(declarator, source)
10096            && !names.contains(&name)
10097        {
10098            names.push(name);
10099        }
10100    }
10101    names
10102}
10103
10104struct RecoveredMacroTypedefAlias<'tree> {
10105    name: String,
10106    end_node: Node<'tree>,
10107}
10108
10109/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
10110/// into an identifier expression statement. The uppercase macro token, missing
10111/// typedef terminator, and complete sibling terminator prove this exact shape.
10112fn recovered_macro_typedef_alias<'tree>(
10113    node: Node<'tree>,
10114    source: &str,
10115) -> Option<RecoveredMacroTypedefAlias<'tree>> {
10116    let type_node = fragmented_parenthesized_typedef_type(node)?;
10117    if type_node.kind() != "type_identifier"
10118        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10119    {
10120        return None;
10121    }
10122
10123    let end_node = node.next_named_sibling()?;
10124    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
10125        return None;
10126    }
10127    let name_node = end_node.named_child(0)?;
10128    if name_node.kind() != "identifier" {
10129        return None;
10130    }
10131    let has_terminator = (0..end_node.child_count()).any(|index| {
10132        end_node
10133            .child(index)
10134            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
10135    });
10136    if !has_terminator {
10137        return None;
10138    }
10139    let name = normalize_cpp_whitespace(node_text(name_node, source));
10140    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
10141}
10142
10143fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
10144    if node.kind() != "type_definition" {
10145        return None;
10146    }
10147    let mut declarator_cursor = node.walk();
10148    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
10149    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
10150        return None;
10151    }
10152    let has_missing_terminator = (0..node.child_count()).any(|index| {
10153        node.child(index)
10154            .is_some_and(|child| child.kind() == ";" && child.is_missing())
10155    });
10156    if !has_missing_terminator {
10157        return None;
10158    }
10159    node.child_by_field_name("type")
10160}
10161
10162fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
10163    match node.kind() {
10164        "identifier" | "field_identifier" | "type_identifier" => {
10165            let name = normalize_cpp_whitespace(node_text(node, source));
10166            (!name.is_empty()).then_some(name)
10167        }
10168        "qualified_identifier" => node
10169            .child_by_field_name("name")
10170            .and_then(|name| extract_typedef_declarator_name(name, source)),
10171        _ => node
10172            .child_by_field_name("declarator")
10173            .or_else(|| node.child_by_field_name("name"))
10174            .or_else(|| last_named_child(node))
10175            .and_then(|child| extract_typedef_declarator_name(child, source)),
10176    }
10177}
10178
10179fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
10180    let name = node
10181        .child_by_field_name("name")
10182        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10183        .or_else(|| {
10184            let mut cursor = node.walk();
10185            node.named_children(&mut cursor)
10186                .find(|child| {
10187                    matches!(
10188                        child.kind(),
10189                        "identifier" | "field_identifier" | "type_identifier"
10190                    )
10191                })
10192                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10193        })?;
10194    (!name.is_empty()).then_some(name)
10195}
10196
10197fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
10198    left.id() == right.id()
10199}
10200
10201fn render_cpp_type_signature(
10202    node: Node<'_>,
10203    source: &str,
10204    template_signature: Option<&str>,
10205) -> String {
10206    let text = normalize_cpp_whitespace(node_text(node, source));
10207    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
10208    let rendered = if head.ends_with(';') {
10209        head.to_string()
10210    } else {
10211        format!("{head} {{")
10212    };
10213    if let Some(template_signature) = template_signature {
10214        format!("template {template_signature} {rendered}")
10215    } else {
10216        rendered
10217    }
10218}
10219
10220fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
10221    if let Some(recovered) = recovered_pyobject_head_field(node, source)
10222        && recovered.declarator == declarator
10223    {
10224        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
10225        let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
10226        return format!("{type_text} {declarator};");
10227    }
10228    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
10229        && recovered.name == declarator
10230    {
10231        let type_text = node
10232            .child_by_field_name("type")
10233            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
10234            .unwrap_or_default();
10235        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
10236        let mut prefix = String::new();
10237        let mut suffix = String::new();
10238        let mut current = recovered.declarator;
10239        while current.kind() != "function_declarator" {
10240            match current.kind() {
10241                "pointer_declarator" => prefix.push('*'),
10242                "reference_declarator" => prefix.push('&'),
10243                "array_declarator" => {
10244                    let size = current
10245                        .child_by_field_name("size")
10246                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
10247                        .unwrap_or_default();
10248                    suffix.push('[');
10249                    suffix.push_str(&size);
10250                    suffix.push(']');
10251                }
10252                "parenthesized_declarator" => {}
10253                _ => unreachable!("validated recovered field declarator wrapper"),
10254            }
10255            current = current
10256                .child_by_field_name("declarator")
10257                .expect("recovered field wrapper has an inner declarator");
10258        }
10259        let separator = if prefix.is_empty() { "" } else { " " };
10260        return format!("{type_text} {prefix}{separator}{name}{suffix};");
10261    }
10262    if let Some(signature) =
10263        render_recovered_macro_qualified_field_signature(node, declarator, source)
10264    {
10265        return signature;
10266    }
10267    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
10268    let prefix = cpp_declaration_prefix(node, source);
10269    let name = extract_variable_name(declarator, source).unwrap_or_default();
10270    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
10271    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
10272        || (prefix.ends_with('&') && raw_suffix == "&")
10273    {
10274        String::new()
10275    } else {
10276        raw_suffix
10277    };
10278
10279    let mut rendered = if suffix.is_empty() {
10280        format!("{prefix} {name}")
10281    } else if suffix.starts_with('*') || suffix.starts_with('&') {
10282        format!("{prefix}{suffix} {name}")
10283    } else if suffix.starts_with('[') || suffix.starts_with('(') {
10284        format!("{prefix} {name}{suffix}")
10285    } else {
10286        format!("{prefix} {suffix}{name}")
10287    };
10288    rendered = collapse_cpp_whitespace(&rendered);
10289
10290    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10291        format!("{rendered} = {initializer};")
10292    } else if declaration_text.ends_with(';') {
10293        format!("{rendered};")
10294    } else {
10295        rendered
10296    }
10297}
10298
10299fn render_recovered_macro_qualified_field_signature(
10300    node: Node<'_>,
10301    declarator: Node<'_>,
10302    source: &str,
10303) -> Option<String> {
10304    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
10305    if !recovered
10306        .iter()
10307        .any(|candidate| same_node(*candidate, declarator))
10308    {
10309        return None;
10310    }
10311    let pseudo_declarator = node.child_by_field_name("declarator")?;
10312    let mut cursor = node.walk();
10313    let clause = node
10314        .named_children(&mut cursor)
10315        .find(|child| child.kind() == "bitfield_clause")?;
10316    let mut cursor = clause.walk();
10317    let error = clause
10318        .named_children(&mut cursor)
10319        .find(|child| child.kind() == "ERROR")?;
10320    let qualified_type =
10321        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
10322    let prefix = cpp_declaration_prefix(node, source);
10323    let name = extract_variable_name(declarator, source)?;
10324    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
10325    let mut rendered = if suffix.is_empty() {
10326        format!("{prefix} {qualified_type} {name}")
10327    } else {
10328        format!("{prefix} {qualified_type} {suffix} {name}")
10329    };
10330    rendered = collapse_cpp_whitespace(&rendered);
10331
10332    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
10333        Some(format!(
10334            "{rendered} = {};",
10335            normalize_cpp_whitespace(node_text(initializer, source))
10336        ))
10337    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10338        Some(format!("{rendered} = {initializer};"))
10339    } else {
10340        Some(format!("{rendered};"))
10341    }
10342}
10343
10344fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
10345    match node.kind() {
10346        "pointer_expression" => {
10347            let operator = node
10348                .child_by_field_name("operator")
10349                .or_else(|| node.child(0))
10350                .map(|operator| node_text(operator, source))
10351                .unwrap_or("*");
10352            let argument = node
10353                .child_by_field_name("argument")
10354                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10355                .unwrap_or_default();
10356            format!("{operator}{argument}")
10357        }
10358        "unary_expression" => {
10359            let operator = node
10360                .child_by_field_name("operator")
10361                .or_else(|| node.child(0))
10362                .map(|operator| node_text(operator, source))
10363                .unwrap_or_default();
10364            let argument = node
10365                .child_by_field_name("argument")
10366                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10367                .unwrap_or_default();
10368            format!("{operator}{argument}")
10369        }
10370        "identifier" | "field_identifier" => String::new(),
10371        _ => cpp_declarator_suffix_without_name(node, source),
10372    }
10373}
10374
10375fn recovered_macro_qualified_field_initializer<'tree>(
10376    clause: Node<'tree>,
10377    declarator: Node<'tree>,
10378) -> Option<Node<'tree>> {
10379    let mut stack = vec![clause];
10380    while let Some(current) = stack.pop() {
10381        if current.kind() == "assignment_expression"
10382            && current
10383                .child_by_field_name("left")
10384                .is_some_and(|left| same_node(left, declarator))
10385        {
10386            return current.child_by_field_name("right");
10387        }
10388        let mut cursor = current.walk();
10389        stack.extend(current.named_children(&mut cursor));
10390    }
10391    None
10392}
10393
10394fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
10395    let text = node_text(node, source);
10396    let mut cursor = node.walk();
10397    let first_declarator = node.named_children(&mut cursor).find(|child| {
10398        matches!(
10399            child.kind(),
10400            "init_declarator"
10401                | "identifier"
10402                | "field_identifier"
10403                | "pointer_declarator"
10404                | "reference_declarator"
10405                | "array_declarator"
10406                | "function_declarator"
10407        )
10408    });
10409    let prefix = if let Some(first_declarator) = first_declarator {
10410        let end = first_declarator
10411            .start_byte()
10412            .saturating_sub(node.start_byte());
10413        let mut prefix = text.get(..end).unwrap_or(text).to_string();
10414        let declarator_suffix = match first_declarator.kind() {
10415            "init_declarator" => first_declarator
10416                .child_by_field_name("declarator")
10417                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
10418                .unwrap_or_default(),
10419            _ => cpp_declarator_suffix_without_name(first_declarator, source),
10420        };
10421        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
10422            prefix.push_str(&declarator_suffix);
10423        }
10424        return collapse_cpp_whitespace(&prefix)
10425            .trim_end_matches(',')
10426            .trim_end_matches(';')
10427            .trim()
10428            .to_string();
10429    } else {
10430        text
10431    };
10432    collapse_cpp_whitespace(prefix)
10433        .trim_end_matches(',')
10434        .trim_end_matches(';')
10435        .trim()
10436        .to_string()
10437}
10438
10439fn cpp_preserved_initializer(
10440    declaration_node: Node<'_>,
10441    declarator: Node<'_>,
10442    source: &str,
10443) -> Option<String> {
10444    let name = extract_variable_name(declarator, source)?;
10445    let mut cursor = declaration_node.walk();
10446    for child in declaration_node.named_children(&mut cursor) {
10447        if child.kind() != "init_declarator" {
10448            continue;
10449        }
10450        let Some(inner) = child.child_by_field_name("declarator") else {
10451            continue;
10452        };
10453        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
10454            continue;
10455        }
10456        let value = child.child_by_field_name("value")?;
10457        let kind = value.kind();
10458        if matches!(
10459            kind,
10460            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
10461        ) {
10462            return Some(normalize_cpp_whitespace(node_text(value, source)));
10463        }
10464        break;
10465    }
10466    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
10467    let pattern = format!(
10468        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
10469        regex::escape(&name)
10470    );
10471    Regex::new(&pattern)
10472        .ok()
10473        .and_then(|regex| regex.captures(&declaration_text))
10474        .and_then(|captures| captures.get(1))
10475        .map(|value| value.as_str().to_string())
10476}
10477
10478fn render_cpp_function_display_signature_from_node<'tree>(
10479    node: Node<'tree>,
10480    source: &str,
10481    template_signature: Option<&str>,
10482    has_body: bool,
10483    ancestry: &ParentIndex<'tree>,
10484) -> String {
10485    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
10486    let parent_text = node_text(root, source);
10487    let body_local_start = root
10488        .child_by_field_name("body")
10489        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
10490        .unwrap_or(parent_text.len());
10491    let display = parent_text
10492        .get(..body_local_start)
10493        .unwrap_or(parent_text)
10494        .trim()
10495        .trim();
10496    let display = if let Some(template_signature) = template_signature {
10497        if display.starts_with("template ") {
10498            display.to_string()
10499        } else {
10500            format!("template {template_signature} {display}")
10501        }
10502    } else {
10503        display.to_string()
10504    };
10505    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
10506    if has_body {
10507        format!("{display} {{...}}")
10508    } else {
10509        format!("{display};")
10510    }
10511}
10512
10513fn cpp_template_signature(
10514    template_node: Node<'_>,
10515    declaration_child: Node<'_>,
10516    source: &str,
10517) -> Option<String> {
10518    let text = source
10519        .get(template_node.start_byte()..declaration_child.start_byte())
10520        .unwrap_or("");
10521    let text = normalize_cpp_whitespace(text);
10522    let start = text.find('<')?;
10523    let end = text.rfind('>')?;
10524    if end < start {
10525        return None;
10526    }
10527    Some(text[start..=end].to_string())
10528}
10529
10530struct RecoveredFragmentedPartialSpecialization<'tree> {
10531    declaration_node: Node<'tree>,
10532    name: String,
10533    range: Range,
10534    prefix_members: Vec<Node<'tree>>,
10535    member_siblings: Vec<Node<'tree>>,
10536    following_declarations: Vec<Node<'tree>>,
10537}
10538
10539struct RecoveredFragmentedPreprocessorClass<'tree> {
10540    declaration_node: Node<'tree>,
10541    class_node: Node<'tree>,
10542    body: Node<'tree>,
10543    name: String,
10544    range: Range,
10545    tail_members: Vec<Node<'tree>>,
10546    member_siblings: Vec<Node<'tree>>,
10547}
10548
10549/// Recover a class whose preprocessor-fragmented parse closes at an early
10550/// member body and publishes the remaining in-class declarations as siblings
10551/// of the surrounding alternative. Primary classes are admitted only when an
10552/// earlier branch contains the matching bodyless declaration and the class
10553/// node retains the displaced `#endif`. Partial specializations instead carry
10554/// their identity structurally in the `template_type` name and template
10555/// metadata. Retain the original AST nodes and re-own only the siblings through
10556/// the displaced structural `};` terminator.
10557fn recover_fragmented_preprocessor_class<'tree>(
10558    template_node: Node<'tree>,
10559    source: &str,
10560    ancestry: &ParentIndex<'tree>,
10561) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
10562    let alternative = ancestry.parent(template_node)?;
10563    if alternative.kind() != "preproc_else" {
10564        return None;
10565    }
10566    let conditional = alternative.parent()?;
10567    if conditional.kind() != "preproc_if" {
10568        return None;
10569    }
10570    let declaration_node = template_node
10571        .named_children(&mut template_node.walk())
10572        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
10573    let class_node = declaration_node
10574        .named_children(&mut declaration_node.walk())
10575        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
10576    let body = cpp_body_node(class_node)?;
10577    if class_node.end_byte() >= declaration_node.end_byte() {
10578        return None;
10579    }
10580    let name = class_like_name(class_node, source, ancestry)?;
10581    let is_partial_specialization = class_node
10582        .child_by_field_name("name")
10583        .is_some_and(|class_name| class_name.kind() == "template_type");
10584    if is_partial_specialization {
10585        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
10586        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
10587            return None;
10588        }
10589    } else {
10590        if !class_has_displaced_preprocessor_terminator(class_node) {
10591            return None;
10592        }
10593        let matching_other_branch = conditional
10594            .named_children(&mut conditional.walk())
10595            .take_while(|child| !same_node(*child, alternative))
10596            .filter(|child| child.kind() == "template_declaration")
10597            .filter_map(first_class_like_child)
10598            .any(|candidate| {
10599                cpp_body_node(candidate).is_none()
10600                    && class_like_name(candidate, source, ancestry).as_deref()
10601                        == Some(name.as_str())
10602            });
10603        if !matching_other_branch {
10604            return None;
10605        }
10606    }
10607
10608    let mut tail_members = Vec::new();
10609    let mut saw_class = false;
10610    let mut declaration_cursor = declaration_node.walk();
10611    for child in declaration_node.named_children(&mut declaration_cursor) {
10612        if same_node(child, class_node) {
10613            saw_class = true;
10614        } else if saw_class {
10615            tail_members.push(child);
10616        }
10617    }
10618
10619    let mut member_siblings = Vec::new();
10620    let mut saw_template = false;
10621    let mut terminator = None;
10622    for index in 0..alternative.child_count() {
10623        let Some(child) = alternative.child(index) else {
10624            continue;
10625        };
10626        if same_node(child, template_node) {
10627            saw_template = true;
10628            continue;
10629        }
10630        if !saw_template {
10631            continue;
10632        }
10633        if displaced_fragmented_class_terminator(alternative, index) {
10634            terminator = alternative.child(index + 1);
10635            break;
10636        }
10637        if child.is_named() {
10638            member_siblings.push(child);
10639        }
10640    }
10641    let terminator = terminator?;
10642    Some(RecoveredFragmentedPreprocessorClass {
10643        declaration_node,
10644        class_node,
10645        body,
10646        name,
10647        range: Range {
10648            start_byte: class_node.start_byte(),
10649            end_byte: terminator.end_byte(),
10650            start_line: class_node.start_position().row + 1,
10651            end_line: terminator.end_position().row + 1,
10652        },
10653        tail_members,
10654        member_siblings,
10655    })
10656}
10657
10658fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
10659    (0..class_node.child_count()).any(|index| {
10660        class_node.child(index).is_some_and(|child| {
10661            child.kind() == "ERROR"
10662                && (0..child.child_count()).any(|error_index| {
10663                    child
10664                        .child(error_index)
10665                        .is_some_and(|token| token.kind() == "#endif")
10666                })
10667        })
10668    })
10669}
10670
10671/// The real `#endif` that tree-sitter consumed inside an error subtree.
10672///
10673/// A preprocessor directive inside a malformed array bound can cause later
10674/// declarations to remain children of the conditional. The non-missing token
10675/// still gives the exact structured boundary. Ignore nested conditionals and
10676/// select the last unpaired error-owned token. Tree-sitter can pair a later
10677/// outer `#endif` with this conditional, so the direct terminator is not
10678/// necessarily missing.
10679pub fn cpp_displaced_preprocessor_terminator<'tree>(
10680    conditional: Node<'tree>,
10681) -> Option<Node<'tree>> {
10682    if !conditional.has_error() {
10683        return None;
10684    }
10685    let has_concrete_direct_terminator = conditional
10686        .child_count()
10687        .checked_sub(1)
10688        .and_then(|index| conditional.child(index))
10689        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
10690    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
10691        // A structured alternative proves that the direct `#endif` closes
10692        // this family. An error-owned terminator inside either branch belongs
10693        // to a damaged nested conditional, not to this one.
10694        return None;
10695    }
10696    // Pair concrete directive tokens while walking the recovered subtree. An
10697    // inner conditional may be represented only by its `#ifndef`/`#endif`
10698    // tokens under an `ERROR`, so skipping nested preprocessor nodes is not
10699    // sufficient: an inner `#endif` can otherwise be mistaken for this
10700    // conditional's displaced terminator.
10701    let mut displaced = None;
10702    let mut conditional_depth = 0usize;
10703    let mut stack = children_iter(conditional)
10704        .map(|child| (child, false))
10705        .collect::<Vec<_>>();
10706    stack.reverse();
10707    while let Some((node, inside_error)) = stack.pop() {
10708        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
10709            continue;
10710        }
10711        if node != conditional
10712            && matches!(
10713                node.kind(),
10714                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
10715            )
10716        {
10717            continue;
10718        }
10719        let inside_error = inside_error || node.kind() == "ERROR";
10720        match node.kind() {
10721            "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
10722                conditional_depth += 1;
10723            }
10724            "#endif" if !node.is_missing() => {
10725                if conditional_depth == 0
10726                    && inside_error
10727                    && displaced
10728                        .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
10729                {
10730                    displaced = Some(node);
10731                }
10732                conditional_depth = conditional_depth.saturating_sub(1);
10733            }
10734            _ => {}
10735        }
10736        let first_pushed = stack.len();
10737        stack.extend(children_iter(node).map(|child| (child, inside_error)));
10738        stack[first_pushed..].reverse();
10739    }
10740    displaced
10741}
10742
10743/// The effective end of a conditional whose real terminator tree-sitter
10744/// displaced into declaration recovery.
10745///
10746/// Most damaged conditionals retain a concrete `#endif` token below an
10747/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
10748/// boundary. A preprocessor family that selects the middle of a declaration
10749/// can lose the directive tokens entirely. In that shape tree-sitter leaves
10750/// the declaration's `typedef` token as the sole child of the immediately
10751/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
10752/// declarator name inside the conditional's first declaration. The declaration
10753/// end is then the smallest structured boundary that contains the whole split
10754/// declaration.
10755#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10756pub struct CppDisplacedPreprocessorBoundary {
10757    pub end_byte: usize,
10758    pub end_line: usize,
10759}
10760
10761pub fn cpp_displaced_preprocessor_boundary(
10762    conditional: Node<'_>,
10763) -> Option<CppDisplacedPreprocessorBoundary> {
10764    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
10765        return Some(CppDisplacedPreprocessorBoundary {
10766            end_byte: terminator.end_byte(),
10767            end_line: terminator.end_position().row + 1,
10768        });
10769    }
10770    if let Some(declaration) = displaced_split_declaration(conditional) {
10771        return Some(CppDisplacedPreprocessorBoundary {
10772            end_byte: declaration.end_byte(),
10773            end_line: declaration.end_position().row + 1,
10774        });
10775    }
10776    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
10777        return Some(CppDisplacedPreprocessorBoundary {
10778            end_byte: terminator.end_byte(),
10779            end_line: terminator.end_position().row + 1,
10780        });
10781    }
10782    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
10783        return Some(CppDisplacedPreprocessorBoundary {
10784            end_byte: terminator.end_byte(),
10785            end_line: terminator.end_position().row + 1,
10786        });
10787    }
10788    None
10789}
10790
10791/// Recover an outer terminator that tree-sitter assigned to a damaged nested
10792/// conditional. This occurs when a split construct such as `extern "C"`
10793/// consumes the nested `#endif` inside an error node: the nested conditional's
10794/// direct terminator is then the outer conditional's real terminator, while
10795/// the outer node ends with a missing token and absorbs later declarations.
10796fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10797    if !conditional.has_error()
10798        || conditional.child_by_field_name("alternative").is_some()
10799        || conditional
10800            .child(conditional.child_count().saturating_sub(1))
10801            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
10802    {
10803        return None;
10804    }
10805    let mut recovered = None;
10806    for nested in named_children_iter(conditional) {
10807        if !matches!(
10808            nested.kind(),
10809            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
10810        ) || nested.child_by_field_name("alternative").is_some()
10811        {
10812            continue;
10813        }
10814        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
10815            continue;
10816        };
10817        if direct.kind() != "#endif" || direct.is_missing() {
10818            continue;
10819        }
10820        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
10821            continue;
10822        };
10823        if displaced.end_byte() >= direct.start_byte() {
10824            continue;
10825        }
10826        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
10827            recovered = Some(direct);
10828        }
10829    }
10830    recovered
10831}
10832
10833fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10834    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
10835        return None;
10836    }
10837    let mut cursor = conditional.walk();
10838    let declarations = conditional
10839        .named_children(&mut cursor)
10840        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
10841        .collect::<Vec<_>>();
10842    let declaration = *declarations.first()?;
10843    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
10844        return None;
10845    }
10846    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
10847    let mut terminator = None;
10848    let mut stack = (0..declaration.child_count())
10849        .filter_map(|index| declaration.child(index))
10850        .filter(|child| child.start_byte() < declarator_start)
10851        .map(|child| (child, false))
10852        .collect::<Vec<_>>();
10853    while let Some((node, inside_error)) = stack.pop() {
10854        let inside_error = inside_error || node.kind() == "ERROR";
10855        if inside_error && node.kind() == "#endif" && !node.is_missing() {
10856            terminator = Some(node);
10857            continue;
10858        }
10859        for index in 0..node.child_count() {
10860            if let Some(child) = node.child(index)
10861                && child.start_byte() < declarator_start
10862            {
10863                stack.push((child, inside_error));
10864            }
10865        }
10866    }
10867    terminator
10868}
10869
10870fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10871    if !conditional.has_error()
10872        || conditional.child_by_field_name("alternative").is_some()
10873        || conditional
10874            .prev_named_sibling()
10875            .filter(|sibling| {
10876                sibling.kind() == "ERROR"
10877                    && sibling.child_count() == 1
10878                    && sibling
10879                        .child(0)
10880                        .is_some_and(|child| child.kind() == "typedef")
10881            })
10882            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
10883            .is_none()
10884    {
10885        return None;
10886    }
10887    let mut cursor = conditional.walk();
10888    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
10889    let declaration_index = children
10890        .iter()
10891        .position(|child| child.kind() == "declaration" && child.has_error())?;
10892    let declaration = children[declaration_index];
10893    if !children
10894        .iter()
10895        .skip(declaration_index + 1)
10896        .any(|child| child.end_byte() > declaration.end_byte())
10897    {
10898        return None;
10899    }
10900    let declarator = declaration.child_by_field_name("declarator")?;
10901    let mut error_end = None;
10902    let mut names = Vec::new();
10903    let mut stack = vec![declarator];
10904    while let Some(node) = stack.pop() {
10905        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
10906            error_end =
10907                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
10908            continue;
10909        }
10910        if matches!(node.kind(), "identifier" | "type_identifier") {
10911            names.push(node.start_byte());
10912        }
10913        push_named_children_reversed(node, &mut stack);
10914    }
10915    let error_end = error_end?;
10916    names
10917        .into_iter()
10918        .any(|start| start >= error_end)
10919        .then_some(declaration)
10920}
10921
10922fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
10923    let Some(error) = parent.child(error_index) else {
10924        return false;
10925    };
10926    if error.kind() != "ERROR"
10927        || error.child_count() != 1
10928        || error.child(0).is_none_or(|child| child.kind() != "}")
10929    {
10930        return false;
10931    }
10932    let Some(semicolon) = parent.child(error_index + 1) else {
10933        return false;
10934    };
10935    semicolon.kind() == "expression_statement"
10936        && semicolon.child_count() == 1
10937        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
10938}
10939
10940/// Locate the real end of a class-like declaration when a macro invocation
10941/// without a source semicolon absorbs the class's `};` into its parsed field.
10942/// The grammar then keeps following namespace declarations as later children
10943/// of the same field list. The direct ERROR-plus-semicolon pair proves the
10944/// boundary structurally; no source-text delimiter scan is needed.
10945fn displaced_macro_class_tail(
10946    declaration_node: Node<'_>,
10947    body: Node<'_>,
10948    source: &str,
10949) -> Option<DisplacedMacroClassTail> {
10950    if !matches!(
10951        declaration_node.kind(),
10952        "class_specifier" | "struct_specifier" | "union_specifier"
10953    ) || body.kind() != "field_declaration_list"
10954    {
10955        return None;
10956    }
10957
10958    let child_count = body.named_child_count();
10959    for index in 0..child_count {
10960        let child = body.named_child(index)?;
10961        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
10962            continue;
10963        };
10964        let split_index = index + 1;
10965        if split_index >= child_count {
10966            return None;
10967        }
10968        let mut cursor = body.walk();
10969        if !body
10970            .named_children(&mut cursor)
10971            .skip(split_index)
10972            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
10973        {
10974            return None;
10975        }
10976        return Some(DisplacedMacroClassTail {
10977            split_index,
10978            class_range: Range {
10979                start_byte: declaration_node.start_byte(),
10980                end_byte: terminator.end_byte(),
10981                start_line: declaration_node.start_position().row + 1,
10982                end_line: terminator.end_position().row + 1,
10983            },
10984        });
10985    }
10986    None
10987}
10988
10989fn displaced_macro_field_terminator<'tree>(
10990    field: Node<'tree>,
10991    source: &str,
10992) -> Option<Node<'tree>> {
10993    if field.kind() != "field_declaration" {
10994        return None;
10995    }
10996    let macro_type = field.child_by_field_name("type")?;
10997    if macro_type.kind() != "type_identifier"
10998        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10999        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
11000    {
11001        return None;
11002    }
11003    for index in 0..field.child_count() {
11004        let error = field.child(index)?;
11005        if error.kind() != "ERROR"
11006            || error.child_count() != 1
11007            || error.child(0).is_none_or(|child| child.kind() != "}")
11008        {
11009            continue;
11010        }
11011        let semicolon = field.child(index + 1)?;
11012        if semicolon.kind() == ";" {
11013            return Some(semicolon);
11014        }
11015    }
11016    None
11017}
11018
11019fn recover_fragmented_partial_specialization<'tree>(
11020    template_node: Node<'tree>,
11021    declaration_child: Node<'tree>,
11022    source: &str,
11023    ancestry: &ParentIndex<'tree>,
11024) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
11025    if declaration_child.kind() != "function_definition" {
11026        return None;
11027    }
11028    let class_node = declaration_child.child_by_field_name("type")?;
11029    if !matches!(
11030        class_node.kind(),
11031        "class_specifier" | "struct_specifier" | "union_specifier"
11032    ) || !class_node
11033        .child_by_field_name("name")
11034        .and_then(|name| direct_identifier_name(name, source))
11035        .is_some_and(|name| cpp_export_macro_token(&name))
11036    {
11037        return None;
11038    }
11039    let declarator = declaration_child.child_by_field_name("declarator")?;
11040    if declarator.kind() != "template_function" {
11041        return None;
11042    }
11043    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
11044    if metadata.specialization_arguments.is_empty() {
11045        return None;
11046    }
11047    let body = declaration_child.child_by_field_name("body")?;
11048    if body.kind() != "compound_statement" {
11049        return None;
11050    }
11051    let complete_prefix = body.named_child(0).filter(|first| {
11052        first.kind() == "labeled_statement"
11053            && first.has_error()
11054            && first
11055                .named_child(first.named_child_count().saturating_sub(1))
11056                .is_some_and(recovered_declaration_has_class_terminator)
11057    });
11058    let complete_body = complete_prefix.is_some();
11059    let mut prefix_members = Vec::new();
11060    if let Some(prefix) = complete_prefix {
11061        prefix_members.push(prefix);
11062    } else {
11063        let mut body_cursor = body.walk();
11064        for child in body.named_children(&mut body_cursor) {
11065            if !is_structurally_valid_fragmented_class_prefix_member(child) {
11066                break;
11067            }
11068            prefix_members.push(child);
11069        }
11070    }
11071    let containing_declarations = template_node.parent()?;
11072    if !matches!(
11073        containing_declarations.kind(),
11074        "declaration_list" | "compound_statement"
11075    ) {
11076        return None;
11077    }
11078    let mut member_siblings = Vec::new();
11079    let mut following_declarations = Vec::new();
11080    let terminator;
11081    if complete_body {
11082        terminator = complete_prefix?;
11083        let mut cursor = body.walk();
11084        let mut after_prefix = false;
11085        for child in body.named_children(&mut cursor) {
11086            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
11087                after_prefix = true;
11088            } else if after_prefix {
11089                following_declarations.push(child);
11090            }
11091        }
11092    } else {
11093        let mut found_template = false;
11094        let mut cursor = containing_declarations.walk();
11095        let mut class_terminator = None;
11096        for child in containing_declarations.children(&mut cursor) {
11097            if same_node(child, template_node) {
11098                found_template = true;
11099                continue;
11100            }
11101            if found_template && child.kind() == "}" {
11102                class_terminator = Some(child);
11103                break;
11104            }
11105            // A namespace can never be a class member: reaching one before the
11106            // terminator proves the class's true close was swallowed upstream
11107            // and this scan has crossed into the enclosing scope, so the
11108            // recovery cannot be bounded -- continuing re-owns the namespace
11109            // block (and its template specializations) as class members under
11110            // a re-appended package, desyncing the fq boundary (#2306).
11111            if found_template && child.kind() == "namespace_definition" {
11112                return None;
11113            }
11114            if found_template && child.is_named() {
11115                member_siblings.push(child);
11116            }
11117        }
11118        terminator = class_terminator?;
11119    }
11120    let name = format!(
11121        "{}<{}>",
11122        metadata.primary_name,
11123        metadata
11124            .specialization_arguments
11125            .iter()
11126            .map(|argument| argument.text.as_str())
11127            .collect::<Vec<_>>()
11128            .join(", ")
11129    );
11130    Some(RecoveredFragmentedPartialSpecialization {
11131        declaration_node: declaration_child,
11132        name,
11133        range: Range {
11134            start_byte: declaration_child.start_byte(),
11135            end_byte: terminator.end_byte(),
11136            start_line: declaration_child.start_position().row + 1,
11137            end_line: terminator.end_position().row + 1,
11138        },
11139        prefix_members,
11140        member_siblings,
11141        following_declarations,
11142    })
11143}
11144
11145/// Whether `node` is the function-shaped parser wrapper whose body was
11146/// recovered as a fragmented partial-specialization class body.
11147///
11148/// Consumers that walk lexical function scopes must use the same complete
11149/// structural proof as declaration extraction. Otherwise member callables in
11150/// the recovered body are mistaken for function-local declarations.
11151pub fn is_recovered_fragmented_partial_specialization_container(
11152    node: Node<'_>,
11153    source: &str,
11154) -> bool {
11155    let Some(template) = node
11156        .parent()
11157        .filter(|parent| parent.kind() == "template_declaration")
11158    else {
11159        return false;
11160    };
11161    let mut root = template;
11162    while let Some(parent) = root.parent() {
11163        root = parent;
11164    }
11165    recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
11166        .is_some()
11167}
11168
11169fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
11170    if declaration.kind() != "declaration" {
11171        return false;
11172    }
11173    // With an export macro between `class` and its name, tree-sitter folds a
11174    // complete class body into a function-shaped declaration. The class's own
11175    // `};` remains structurally identifiable as a direct ERROR child holding
11176    // `}`, immediately followed by the declaration's direct `;` child.
11177    (0..declaration.child_count().saturating_sub(1)).any(|index| {
11178        let Some(error) = declaration.child(index) else {
11179            return false;
11180        };
11181        error.kind() == "ERROR"
11182            && error.child_count() == 1
11183            && error.child(0).is_some_and(|child| child.kind() == "}")
11184            && declaration
11185                .child(index + 1)
11186                .is_some_and(|child| child.kind() == ";")
11187    })
11188}
11189
11190fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
11191    if node.has_error() {
11192        return false;
11193    }
11194    match node.kind() {
11195        "declaration"
11196        | "field_declaration"
11197        | "alias_declaration"
11198        | "type_definition"
11199        | "static_assert_declaration" => true,
11200        "labeled_statement" => node
11201            .named_child(node.named_child_count().saturating_sub(1))
11202            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
11203        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
11204            matches!(
11205                child.kind(),
11206                "declaration"
11207                    | "field_declaration"
11208                    | "alias_declaration"
11209                    | "type_definition"
11210                    | "function_definition"
11211            )
11212        }),
11213        _ => false,
11214    }
11215}
11216
11217fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
11218    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
11219        .then(|| node.child_by_field_name("declarator"))
11220        .flatten()
11221        .and_then(|declarator| extract_variable_name(declarator, source))
11222}
11223
11224fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
11225    while let Some(parent) = node.parent() {
11226        if matches!(parent.kind(), "function_definition" | "lambda_expression") {
11227            return true;
11228        }
11229        node = parent;
11230    }
11231    false
11232}
11233
11234fn cpp_template_metadata<'tree>(
11235    template_node: Node<'tree>,
11236    declaration_child: Node<'tree>,
11237    source: &str,
11238    ancestry: &ParentIndex<'tree>,
11239) -> Option<CppTemplateMetadata> {
11240    let parameters_node = template_node.child_by_field_name("parameters")?;
11241    let name_node = cpp_templated_class_name_node(declaration_child)?;
11242    let primary_node = match name_node.kind() {
11243        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
11244        _ => name_node,
11245    };
11246    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
11247    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
11248        return None;
11249    }
11250
11251    let mut parameter_nodes = Vec::new();
11252    let mut parameter_names = Vec::new();
11253    let mut cursor = parameters_node.walk();
11254    for parameter in parameters_node.named_children(&mut cursor) {
11255        if !matches!(
11256            parameter.kind(),
11257            "type_parameter_declaration"
11258                | "optional_type_parameter_declaration"
11259                | "variadic_type_parameter_declaration"
11260                | "template_template_parameter_declaration"
11261                | "parameter_declaration"
11262                | "optional_parameter_declaration"
11263                | "variadic_parameter_declaration"
11264        ) {
11265            continue;
11266        }
11267        let index = parameter_nodes.len();
11268        // An unnamed parameter still contributes template arity and kind. Use
11269        // an impossible C++ identifier so positional reconciliation can bind
11270        // it without making source expressions refer to a name that was not
11271        // written.
11272        let name = cpp_template_parameter_name(parameter, source)
11273            .unwrap_or_else(|| format!("<anonymous:{index}>"));
11274        parameter_names.push(name);
11275        parameter_nodes.push(parameter);
11276    }
11277    let parameters = parameter_nodes
11278        .into_iter()
11279        .zip(parameter_names.iter().cloned())
11280        .map(|(parameter, name)| CppTemplateParameterMetadata {
11281            name,
11282            kind: cpp_template_parameter_kind(parameter),
11283            variadic: matches!(
11284                parameter.kind(),
11285                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
11286            ),
11287            default: cpp_template_parameter_default_expression(
11288                parameter,
11289                source,
11290                &parameter_names,
11291                ancestry,
11292            ),
11293        })
11294        .collect();
11295    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
11296        Vec::new()
11297    } else {
11298        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
11299            .unwrap_or_default()
11300    };
11301    let alias_target = (declaration_child.kind() == "alias_declaration")
11302        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
11303        .flatten();
11304    Some(CppTemplateMetadata {
11305        primary_name,
11306        primary_fq_name: String::new(),
11307        parameters,
11308        specialization_arguments,
11309        alias_target,
11310    })
11311}
11312
11313fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
11314    match node.kind() {
11315        "class_specifier" | "struct_specifier" | "union_specifier" => {
11316            node.child_by_field_name("name")
11317        }
11318        "function_definition" => {
11319            let declarator = node.child_by_field_name("declarator")?;
11320            if matches!(declarator.kind(), "identifier" | "template_function") {
11321                Some(declarator)
11322            } else {
11323                None
11324            }
11325        }
11326        "alias_declaration" => node.child_by_field_name("name"),
11327        _ => None,
11328    }
11329}
11330
11331fn cpp_template_alias_target<'tree>(
11332    alias: Node<'tree>,
11333    source: &str,
11334    parameter_names: &[String],
11335    ancestry: &ParentIndex<'tree>,
11336) -> Option<CppTemplateAliasTargetMetadata> {
11337    let mut type_node = alias.child_by_field_name("type")?;
11338    while type_node.kind() == "type_descriptor" {
11339        type_node = type_node.child_by_field_name("type")?;
11340    }
11341    let global = type_node.child_by_field_name("scope").is_none()
11342        && type_node.child(0).is_some_and(|child| child.kind() == "::");
11343    let mut components = Vec::new();
11344    cpp_template_target_components(type_node, source, &mut components)?;
11345    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
11346    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
11347        components,
11348        global,
11349        arguments,
11350    })
11351}
11352
11353fn cpp_template_target_components(
11354    node: Node<'_>,
11355    source: &str,
11356    out: &mut Vec<String>,
11357) -> Option<()> {
11358    match node.kind() {
11359        "identifier" | "namespace_identifier" | "type_identifier" => {
11360            out.push(node_text(node, source).to_string());
11361            Some(())
11362        }
11363        "template_type" => {
11364            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11365        }
11366        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11367            if let Some(scope) = node.child_by_field_name("scope") {
11368                cpp_template_target_components(scope, source, out)?;
11369            }
11370            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11371        }
11372        _ => None,
11373    }
11374}
11375
11376fn cpp_template_argument_expressions<'tree>(
11377    mut node: Node<'tree>,
11378    source: &str,
11379    parameter_names: &[String],
11380    ancestry: &ParentIndex<'tree>,
11381) -> Option<Vec<CppTemplateExpression>> {
11382    loop {
11383        match node.kind() {
11384            "template_type" | "template_function" => {
11385                let arguments = node.child_by_field_name("arguments")?;
11386                let mut cursor = arguments.walk();
11387                return Some(
11388                    arguments
11389                        .named_children(&mut cursor)
11390                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
11391                        .map(|argument| {
11392                            cpp_template_expression(argument, source, parameter_names, ancestry)
11393                        })
11394                        .collect(),
11395                );
11396            }
11397            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
11398                node = node
11399                    .child_by_field_name("name")
11400                    .or_else(|| node.child_by_field_name("type"))?;
11401            }
11402            _ => return None,
11403        }
11404    }
11405}
11406
11407fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
11408    let candidate = node
11409        .child_by_field_name("name")
11410        .or_else(|| node.child_by_field_name("declarator"))
11411        .or_else(|| {
11412            let mut cursor = node.walk();
11413            node.named_children(&mut cursor).find(|child| {
11414                matches!(
11415                    child.kind(),
11416                    "identifier" | "type_identifier" | "field_identifier"
11417                )
11418            })
11419        })?;
11420    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
11421    (!name.is_empty()).then_some(name)
11422}
11423
11424fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
11425    match node.kind() {
11426        "type_parameter_declaration"
11427        | "optional_type_parameter_declaration"
11428        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
11429        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
11430        _ => CppTemplateParameterKind::Value,
11431    }
11432}
11433
11434fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
11435    node.child_by_field_name("default_type")
11436        .or_else(|| node.child_by_field_name("default_value"))
11437}
11438
11439fn cpp_template_parameter_default_expression<'tree>(
11440    parameter: Node<'tree>,
11441    source: &str,
11442    parameter_names: &[String],
11443    ancestry: &ParentIndex<'tree>,
11444) -> Option<CppTemplateExpression> {
11445    let default = cpp_template_parameter_default(parameter)?;
11446    let base = cpp_template_expression(default, source, parameter_names, ancestry);
11447    let Some(pointer_error) = parameter.next_named_sibling() else {
11448        return Some(base);
11449    };
11450    let Some(pointer_declarator) =
11451        recovered_abstract_pointer_declarator_term(pointer_error, source)
11452    else {
11453        return Some(base);
11454    };
11455    Some(CppTemplateExpression {
11456        text: format!(
11457            "{}{}",
11458            base.text,
11459            normalize_cpp_whitespace(node_text(pointer_error, source))
11460        ),
11461        term: CppTemplateTerm::Node {
11462            kind: "type_descriptor".to_string(),
11463            children: vec![base.term, pointer_declarator],
11464        },
11465    })
11466}
11467
11468fn recovered_abstract_pointer_declarator_term(
11469    node: Node<'_>,
11470    source: &str,
11471) -> Option<CppTemplateTerm> {
11472    if node.kind() != "ERROR" || node.child_count() == 0 {
11473        return None;
11474    }
11475    let mut children = Vec::new();
11476    for index in 0..node.child_count() {
11477        let child = node.child(index)?;
11478        if child.kind() != "*" {
11479            return None;
11480        }
11481        children.push(CppTemplateTerm::Atom {
11482            kind: "*".to_string(),
11483            text: normalize_cpp_whitespace(node_text(child, source)),
11484        });
11485    }
11486    Some(CppTemplateTerm::Node {
11487        kind: "abstract_pointer_declarator".to_string(),
11488        children,
11489    })
11490}
11491
11492fn cpp_template_expression<'tree>(
11493    node: Node<'tree>,
11494    source: &str,
11495    parameter_names: &[String],
11496    ancestry: &ParentIndex<'tree>,
11497) -> CppTemplateExpression {
11498    let text = normalize_cpp_whitespace(node_text(node, source));
11499    CppTemplateExpression {
11500        text,
11501        term: cpp_template_term(node, source, parameter_names, ancestry),
11502    }
11503}
11504
11505pub fn cpp_template_term<'tree>(
11506    node: Node<'tree>,
11507    source: &str,
11508    parameter_names: &[String],
11509    ancestry: &ParentIndex<'tree>,
11510) -> CppTemplateTerm {
11511    enum Work<'tree> {
11512        Visit(Node<'tree>),
11513        Build { kind: String, child_count: usize },
11514    }
11515
11516    let mut work = vec![Work::Visit(node)];
11517    let mut terms = Vec::new();
11518    while let Some(next) = work.pop() {
11519        match next {
11520            Work::Visit(current) => {
11521                let text = normalize_cpp_whitespace(node_text(current, source));
11522                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
11523                    terms.push(CppTemplateTerm::Parameter(text));
11524                    continue;
11525                }
11526                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
11527                    let mut cursor = current.walk();
11528                    let named = current
11529                        .named_children(&mut cursor)
11530                        .filter(|child| !child.is_extra() && child.kind() != "comment")
11531                        .collect::<Vec<_>>();
11532                    if let [child] = named.as_slice() {
11533                        work.push(Work::Visit(*child));
11534                        continue;
11535                    }
11536                }
11537                if current.child_count() == 0 {
11538                    terms.push(CppTemplateTerm::Atom {
11539                        kind: if matches!(
11540                            current.kind(),
11541                            "identifier"
11542                                | "type_identifier"
11543                                | "field_identifier"
11544                                | "namespace_identifier"
11545                        ) {
11546                            "identifier".to_string()
11547                        } else {
11548                            current.kind().to_string()
11549                        },
11550                        text,
11551                    });
11552                    continue;
11553                }
11554                let children = (0..current.child_count())
11555                    .filter_map(|index| current.child(index))
11556                    .filter(|child| !child.is_extra() && child.kind() != "comment")
11557                    .collect::<Vec<_>>();
11558                work.push(Work::Build {
11559                    kind: current.kind().to_string(),
11560                    child_count: children.len(),
11561                });
11562                work.extend(children.into_iter().rev().map(Work::Visit));
11563            }
11564            Work::Build { kind, child_count } => {
11565                let children = terms.split_off(terms.len() - child_count);
11566                terms.push(CppTemplateTerm::Node { kind, children });
11567            }
11568        }
11569    }
11570    terms.pop().expect("template term traversal emits one root")
11571}
11572
11573fn cpp_template_term_leaf_is_parameter<'tree>(
11574    node: Node<'tree>,
11575    text: &str,
11576    parameter_names: &[String],
11577    ancestry: &ParentIndex<'tree>,
11578) -> bool {
11579    if !parameter_names.iter().any(|parameter| parameter == text) {
11580        return false;
11581    }
11582    !ancestry.parent(node).is_some_and(|parent| {
11583        matches!(
11584            parent.kind(),
11585            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
11586        ) && parent.child_by_field_name("scope").is_some()
11587            && parent.child_by_field_name("name") == Some(node)
11588    })
11589}
11590
11591fn enclosing_cpp_declaration_node<'tree>(
11592    mut node: Node<'tree>,
11593    ancestry: &ParentIndex<'tree>,
11594) -> Option<Node<'tree>> {
11595    loop {
11596        match node.kind() {
11597            "declaration"
11598            | "function_declaration"
11599            | "field_declaration"
11600            | "function_definition" => return Some(node),
11601            _ => node = ancestry.parent(node)?,
11602        }
11603    }
11604}
11605
11606fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
11607    let mut params = Vec::new();
11608    let mut cursor = parameters_node.walk();
11609    for child in parameters_node.children(&mut cursor) {
11610        match child.kind() {
11611            "parameter_declaration" | "optional_parameter_declaration" => {
11612                params.push(cpp_parameter_type(child, source));
11613            }
11614            "variadic_parameter_declaration" => {
11615                params.push(cpp_parameter_type(child, source));
11616            }
11617            "variadic_parameter" | "..." => params.push("...".to_string()),
11618            _ => {}
11619        }
11620    }
11621
11622    if params.is_empty() {
11623        "()".to_string()
11624    } else {
11625        format!("({})", params.join(", "))
11626    }
11627}
11628
11629fn cpp_signature_metadata<'tree>(
11630    signature: String,
11631    function_declarator: Node<'tree>,
11632    source: &str,
11633    ancestry: &ParentIndex<'tree>,
11634) -> SignatureMetadata {
11635    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
11636    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
11637    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
11638    let return_type_identity =
11639        cpp_callable_return_type_identity(function_declarator, source, ancestry);
11640    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
11641        return enrich(
11642            SignatureMetadata::new(signature, Vec::new())
11643                .with_return_type_text(return_type_text)
11644                .with_return_type_identity(return_type_identity),
11645        );
11646    };
11647    let callable_arity = cpp_callable_arity(parameters_node, source);
11648    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
11649    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
11650    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
11651    let Some(relative_start) = signature
11652        .get(search_from..)
11653        .and_then(|suffix| suffix.find(&parameter_text))
11654    else {
11655        return enrich(
11656            SignatureMetadata::new(signature, Vec::new())
11657                .with_callable_arity(callable_arity)
11658                .with_callable_parameter_types(callable_parameter_types)
11659                .with_return_type_text(return_type_text)
11660                .with_return_type_identity(return_type_identity),
11661        );
11662    };
11663    let parameters_start = search_from + relative_start;
11664    let parameters_end = parameters_start + parameter_text.len();
11665    let mut search_start = parameters_start;
11666    let parameters = cpp_parameter_label_nodes(parameters_node)
11667        .into_iter()
11668        .filter_map(|label_node| {
11669            let label = normalize_cpp_whitespace(node_text(label_node, source));
11670            if label.is_empty() || search_start > parameters_end {
11671                return None;
11672            }
11673            let haystack = signature.get(search_start..parameters_end)?;
11674            let relative_start = haystack.find(&label)?;
11675            let start_byte = search_start + relative_start;
11676            let end_byte = start_byte + label.len();
11677            search_start = end_byte;
11678            Some(ParameterMetadata::new(label, start_byte, end_byte))
11679        })
11680        .collect();
11681    enrich(
11682        SignatureMetadata::new(signature, parameters)
11683            .with_callable_arity(callable_arity)
11684            .with_callable_parameter_types(callable_parameter_types)
11685            .with_return_type_text(return_type_text)
11686            .with_return_type_identity(return_type_identity),
11687    )
11688}
11689
11690fn cpp_callable_is_structural_constructor<'tree>(
11691    function_declarator: Node<'tree>,
11692    source: &str,
11693    ancestry: &ParentIndex<'tree>,
11694) -> bool {
11695    let Some(name_node) = function_declarator
11696        .child_by_field_name("declarator")
11697        .or_else(|| function_declarator.child_by_field_name("name"))
11698        .or_else(|| last_named_child(function_declarator))
11699    else {
11700        return false;
11701    };
11702    let Some(callable_name) = direct_identifier_name(name_node, source) else {
11703        return false;
11704    };
11705
11706    let mut current = ancestry.parent(function_declarator);
11707    while let Some(ancestor) = current {
11708        let owner_name = match ancestor.kind() {
11709            "class_specifier" | "struct_specifier" | "union_specifier" => {
11710                class_like_name(ancestor, source, ancestry)
11711            }
11712            "ERROR" => malformed_class_error_owner_name(ancestor, source),
11713            _ => None,
11714        };
11715        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
11716            return true;
11717        }
11718        current = ancestry.parent(ancestor);
11719    }
11720    false
11721}
11722
11723/// Recover the owner name from the direct grammar shape retained when a later
11724/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
11725/// `ERROR` node:
11726///
11727/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
11728///
11729/// Direct-child checks keep this distinct from an unrelated nested class inside
11730/// a broader error region. The closing brace may be displaced past the error
11731/// node, so the opening body token is the available structural boundary.
11732fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
11733    if node.kind() != "ERROR" {
11734        return None;
11735    }
11736    let keyword = node.child(0)?;
11737    if !matches!(keyword.kind(), "class" | "struct" | "union") {
11738        return None;
11739    }
11740    let name_node = node.child(1)?;
11741    let name = direct_identifier_name(name_node, source)?;
11742    let has_body = (2..node.child_count())
11743        .filter_map(|index| node.child(index))
11744        .any(|child| child.kind() == "{");
11745    has_body.then_some(name)
11746}
11747
11748/// The parser-derived return type of one callable declaration.
11749///
11750/// This accepts the enclosing declaration node so consumers do not need to
11751/// duplicate the declarator-unwrapping rules before asking for the structured
11752/// identity.
11753pub fn cpp_callable_declaration_return_type_identity<'tree>(
11754    callable: Node<'tree>,
11755    source: &str,
11756    ancestry: &ParentIndex<'tree>,
11757) -> Option<StructuredTypeIdentity> {
11758    let declarator = callable
11759        .child_by_field_name("declarator")
11760        .and_then(extract_function_declarator)?;
11761    cpp_callable_return_type_identity(declarator, source, ancestry)
11762}
11763
11764pub(crate) fn cpp_callable_return_type_identity<'tree>(
11765    function_declarator: Node<'tree>,
11766    source: &str,
11767    ancestry: &ParentIndex<'tree>,
11768) -> Option<StructuredTypeIdentity> {
11769    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
11770        return None;
11771    }
11772    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
11773    if let Some((return_type, _)) =
11774        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
11775    {
11776        return cpp_structured_type_identity(return_type, source, &lexical_scope);
11777    }
11778    let mut cursor = function_declarator.walk();
11779    if let Some(trailing) = function_declarator
11780        .named_children(&mut cursor)
11781        .find(|child| child.kind() == "trailing_return_type")
11782        && let Some(type_descriptor) = trailing.named_child(0)
11783    {
11784        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
11785    }
11786
11787    let mut current = function_declarator;
11788    let mut wrappers = Vec::new();
11789    while let Some(parent) = ancestry.parent(current) {
11790        if matches!(
11791            parent.kind(),
11792            "function_definition" | "declaration" | "field_declaration"
11793        ) {
11794            let type_node = parent.child_by_field_name("type")?;
11795            if cpp_export_macro_token(node_text(type_node, source))
11796                && (0..parent.named_child_count()).any(|index| {
11797                    parent
11798                        .named_child(index)
11799                        .is_some_and(|child| child.kind() == "ERROR")
11800                })
11801            {
11802                return None;
11803            }
11804            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
11805            for wrapper in wrappers.into_iter().rev() {
11806                identity = cpp_wrap_structured_type(identity, wrapper)?;
11807            }
11808            return Some(identity);
11809        }
11810        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
11811            || (matches!(
11812                parent.kind(),
11813                "pointer_declarator"
11814                    | "reference_declarator"
11815                    | "array_declarator"
11816                    | "parenthesized_declarator"
11817            ) && parent.named_child_count() == 1
11818                && parent.named_child(0) == Some(current));
11819        if !wraps_current_declarator {
11820            return None;
11821        }
11822        match parent.kind() {
11823            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
11824            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
11825            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
11826            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
11827            _ => return None,
11828        }
11829        current = parent;
11830    }
11831    None
11832}
11833
11834fn cpp_structured_type_identity(
11835    node: Node<'_>,
11836    source: &str,
11837    lexical_scope: &[String],
11838) -> Option<StructuredTypeIdentity> {
11839    enum Work<'tree> {
11840        Visit(Node<'tree>),
11841        Wrap(CppStructuredTypeWrapper),
11842        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
11843        BuildGeneric { argument_count: usize },
11844    }
11845
11846    let mut work = vec![Work::Visit(node)];
11847    let mut values = Vec::new();
11848    let mut builder = StructuredTypeIdentityBuilder::default();
11849    while let Some(next) = work.pop() {
11850        match next {
11851            Work::Visit(current) => match current.kind() {
11852                "type_descriptor" => {
11853                    let type_node = current
11854                        .child_by_field_name("type")
11855                        .or_else(|| current.named_child(0))?;
11856                    let mut wrappers = Vec::new();
11857                    let mut cursor = current.walk();
11858                    for child in current.named_children(&mut cursor) {
11859                        if child.id() != type_node.id() {
11860                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
11861                        }
11862                    }
11863                    work.push(Work::ApplyWrappers(wrappers));
11864                    work.push(Work::Visit(type_node));
11865                }
11866                "pointer_declarator" | "abstract_pointer_declarator" => {
11867                    let child = current
11868                        .child_by_field_name("declarator")
11869                        .or_else(|| current.named_child(0))?;
11870                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
11871                    work.push(Work::Visit(child));
11872                }
11873                "reference_declarator" => {
11874                    let child = current
11875                        .child_by_field_name("declarator")
11876                        .or_else(|| current.named_child(0))?;
11877                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
11878                    work.push(Work::Visit(child));
11879                }
11880                "array_declarator" | "abstract_array_declarator" => {
11881                    let child = current
11882                        .child_by_field_name("declarator")
11883                        .or_else(|| current.named_child(0))?;
11884                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
11885                    work.push(Work::Visit(child));
11886                }
11887                "template_type" => {
11888                    let name_node = current.child_by_field_name("name")?;
11889                    let arguments = current
11890                        .child_by_field_name("arguments")
11891                        .map(|arguments_node| {
11892                            let mut cursor = arguments_node.walk();
11893                            arguments_node
11894                                .named_children(&mut cursor)
11895                                .filter(|child| !child.is_extra() && child.kind() != "comment")
11896                                .collect::<Vec<_>>()
11897                        })
11898                        .unwrap_or_default();
11899                    work.push(Work::BuildGeneric {
11900                        argument_count: arguments.len(),
11901                    });
11902                    work.extend(arguments.into_iter().rev().map(Work::Visit));
11903                    work.push(Work::Visit(name_node));
11904                }
11905                "qualified_identifier"
11906                | "scoped_identifier"
11907                | "scoped_type_identifier"
11908                | "type_identifier"
11909                | "field_identifier"
11910                | "identifier"
11911                | "namespace_identifier"
11912                | "primitive_type" => {
11913                    values.push(builder.named(cpp_structured_named_type(
11914                        current,
11915                        source,
11916                        lexical_scope,
11917                    )?)?);
11918                }
11919                _ => {
11920                    let child = current.child_by_field_name("type").or_else(|| {
11921                        (current.named_child_count() == 1)
11922                            .then(|| current.named_child(0))
11923                            .flatten()
11924                    })?;
11925                    work.push(Work::Visit(child));
11926                }
11927            },
11928            Work::Wrap(wrapper) => {
11929                let root = values.pop()?;
11930                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
11931            }
11932            Work::ApplyWrappers(wrappers) => {
11933                let mut root = values.pop()?;
11934                for wrapper in wrappers.into_iter().rev() {
11935                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
11936                }
11937                values.push(root);
11938            }
11939            Work::BuildGeneric { argument_count } => {
11940                let value_count = argument_count.checked_add(1)?;
11941                let start = values.len().checked_sub(value_count)?;
11942                let mut built = values.split_off(start);
11943                let base = built.remove(0);
11944                values.push(builder.generic(base, built)?);
11945            }
11946        }
11947    }
11948    (values.len() == 1)
11949        .then(|| values.pop())
11950        .flatten()
11951        .and_then(|root| builder.finish(root))
11952}
11953
11954fn cpp_structured_named_type(
11955    node: Node<'_>,
11956    source: &str,
11957    lexical_scope: &[String],
11958) -> Option<StructuredTypeName> {
11959    let path = cpp_structured_type_path(node, source)?;
11960    let absolute = node.child_by_field_name("scope").is_none()
11961        && node.child(0).is_some_and(|child| child.kind() == "::");
11962    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
11963}
11964
11965#[derive(Clone, Copy)]
11966enum CppStructuredTypeWrapper {
11967    Pointer,
11968    LvalueReference,
11969    RvalueReference,
11970    Array,
11971}
11972
11973fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
11974    let mut wrappers = Vec::new();
11975    let mut current = node;
11976    loop {
11977        match current.kind() {
11978            "pointer_declarator" | "abstract_pointer_declarator" => {
11979                wrappers.push(CppStructuredTypeWrapper::Pointer)
11980            }
11981            "reference_declarator" | "abstract_reference_declarator" => {
11982                wrappers.push(cpp_reference_wrapper(current)?);
11983            }
11984            "array_declarator" | "abstract_array_declarator" => {
11985                wrappers.push(CppStructuredTypeWrapper::Array)
11986            }
11987            // An initializer and a grouping parenthesis are not type
11988            // structure. Stopping at one of them dropped every wrapper an
11989            // initialized or parenthesized declarator writes, so
11990            // `std::string &alias = value;` reported the unqualified
11991            // `std::string` while `const std::string &value` as a parameter
11992            // reported the reference it shares.
11993            "init_declarator" | "parenthesized_declarator" => {}
11994            _ => break,
11995        }
11996        let Some(child) = current
11997            .child_by_field_name("declarator")
11998            .or_else(|| current.named_child(0))
11999        else {
12000            break;
12001        };
12002        current = child;
12003    }
12004    Some(wrappers)
12005}
12006
12007fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
12008    node.children(&mut node.walk())
12009        .find_map(|child| match child.kind() {
12010            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
12011            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
12012            _ => None,
12013        })
12014}
12015
12016fn cpp_wrap_structured_type(
12017    identity: StructuredTypeIdentity,
12018    wrapper: CppStructuredTypeWrapper,
12019) -> Option<StructuredTypeIdentity> {
12020    match wrapper {
12021        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
12022        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
12023        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
12024        CppStructuredTypeWrapper::Array => identity.wrap_array(),
12025    }
12026}
12027
12028fn cpp_wrap_structured_type_node(
12029    builder: &mut StructuredTypeIdentityBuilder,
12030    inner: StructuredTypeNodeId,
12031    wrapper: CppStructuredTypeWrapper,
12032) -> Option<StructuredTypeNodeId> {
12033    match wrapper {
12034        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
12035        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
12036        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
12037        CppStructuredTypeWrapper::Array => builder.array(inner),
12038    }
12039}
12040
12041/// The dot-free name components a qualified C++ name node writes, outermost
12042/// first.
12043///
12044/// The walk reads the grammar's `scope` and `name` fields rather than cutting
12045/// a rendered spelling on `::`, so `std::move` yields `["std", "move"]` and a
12046/// shape the grammar does not name yields nothing at all.
12047pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
12048    let mut path = Vec::new();
12049    let mut stack = vec![node];
12050    while let Some(current) = stack.pop() {
12051        match current.kind() {
12052            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
12053                let component = node_text(current, source).to_string();
12054                if component.is_empty() {
12055                    return None;
12056                }
12057                path.push(component);
12058            }
12059            "template_type" | "dependent_type" => {
12060                stack.push(current.child_by_field_name("name")?);
12061            }
12062            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12063                stack.push(current.child_by_field_name("name")?);
12064                if let Some(scope) = current.child_by_field_name("scope") {
12065                    stack.push(scope);
12066                }
12067            }
12068            _ => return None,
12069        }
12070    }
12071    (!path.is_empty()).then_some(path)
12072}
12073
12074fn cpp_callable_lexical_scope<'tree>(
12075    node: Node<'tree>,
12076    source: &str,
12077    ancestry: &ParentIndex<'tree>,
12078) -> Vec<String> {
12079    let mut groups = Vec::new();
12080    let mut current = ancestry.parent(node);
12081    while let Some(parent) = current {
12082        if matches!(
12083            parent.kind(),
12084            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
12085        ) && let Some(name_node) = parent.child_by_field_name("name")
12086            && let Some(components) = cpp_structured_type_path(name_node, source)
12087            && !components.is_empty()
12088        {
12089            groups.push(components);
12090        }
12091        current = ancestry.parent(parent);
12092    }
12093    groups.reverse();
12094    groups.into_iter().flatten().collect()
12095}
12096
12097fn cpp_callable_dispatch_extensibility<'tree>(
12098    function_declarator: Node<'tree>,
12099    ancestry: &ParentIndex<'tree>,
12100) -> DispatchExtensibility {
12101    let mut declaration = None;
12102    let mut current = Some(function_declarator);
12103    while let Some(node) = current {
12104        match node.kind() {
12105            "template_declaration"
12106            | "preproc_if"
12107            | "preproc_ifdef"
12108            | "preproc_else"
12109            | "preproc_elif"
12110            | "preproc_call"
12111            | "ERROR" => return DispatchExtensibility::Open,
12112            "declaration" | "field_declaration" | "function_definition" => {
12113                declaration.get_or_insert(node);
12114            }
12115            "translation_unit" => break,
12116            _ => {}
12117        }
12118        current = ancestry.parent(node);
12119    }
12120    let Some(declaration) = declaration else {
12121        return DispatchExtensibility::Open;
12122    };
12123
12124    let mut saw_virtual_boundary = false;
12125    let mut stack = vec![declaration];
12126    while let Some(node) = stack.pop() {
12127        match node.kind() {
12128            "compound_statement" | "field_declaration_list" => continue,
12129            "final" | "final_specifier" => return DispatchExtensibility::Closed,
12130            "virtual"
12131            | "override"
12132            | "virtual_specifier"
12133            | "pure_virtual_clause"
12134            | "template_parameter_list"
12135            | "template_method"
12136            | "template_function"
12137            | "ERROR" => saw_virtual_boundary = true,
12138            _ => {}
12139        }
12140        let mut cursor = node.walk();
12141        stack.extend(node.children(&mut cursor));
12142    }
12143
12144    if saw_virtual_boundary {
12145        DispatchExtensibility::Open
12146    } else {
12147        DispatchExtensibility::Closed
12148    }
12149}
12150
12151fn cpp_callable_linkage<'tree>(
12152    declaration: Node<'tree>,
12153    source: &str,
12154    ancestry: &ParentIndex<'tree>,
12155) -> CallableLinkage {
12156    let mut enclosed_by_class = false;
12157    let mut current = ancestry.parent(declaration);
12158    while let Some(node) = current {
12159        if node.kind() == "namespace_definition"
12160            && node
12161                .child_by_field_name("name")
12162                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12163        {
12164            return CallableLinkage::Internal;
12165        }
12166        if matches!(
12167            node.kind(),
12168            "class_specifier" | "struct_specifier" | "union_specifier"
12169        ) {
12170            if node
12171                .child_by_field_name("name")
12172                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12173            {
12174                return CallableLinkage::Internal;
12175            }
12176            enclosed_by_class = true;
12177        }
12178        // An export macro between `class` and the class name can make
12179        // tree-sitter parse the entire class as a function definition. The
12180        // structured recovery proves that this container is a named class
12181        // scope, not a local callable scope.
12182        if node.kind() == "lambda_expression"
12183            || node.kind() == "function_definition"
12184                && !is_recovered_exported_class_container(node, source)
12185        {
12186            return CallableLinkage::Internal;
12187        }
12188        current = ancestry.parent(node);
12189    }
12190
12191    if enclosed_by_class {
12192        return CallableLinkage::External;
12193    }
12194
12195    let mut cursor = declaration.walk();
12196    if declaration.named_children(&mut cursor).any(|child| {
12197        child.kind() == "storage_class_specifier"
12198            && normalize_cpp_whitespace(node_text(child, source)) == "static"
12199    }) {
12200        CallableLinkage::Internal
12201    } else {
12202        CallableLinkage::External
12203    }
12204}
12205
12206fn cpp_callable_return_type_text<'tree>(
12207    function_declarator: Node<'tree>,
12208    source: &str,
12209    ancestry: &ParentIndex<'tree>,
12210) -> Option<String> {
12211    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
12212        return None;
12213    }
12214    if let Some((return_type, _)) =
12215        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
12216    {
12217        let text = normalize_cpp_whitespace(node_text(return_type, source));
12218        return (!text.is_empty()).then_some(text);
12219    }
12220    let mut cursor = function_declarator.walk();
12221    if let Some(trailing) = function_declarator
12222        .named_children(&mut cursor)
12223        .find(|child| child.kind() == "trailing_return_type")
12224        && let Some(type_descriptor) = trailing.named_child(0)
12225    {
12226        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
12227        if !text.is_empty() {
12228            return Some(text);
12229        }
12230    }
12231
12232    let mut current = function_declarator;
12233    let mut indirection = String::new();
12234    while let Some(parent) = ancestry.parent(current) {
12235        if matches!(
12236            parent.kind(),
12237            "function_definition" | "declaration" | "field_declaration"
12238        ) {
12239            let type_node = parent.child_by_field_name("type")?;
12240            if cpp_export_macro_token(node_text(type_node, source))
12241                && (0..parent.named_child_count()).any(|index| {
12242                    parent
12243                        .named_child(index)
12244                        .is_some_and(|child| child.kind() == "ERROR")
12245                })
12246            {
12247                // Export/decorator macros commonly occupy the grammar's `type`
12248                // field and leave the semantic return type in an ERROR sibling.
12249                // Do not persist the macro token as a return type. The malformed
12250                // declaration does not carry enough structured evidence here.
12251                return None;
12252            }
12253            let base = normalize_cpp_whitespace(node_text(type_node, source));
12254            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
12255        }
12256        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
12257            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
12258                && parent.named_child_count() == 1
12259                && parent.named_child(0) == Some(current));
12260        if wraps_current_declarator {
12261            match parent.kind() {
12262                "pointer_declarator" => indirection.push('*'),
12263                "reference_declarator" => {
12264                    let reference = parent
12265                        .children(&mut parent.walk())
12266                        .find(|child| !child.is_named())
12267                        .map(|child| node_text(child, source))
12268                        .unwrap_or("&");
12269                    indirection.push_str(reference);
12270                }
12271                "init_declarator" | "parenthesized_declarator" => {}
12272                _ => return None,
12273            }
12274            current = parent;
12275            continue;
12276        }
12277        return None;
12278    }
12279    None
12280}
12281
12282fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
12283    let mut required = 0;
12284    let mut total = 0;
12285    let mut repeated = false;
12286    let mut cursor = parameters_node.walk();
12287    for child in parameters_node.children(&mut cursor) {
12288        match child.kind() {
12289            "parameter_declaration" => {
12290                if cpp_parameter_is_explicit_object(child, source) {
12291                    continue;
12292                }
12293                if child.child_by_field_name("declarator").is_none()
12294                    && child
12295                        .child_by_field_name("type")
12296                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
12297                {
12298                    continue;
12299                }
12300                required += 1;
12301                total += 1;
12302            }
12303            "optional_parameter_declaration" => total += 1,
12304            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12305                repeated = true;
12306            }
12307            _ => {}
12308        }
12309    }
12310    CallableArity::new(required, total, repeated)
12311}
12312
12313fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
12314    parameter
12315        .child_by_field_name("type")
12316        .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
12317        .and_then(|type_node| type_node.child_by_field_name("constraint"))
12318        .is_some_and(|constraint| {
12319            constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
12320        })
12321}
12322
12323/// One entry of a callable's invocation parameter list.
12324///
12325/// The list excludes an explicit object parameter and a lone `void`, so its
12326/// length is the callable's invocation arity. Every derivation of a parameter
12327/// type - the rendered spelling used for overload discrimination and the
12328/// structured identity used by dependency-pack production - starts from this
12329/// same sequence, so the two can never disagree about which parameters exist.
12330#[derive(Clone, Copy)]
12331enum CppParameterSlot<'tree> {
12332    Declared(Node<'tree>),
12333    Ellipsis,
12334}
12335
12336fn cpp_callable_parameter_slots<'tree>(
12337    parameters_node: Node<'tree>,
12338    source: &str,
12339) -> Vec<CppParameterSlot<'tree>> {
12340    let mut slots = Vec::new();
12341    let mut cursor = parameters_node.walk();
12342    for parameter in parameters_node.children(&mut cursor) {
12343        match parameter.kind() {
12344            "parameter_declaration" | "optional_parameter_declaration" => {
12345                if cpp_parameter_is_explicit_object(parameter, source)
12346                    || (parameter.child_by_field_name("declarator").is_none()
12347                        && parameter
12348                            .child_by_field_name("type")
12349                            .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
12350                {
12351                    continue;
12352                }
12353                slots.push(CppParameterSlot::Declared(parameter));
12354            }
12355            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12356                slots.push(CppParameterSlot::Ellipsis);
12357            }
12358            _ => {}
12359        }
12360    }
12361    slots
12362}
12363
12364fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
12365    cpp_callable_parameter_slots(parameters_node, source)
12366        .into_iter()
12367        .map(|slot| match slot {
12368            CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
12369            CppParameterSlot::Ellipsis => "...".to_string(),
12370        })
12371        .collect()
12372}
12373
12374/// One callable parameter's parser-derived type.
12375///
12376/// A rendered spelling such as `const T&` is a source text, not a type name. A
12377/// consumer that must publish a type into a structured model - a semantic-pack
12378/// type reference, for example - reads this instead.
12379#[derive(Debug, Clone, PartialEq, Eq)]
12380pub enum CppParameterType {
12381    /// The written type reduced to a structured identity. C++ cv-qualifiers
12382    /// have no place in that model and are not represented.
12383    Structured(StructuredTypeIdentity),
12384    /// A `...` pack, which declares no parameter type at all.
12385    Ellipsis,
12386    /// A written type with no structured reduction, such as a macro-obscured,
12387    /// `decltype`-computed, or function-pointer parameter.
12388    Unstructured,
12389}
12390
12391/// The structured type of each invocation parameter, in declaration order.
12392///
12393/// The result is index-parallel with the rendered
12394/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
12395/// callable.
12396pub fn cpp_callable_parameter_type_identities<'tree>(
12397    function_declarator: Node<'tree>,
12398    source: &str,
12399    ancestry: &ParentIndex<'tree>,
12400) -> Vec<CppParameterType> {
12401    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12402        return Vec::new();
12403    };
12404    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12405    cpp_callable_parameter_slots(parameters_node, source)
12406        .into_iter()
12407        .map(|slot| match slot {
12408            CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
12409            CppParameterSlot::Declared(parameter) => {
12410                cpp_parameter_type_identity(parameter, source, &lexical_scope)
12411                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
12412            }
12413        })
12414        .collect()
12415}
12416
12417/// The parser-derived type of one declared object or parameter.
12418///
12419/// The declaration owns the base `type` field while the individual declarator
12420/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
12421/// lets callers distinguish several declarators in one declaration without
12422/// reparsing a rendered signature.
12423pub fn cpp_declaration_type_identity<'tree>(
12424    declaration: Node<'tree>,
12425    declarator: Node<'tree>,
12426    source: &str,
12427    ancestry: &ParentIndex<'tree>,
12428) -> Option<StructuredTypeIdentity> {
12429    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
12430    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
12431}
12432
12433fn cpp_parameter_type_identity(
12434    parameter: Node<'_>,
12435    source: &str,
12436    lexical_scope: &[String],
12437) -> Option<StructuredTypeIdentity> {
12438    cpp_declaration_type_identity_in_scope(
12439        parameter,
12440        cpp_parameter_declarator(parameter),
12441        source,
12442        lexical_scope,
12443    )
12444}
12445
12446fn cpp_declaration_type_identity_in_scope(
12447    declaration: Node<'_>,
12448    declarator: Option<Node<'_>>,
12449    source: &str,
12450    lexical_scope: &[String],
12451) -> Option<StructuredTypeIdentity> {
12452    let type_node = declaration.child_by_field_name("type")?;
12453    let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
12454    if let Some(declarator) = declarator {
12455        for wrapper in cpp_structured_declarator_wrappers(declarator)?
12456            .into_iter()
12457            .rev()
12458        {
12459            identity = cpp_wrap_structured_type(identity, wrapper)?;
12460        }
12461    }
12462    Some(identity)
12463}
12464
12465/// One callable parameter's comparable shape.
12466///
12467/// [`CppParameterType`] above answers "which type is written here" for a
12468/// structured model and deliberately records no cv-qualifiers, so it reports
12469/// the same value for `f(char *)` and `f(const char *)`. Deciding whether two
12470/// callable declarations declare one function needs the opposite trade: every
12471/// cv-qualifier that C++ counts as part of the parameter type must survive,
12472/// while the two declarations may spell the same type through different
12473/// qualifications. This slot carries that comparand.
12474///
12475/// The result is index-parallel with [`cpp_callable_parameter_type_identities`]
12476/// and with the rendered parameter spellings of the same callable.
12477#[derive(Debug, Clone, PartialEq, Eq)]
12478pub enum CppComparableSlot {
12479    /// A declared parameter reduced to its comparable shape.
12480    Shape(CppComparableParameter),
12481    /// A `...` pack, which declares no parameter type at all.
12482    Ellipsis,
12483    /// A parameter with no comparable reduction, such as a macro-obscured,
12484    /// `decltype`-computed, or function-pointer parameter.
12485    Unstructured,
12486}
12487
12488/// A parameter type as a flat arena of nodes plus a root index.
12489///
12490/// The arena carries the same rationale as [`StructuredTypeIdentity`]: source
12491/// can nest types very deeply, and cloning, comparing or dropping the value
12492/// must not consume the Rust call stack. Nodes are appended in post-order, so
12493/// every child index is smaller than its parent's and the last appended node is
12494/// the root.
12495///
12496/// That post-order append is also what makes the derived `PartialEq` a correct
12497/// structural equality: the builder below is deterministic, so one type shape
12498/// has exactly one arena layout no matter which spelling produced it. Two
12499/// shapes are equal as values iff they are equal as type trees.
12500#[derive(Debug, Clone, PartialEq, Eq)]
12501pub struct CppComparableParameter {
12502    nodes: Vec<CppComparableNode>,
12503    root: usize,
12504}
12505
12506/// One node of a [`CppComparableParameter`] arena.
12507///
12508/// `Reference` and `Array` carry no qualifiers because the grammar writes none
12509/// on them: a reference cannot be cv-qualified in C++, and an array's
12510/// qualifiers belong to its element type. A cv-qualifier written on a generic
12511/// type (`const std::vector<int>`) is recorded on the generic's base leaf,
12512/// which is the only Named node the whole spelling produces.
12513#[derive(Debug, Clone, PartialEq, Eq)]
12514pub enum CppComparableNode {
12515    Named {
12516        name: StructuredTypeName,
12517        primitive: bool,
12518        konst: bool,
12519        volatil: bool,
12520    },
12521    Pointer {
12522        inner: usize,
12523        konst: bool,
12524        volatil: bool,
12525    },
12526    Reference {
12527        inner: usize,
12528    },
12529    Array {
12530        inner: usize,
12531    },
12532    Generic {
12533        base: usize,
12534        arguments: Vec<usize>,
12535    },
12536}
12537
12538impl CppComparableParameter {
12539    pub fn root(&self) -> usize {
12540        self.root
12541    }
12542
12543    pub fn node(&self, index: usize) -> &CppComparableNode {
12544        &self.nodes[index]
12545    }
12546
12547    /// Apply the [dcl.fct]/5 parameter-type adjustments, which hold at the
12548    /// parameter's top level only.
12549    ///
12550    /// A top-level cv-qualifier is discarded, so `f(const int)` and `f(int)`
12551    /// declare one function, and a top-level array type becomes a pointer to
12552    /// its element type, so `f(int[3])` and `f(int *)` do too. The outermost
12553    /// type constructor is this arena's root, which is why both adjustments
12554    /// are one match on it: cv on an inner pointer level, on a pointee, or on
12555    /// an array element keeps distinguishing the type, and an array behind a
12556    /// pointer or reference is not a top-level array.
12557    fn adjust_parameter_top_level(&mut self) {
12558        let root = self.root;
12559        match &mut self.nodes[root] {
12560            CppComparableNode::Named { konst, volatil, .. }
12561            | CppComparableNode::Pointer { konst, volatil, .. } => {
12562                *konst = false;
12563                *volatil = false;
12564            }
12565            CppComparableNode::Array { inner } => {
12566                let inner = *inner;
12567                self.nodes[root] = CppComparableNode::Pointer {
12568                    inner,
12569                    konst: false,
12570                    volatil: false,
12571                };
12572            }
12573            CppComparableNode::Generic { base, .. } => {
12574                let base = *base;
12575                let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
12576                    unreachable!("a comparable generic's base is always a named leaf");
12577                };
12578                *konst = false;
12579                *volatil = false;
12580            }
12581            CppComparableNode::Reference { .. } => {}
12582        }
12583    }
12584}
12585
12586/// The comparable shape of each invocation parameter, in declaration order.
12587///
12588/// The result is index-parallel with
12589/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
12590/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
12591/// must treat as evidence of nothing rather than as agreement.
12592pub fn cpp_comparable_parameter_shapes<'tree>(
12593    function_declarator: Node<'tree>,
12594    source: &str,
12595    ancestry: &ParentIndex<'tree>,
12596) -> Vec<CppComparableSlot> {
12597    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12598        return Vec::new();
12599    };
12600    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12601    cpp_callable_parameter_slots(parameters_node, source)
12602        .into_iter()
12603        .map(|slot| match slot {
12604            CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
12605            CppParameterSlot::Declared(parameter) => {
12606                cpp_comparable_parameter(parameter, source, &lexical_scope)
12607                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
12608            }
12609        })
12610        .collect()
12611}
12612
12613fn cpp_comparable_parameter(
12614    parameter: Node<'_>,
12615    source: &str,
12616    lexical_scope: &[String],
12617) -> Option<CppComparableParameter> {
12618    let type_node = parameter.child_by_field_name("type")?;
12619    let levels = match cpp_parameter_declarator(parameter) {
12620        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12621        None => Vec::new(),
12622    };
12623    let mut shape = cpp_comparable_type_shape(
12624        type_node,
12625        cpp_cv_qualifiers(parameter, source),
12626        levels,
12627        source,
12628        lexical_scope,
12629    )?;
12630    shape.adjust_parameter_top_level();
12631    Some(shape)
12632}
12633
12634/// The `const` and `volatile` qualifiers written as direct named children of
12635/// `node`.
12636///
12637/// The grammar exposes `type_qualifier` as a non-field named child in exactly
12638/// the three places a parameter's qualifiers can be written: on the
12639/// `parameter_declaration` itself (the base type), on a `type_descriptor`
12640/// (inside a template argument list), and on each `pointer_declarator` level
12641/// (the pointer object). Every other qualifier the grammar admits - `restrict`
12642/// and friends - takes no part in C++ type identity, the same filter
12643/// `cpp_parameter_type` applies to the rendered spelling (#1827).
12644fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
12645    let mut qualifiers = CppCvQualifiers::default();
12646    let mut cursor = node.walk();
12647    for child in node.named_children(&mut cursor) {
12648        if child.kind() != "type_qualifier" {
12649            continue;
12650        }
12651        match node_text(child, source) {
12652            "const" => qualifiers.konst = true,
12653            "volatile" => qualifiers.volatil = true,
12654            _ => {}
12655        }
12656    }
12657    qualifiers
12658}
12659
12660#[derive(Clone, Copy, Default)]
12661struct CppCvQualifiers {
12662    konst: bool,
12663    volatil: bool,
12664}
12665
12666impl CppCvQualifiers {
12667    fn union(self, other: Self) -> Self {
12668        Self {
12669            konst: self.konst || other.konst,
12670            volatil: self.volatil || other.volatil,
12671        }
12672    }
12673}
12674
12675/// One pointer, reference or array level a declarator chain adds.
12676#[derive(Clone, Copy)]
12677enum CppComparableLevel {
12678    Pointer { konst: bool, volatil: bool },
12679    Reference,
12680    Array,
12681}
12682
12683/// The levels `declarator` adds, outermost written level first.
12684///
12685/// C++ declarator syntax binds inside out: the level written closest to the
12686/// declared name is the outermost type constructor, and tree-sitter nests it
12687/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
12688/// applies in order to reach "array of pointer to int", and the qualifier of
12689/// `int * const *p` is read on the level it was written next to, the inner
12690/// pointer of the resulting type.
12691///
12692/// A declarator chain that names a function type - a function-pointer
12693/// parameter - has no comparable shape and reports `None`, matching the
12694/// structured identity channel.
12695fn cpp_comparable_declarator_levels(
12696    declarator: Node<'_>,
12697    source: &str,
12698) -> Option<Vec<CppComparableLevel>> {
12699    let mut levels = Vec::new();
12700    let mut current = declarator;
12701    loop {
12702        match current.kind() {
12703            "pointer_declarator" | "abstract_pointer_declarator" => {
12704                let qualifiers = cpp_cv_qualifiers(current, source);
12705                levels.push(CppComparableLevel::Pointer {
12706                    konst: qualifiers.konst,
12707                    volatil: qualifiers.volatil,
12708                });
12709            }
12710            "reference_declarator" | "abstract_reference_declarator" => {
12711                levels.push(CppComparableLevel::Reference);
12712            }
12713            "array_declarator" | "abstract_array_declarator" => {
12714                levels.push(CppComparableLevel::Array);
12715            }
12716            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
12717            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
12718            _ => return None,
12719        }
12720        let Some(next) = cpp_nested_declarator(current) else {
12721            return Some(levels);
12722        };
12723        current = next;
12724    }
12725}
12726
12727/// Reduce one written type to a comparable arena.
12728///
12729/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
12730/// two additions: each visited type node carries the cv-qualifiers written on
12731/// it, and declarator levels arrive as a prepared list rather than being
12732/// rediscovered inside the walk.
12733fn cpp_comparable_type_shape(
12734    type_node: Node<'_>,
12735    qualifiers: CppCvQualifiers,
12736    levels: Vec<CppComparableLevel>,
12737    source: &str,
12738    lexical_scope: &[String],
12739) -> Option<CppComparableParameter> {
12740    enum Work<'tree> {
12741        Visit {
12742            node: Node<'tree>,
12743            qualifiers: CppCvQualifiers,
12744        },
12745        ApplyLevels(Vec<CppComparableLevel>),
12746        BuildGeneric {
12747            argument_count: usize,
12748        },
12749    }
12750
12751    let mut nodes: Vec<CppComparableNode> = Vec::new();
12752    let mut values: Vec<usize> = Vec::new();
12753    let mut work = vec![
12754        Work::ApplyLevels(levels),
12755        Work::Visit {
12756            node: type_node,
12757            qualifiers,
12758        },
12759    ];
12760    while let Some(next) = work.pop() {
12761        match next {
12762            Work::Visit { node, qualifiers } => match node.kind() {
12763                "type_descriptor" => {
12764                    let inner_type = node
12765                        .child_by_field_name("type")
12766                        .or_else(|| node.named_child(0))?;
12767                    let mut cursor = node.walk();
12768                    let declarator = node.child_by_field_name("declarator").or_else(|| {
12769                        node.named_children(&mut cursor).find(|child| {
12770                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
12771                        })
12772                    });
12773                    let levels = match declarator {
12774                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12775                        None => Vec::new(),
12776                    };
12777                    work.push(Work::ApplyLevels(levels));
12778                    work.push(Work::Visit {
12779                        node: inner_type,
12780                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
12781                    });
12782                }
12783                "sized_type_specifier" => {
12784                    // `unsigned char` is one primitive type whose components are
12785                    // partly unnamed tokens, so the whole specifier is its own
12786                    // name component. Reducing it to the `type` child would make
12787                    // `f(unsigned char)` and `f(char)` compare equal.
12788                    let name = StructuredTypeName::new(
12789                        vec![normalize_cpp_whitespace(node_text(node, source))],
12790                        lexical_scope.to_vec(),
12791                        false,
12792                    )?;
12793                    values.push(cpp_push_comparable_node(
12794                        &mut nodes,
12795                        CppComparableNode::Named {
12796                            name,
12797                            primitive: true,
12798                            konst: qualifiers.konst,
12799                            volatil: qualifiers.volatil,
12800                        },
12801                    ));
12802                }
12803                "qualified_identifier"
12804                | "scoped_identifier"
12805                | "scoped_type_identifier"
12806                | "type_identifier"
12807                | "field_identifier"
12808                | "identifier"
12809                | "namespace_identifier"
12810                | "primitive_type"
12811                | "template_type" => {
12812                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
12813                    values.push(cpp_push_comparable_node(
12814                        &mut nodes,
12815                        CppComparableNode::Named {
12816                            name,
12817                            primitive: node.kind() == "primitive_type",
12818                            konst: qualifiers.konst,
12819                            volatil: qualifiers.volatil,
12820                        },
12821                    ));
12822                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
12823                        let mut cursor = arguments_node.walk();
12824                        let arguments = arguments_node
12825                            .named_children(&mut cursor)
12826                            .filter(|child| !child.is_extra() && child.kind() != "comment")
12827                            .collect::<Vec<_>>();
12828                        work.push(Work::BuildGeneric {
12829                            argument_count: arguments.len(),
12830                        });
12831                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
12832                            node: argument,
12833                            qualifiers: CppCvQualifiers::default(),
12834                        }));
12835                    }
12836                }
12837                _ => {
12838                    let inner = node.child_by_field_name("type").or_else(|| {
12839                        (node.named_child_count() == 1)
12840                            .then(|| node.named_child(0))
12841                            .flatten()
12842                    })?;
12843                    work.push(Work::Visit {
12844                        node: inner,
12845                        qualifiers,
12846                    });
12847                }
12848            },
12849            Work::ApplyLevels(levels) => {
12850                let mut root = values.pop()?;
12851                for level in levels {
12852                    let node = match level {
12853                        CppComparableLevel::Pointer { konst, volatil } => {
12854                            CppComparableNode::Pointer {
12855                                inner: root,
12856                                konst,
12857                                volatil,
12858                            }
12859                        }
12860                        CppComparableLevel::Reference => {
12861                            CppComparableNode::Reference { inner: root }
12862                        }
12863                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
12864                    };
12865                    root = cpp_push_comparable_node(&mut nodes, node);
12866                }
12867                values.push(root);
12868            }
12869            Work::BuildGeneric { argument_count } => {
12870                let value_count = argument_count.checked_add(1)?;
12871                let start = values.len().checked_sub(value_count)?;
12872                let mut built = values.split_off(start);
12873                let base = built.remove(0);
12874                values.push(cpp_push_comparable_node(
12875                    &mut nodes,
12876                    CppComparableNode::Generic {
12877                        base,
12878                        arguments: built,
12879                    },
12880                ));
12881            }
12882        }
12883    }
12884    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
12885    debug_assert_eq!(
12886        root,
12887        nodes.len().saturating_sub(1),
12888        "comparable nodes are appended in post-order, so the root is the last one"
12889    );
12890    Some(CppComparableParameter { nodes, root })
12891}
12892
12893fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
12894    nodes.push(node);
12895    nodes.len() - 1
12896}
12897
12898/// The template argument list of the name `node` terminates in, if any.
12899///
12900/// `std::vector<int>` writes its arguments on the `name` of a qualified
12901/// identifier, so a walk that stopped at the qualified node would reduce
12902/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
12903fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
12904    let mut current = node;
12905    loop {
12906        match current.kind() {
12907            "template_type" => return current.child_by_field_name("arguments"),
12908            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12909                current = current.child_by_field_name("name")?;
12910            }
12911            _ => return None,
12912        }
12913    }
12914}
12915
12916/// The callable declarator of the declaration that covers `start_byte`.
12917///
12918/// A consumer that holds a declaration's recorded byte position rather than its
12919/// syntax node - external header extraction, for instance - uses this to reach
12920/// the same `function_declarator` the declaration walk read.
12921pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
12922    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
12923    loop {
12924        if matches!(
12925            current.kind(),
12926            "declaration" | "field_declaration" | "function_definition"
12927        ) && let Some(declarator) = current
12928            .child_by_field_name("declarator")
12929            .and_then(extract_function_declarator)
12930        {
12931            return Some(declarator);
12932        }
12933        current = current.parent()?;
12934    }
12935}
12936
12937fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
12938    let mut labels = Vec::new();
12939    let mut cursor = parameters_node.walk();
12940    for child in parameters_node.children(&mut cursor) {
12941        match child.kind() {
12942            "parameter_declaration" | "optional_parameter_declaration" => {
12943                if let Some(name_node) = child
12944                    .child_by_field_name("declarator")
12945                    .and_then(cpp_declarator_label_node)
12946                {
12947                    labels.push(name_node);
12948                } else {
12949                    labels.push(child);
12950                }
12951            }
12952            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12953                labels.push(child);
12954            }
12955            _ => {}
12956        }
12957    }
12958    labels
12959}
12960
12961fn cpp_signature_search_start<'tree>(
12962    signature: &str,
12963    function_declarator: Node<'tree>,
12964    source: &str,
12965    ancestry: &ParentIndex<'tree>,
12966) -> usize {
12967    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
12968        return 0;
12969    };
12970    let raw = node_text(enclosing, source);
12971    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
12972    let offset = function_declarator
12973        .start_byte()
12974        .saturating_sub(enclosing.start_byte())
12975        .saturating_sub(leading_trim_bytes);
12976    offset.min(signature.len())
12977}
12978
12979fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
12980    match node.kind() {
12981        "identifier" | "field_identifier" => Some(node),
12982        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
12983            .child_by_field_name("declarator")
12984            .or_else(|| last_named_child(node))
12985            .and_then(cpp_declarator_label_node),
12986        "array_declarator" => node
12987            .child_by_field_name("declarator")
12988            .and_then(cpp_declarator_label_node),
12989        "function_declarator" => node
12990            .child_by_field_name("declarator")
12991            .or_else(|| node.child_by_field_name("name"))
12992            .or_else(|| last_named_child(node))
12993            .and_then(cpp_declarator_label_node),
12994        _ => None,
12995    }
12996}
12997
12998fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
12999    let base_type = parameter
13000        .child_by_field_name("type")
13001        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
13002        .unwrap_or_default();
13003    let declarator = cpp_parameter_declarator(parameter);
13004    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
13005    // are discarded, so `f(const int)` and `f(int)` declare one function. A
13006    // qualifier written next to the parameter's type is only top-level when
13007    // the declarator adds no indirection; behind a pointer, reference or array
13008    // declarator the same qualifier belongs to the pointee, referent or
13009    // element and keeps distinguishing the type (#1827).
13010    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
13011    let mut cursor = parameter.walk();
13012    let qualifiers = parameter
13013        .named_children(&mut cursor)
13014        .filter(|child| child.kind() == "type_qualifier")
13015        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13016        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
13017        .collect::<Vec<_>>()
13018        .join(" ");
13019    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
13020        (true, _) => base_type,
13021        (_, true) => qualifiers,
13022        (false, false) => format!("{qualifiers} {base_type}"),
13023    };
13024    let declarator_suffix = declarator
13025        .map(|node| cpp_declarator_suffix_without_name(node, source))
13026        .unwrap_or_default();
13027
13028    let combined = if type_text.is_empty() {
13029        declarator_suffix
13030    } else if declarator_suffix.is_empty() {
13031        type_text
13032    } else {
13033        format!("{type_text} {declarator_suffix}")
13034    };
13035    normalize_cpp_type_text(&combined)
13036}
13037
13038fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
13039    parameter.child_by_field_name("declarator").or_else(|| {
13040        // Some unnamed prototype parameters expose their abstract declarator
13041        // as a direct named child without the grammar's `declarator` field.
13042        // Recover only the structured abstract-declarator node; the parameter's
13043        // type and qualifiers are distinct children and must not be guessed from
13044        // source text.
13045        let mut cursor = parameter.walk();
13046        parameter
13047            .named_children(&mut cursor)
13048            .find(|child| is_cpp_abstract_declarator(child.kind()))
13049    })
13050}
13051
13052/// Whether a parameter's declarator chain adds indirection - a pointer,
13053/// reference, array or function declarator - to the parameter's written type.
13054pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
13055    let mut current = Some(declarator);
13056    while let Some(node) = current {
13057        if matches!(
13058            node.kind(),
13059            "pointer_declarator"
13060                | "abstract_pointer_declarator"
13061                | "reference_declarator"
13062                | "abstract_reference_declarator"
13063                | "array_declarator"
13064                | "abstract_array_declarator"
13065                | "function_declarator"
13066                | "abstract_function_declarator"
13067        ) {
13068            return true;
13069        }
13070        current = cpp_nested_declarator(node);
13071    }
13072    false
13073}
13074
13075fn is_cpp_abstract_declarator(kind: &str) -> bool {
13076    matches!(
13077        kind,
13078        "abstract_pointer_declarator"
13079            | "abstract_reference_declarator"
13080            | "abstract_array_declarator"
13081            | "abstract_function_declarator"
13082            | "abstract_parenthesized_declarator"
13083    )
13084}
13085
13086fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
13087    node.child_by_field_name("declarator").or_else(|| {
13088        if is_cpp_abstract_declarator(node.kind()) {
13089            let mut cursor = node.walk();
13090            node.named_children(&mut cursor)
13091                .find(|child| is_cpp_abstract_declarator(child.kind()))
13092        } else {
13093            // Named declarators historically use their last named child when
13094            // tree-sitter omits the field. Keep that broad fallback for
13095            // attributed, variadic, and recovered named shapes.
13096            last_named_child(node)
13097        }
13098    })
13099}
13100
13101fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
13102    match node.kind() {
13103        "identifier" | "field_identifier" => String::new(),
13104        "pointer_declarator" | "abstract_pointer_declarator" => {
13105            let inner = cpp_nested_declarator(node)
13106                .map(|child| cpp_declarator_suffix_without_name(child, source))
13107                .unwrap_or_default();
13108            format!("*{inner}")
13109        }
13110        "reference_declarator" | "abstract_reference_declarator" => {
13111            let inner = cpp_nested_declarator(node)
13112                .map(|child| cpp_declarator_suffix_without_name(child, source))
13113                .unwrap_or_default();
13114            let reference = node
13115                .children(&mut node.walk())
13116                .find(|child| matches!(child.kind(), "&" | "&&"))
13117                .map(|child| node_text(child, source))
13118                .unwrap_or("&");
13119            format!("{reference}{inner}")
13120        }
13121        "array_declarator" | "abstract_array_declarator" => {
13122            let inner = cpp_nested_declarator(node)
13123                .map(|child| cpp_declarator_suffix_without_name(child, source))
13124                .unwrap_or_default();
13125            let size = node
13126                .child_by_field_name("size")
13127                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13128                .unwrap_or_default();
13129            format!("{inner}[{size}]")
13130        }
13131        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
13132            let inner = cpp_nested_declarator(node);
13133            inner
13134                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
13135                .unwrap_or_default()
13136        }
13137        "function_declarator" | "abstract_function_declarator" => {
13138            let inner = cpp_nested_declarator(node)
13139                .map(|child| cpp_declarator_suffix_without_name(child, source))
13140                .unwrap_or_default();
13141            let params = node
13142                .child_by_field_name("parameters")
13143                .map(|child| cpp_parameter_signature(child, source))
13144                .unwrap_or_else(|| "()".to_string());
13145            format!("{inner}{params}")
13146        }
13147        _ => {
13148            let text = normalize_cpp_whitespace(node_text(node, source));
13149            let name = extract_declarator_name(node, source);
13150            if name.is_empty() {
13151                text
13152            } else {
13153                text.replace(&name, "").trim().to_string()
13154            }
13155        }
13156    }
13157}
13158
13159fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
13160    collapse_cpp_whitespace(
13161        suffix
13162            .trim()
13163            .trim_start_matches("->")
13164            .trim_start_matches('{')
13165            .trim_end_matches(';'),
13166    )
13167}
13168
13169pub fn normalize_cpp_whitespace(value: &str) -> String {
13170    collapse_cpp_whitespace(value)
13171}
13172
13173fn normalize_cpp_type_text(value: &str) -> String {
13174    collapse_cpp_whitespace(value)
13175        .replace(", ", ",")
13176        .replace(" <", "<")
13177        .replace("< ", "<")
13178        .replace(" >", ">")
13179}
13180
13181fn collapse_cpp_whitespace(value: &str) -> String {
13182    let mut result = String::new();
13183    let mut prev_space = false;
13184    for ch in value.chars() {
13185        if ch.is_whitespace() {
13186            if !prev_space {
13187                result.push(' ');
13188            }
13189            prev_space = true;
13190        } else {
13191            result.push(ch);
13192            prev_space = false;
13193        }
13194    }
13195    result.trim().to_string()
13196}
13197
13198pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
13199    node_source_text(node, source)
13200}
13201
13202pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
13203    walk_named_tree_preorder(node, true, |node| {
13204        match node.kind() {
13205            "type_identifier" | "identifier" | "qualified_identifier" => {
13206                let text = node_text(node, source).trim();
13207                if !text.is_empty() {
13208                    identifiers.insert(text.to_string());
13209                }
13210            }
13211            _ => {}
13212        }
13213        WalkControl::Continue
13214    });
13215}
13216
13217fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
13218    node.child_by_field_name("body").or_else(|| {
13219        let mut cursor = node.walk();
13220        node.named_children(&mut cursor).find(|child| {
13221            matches!(
13222                child.kind(),
13223                "declaration_list" | "field_declaration_list" | "enumerator_list"
13224            )
13225        })
13226    })
13227}
13228
13229/// Return a class body's actual closing brace when the parser supplied one.
13230///
13231/// A malformed namespace sentinel can leave a class node carrying unrelated
13232/// parser errors even though its own class body is complete.  `has_error()` is
13233/// therefore too coarse an admission predicate for sentinel ownership.  The
13234/// body list, however, exposes the opening and closing punctuation directly;
13235/// a real (non-missing) final `}` proves that the class did not borrow the
13236/// enclosing namespace's close.  Requiring the body to end before its parent
13237/// container also rejects a recovered node whose body swallowed that outer
13238/// boundary.
13239fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
13240    if !matches!(
13241        node.kind(),
13242        "class_specifier" | "struct_specifier" | "union_specifier"
13243    ) {
13244        return None;
13245    }
13246    let body = cpp_body_node(node)?;
13247    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
13248        return None;
13249    }
13250    let open = body.child(0)?;
13251    let close = body.child(body.child_count().checked_sub(1)?)?;
13252    if open.kind() != "{"
13253        || open.is_missing()
13254        || close.kind() != "}"
13255        || close.is_missing()
13256        || close.end_byte() != body.end_byte()
13257        || body.end_byte() > node.end_byte()
13258        || node
13259            .parent()
13260            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
13261    {
13262        return None;
13263    }
13264    Some(close)
13265}
13266
13267fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
13268    if node.kind() == "namespace_definition" {
13269        return true;
13270    }
13271    let mut cursor = node.walk();
13272    node.named_children(&mut cursor)
13273        .any(cpp_contains_namespace_definition)
13274}
13275
13276struct CppNestedNamespaceSentinel<'tree> {
13277    function: Node<'tree>,
13278    body: Node<'tree>,
13279    namespace_components: Vec<String>,
13280}
13281
13282/// Owned structural recovery metadata for a namespace-sentinel region.
13283///
13284/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
13285/// instead of the namespace/class scopes that the declaration visitor restores.
13286/// The inverted usage walk has the original CST, so it needs the same ownership
13287/// evidence without borrowing parser nodes across its file scan.  Keep this
13288/// descriptor deliberately source-range based: callers can match a reference
13289/// node by containment and then resolve its structured type spelling in the
13290/// recovered class scope.
13291#[derive(Debug, Clone)]
13292pub struct CppSentinelRecoveredOwner {
13293    pub range: Range,
13294    /// Start of the qualified owner name (`btree<P>::method`).  A leading
13295    /// return type before this byte is looked up from the namespace; parameters,
13296    /// trailing returns, and the body use the member owner scope.
13297    pub owner_name_start_byte: usize,
13298    /// Number of leading components belonging to the namespace rather than
13299    /// the qualified class owner.  A leading return type is looked up before
13300    /// every owner component, not merely before the innermost class.
13301    pub namespace_component_count: usize,
13302    pub scope_components: Vec<String>,
13303}
13304
13305#[derive(Debug, Clone)]
13306pub struct CppSentinelRecoveredClass {
13307    pub namespace_range: Range,
13308    pub namespace_scope_components: Vec<String>,
13309    pub class_range: Range,
13310    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
13311    pub scope_components: Vec<String>,
13312    /// Qualified out-of-line member definitions owned by this class.  Their
13313    /// ranges may extend beyond `class_range` when the malformed sentinel
13314    /// swallowed the namespace close and left definitions as function siblings.
13315    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
13316}
13317
13318/// Resolve the lexical scope restored for a node in a malformed
13319/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
13320/// outrank class spans, which in turn outrank the surviving namespace body.
13321/// The class ancestor suffix is recovered from the original CST so nested
13322/// members keep their complete `Outer::Inner` owner chain.
13323pub fn cpp_sentinel_recovered_scope_for_node(
13324    node: Node<'_>,
13325    source: &str,
13326    recovered_classes: &[CppSentinelRecoveredClass],
13327) -> Option<Vec<String>> {
13328    let contains =
13329        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
13330    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
13331    for recovered in recovered_classes {
13332        for owner in recovered
13333            .owner_ranges
13334            .iter()
13335            .filter(|owner| contains(owner.range))
13336        {
13337            let replace = best_owner.is_none_or(|existing| {
13338                owner.range.end_byte.saturating_sub(owner.range.start_byte)
13339                    < existing
13340                        .range
13341                        .end_byte
13342                        .saturating_sub(existing.range.start_byte)
13343            });
13344            if replace {
13345                best_owner = Some(owner);
13346            }
13347        }
13348    }
13349    if let Some(owner) = best_owner {
13350        let mut scope = owner.scope_components.clone();
13351        if node.start_byte() < owner.owner_name_start_byte {
13352            scope.truncate(owner.namespace_component_count);
13353        }
13354        return Some(scope);
13355    }
13356
13357    let class = recovered_classes
13358        .iter()
13359        .filter(|recovered| contains(recovered.class_range))
13360        .min_by_key(|recovered| {
13361            recovered
13362                .class_range
13363                .end_byte
13364                .saturating_sub(recovered.class_range.start_byte)
13365        });
13366    let class_scope = class.is_some();
13367    let mut scope = if let Some(class) = class {
13368        class.scope_components.clone()
13369    } else {
13370        let namespace = recovered_classes
13371            .iter()
13372            .filter(|recovered| contains(recovered.namespace_range))
13373            .min_by_key(|recovered| {
13374                recovered
13375                    .namespace_range
13376                    .end_byte
13377                    .saturating_sub(recovered.namespace_range.start_byte)
13378            })?;
13379        let mut scope = namespace.namespace_scope_components.clone();
13380        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
13381        let common_prefix = scope
13382            .iter()
13383            .zip(&parser_namespace)
13384            .take_while(|(recovered, parser)| recovered == parser)
13385            .count();
13386        scope.extend(parser_namespace.into_iter().skip(common_prefix));
13387        scope
13388    };
13389    if class_scope {
13390        let mut ancestor_components = Vec::new();
13391        let mut ancestor = node.parent();
13392        while let Some(current) = ancestor {
13393            if matches!(
13394                current.kind(),
13395                "class_specifier" | "struct_specifier" | "union_specifier"
13396            ) && let Some(name) = current.child_by_field_name("name")
13397                && let Some(name_components) = cpp_name_components(name, source)
13398            {
13399                ancestor_components.push(
13400                    name_components
13401                        .into_iter()
13402                        .map(|component| component.name)
13403                        .collect::<Vec<_>>(),
13404                );
13405            }
13406            ancestor = current.parent();
13407        }
13408        ancestor_components.reverse();
13409        let base_len = scope.len();
13410        for component in ancestor_components.into_iter().flatten() {
13411            if scope.len() >= base_len && scope.last() == Some(&component) {
13412                continue;
13413            }
13414            scope.push(component);
13415        }
13416    }
13417    Some(scope)
13418}
13419
13420struct CppSentinelFragmentedClassTail<'tree> {
13421    class_node: Node<'tree>,
13422    template_node: Option<Node<'tree>>,
13423    name: String,
13424    raw_supertypes: Option<Vec<String>>,
13425    fragmented: FragmentedExportBody,
13426    consumed_start: usize,
13427}
13428
13429struct CppSentinelFragmentedClassErrorPrefix<'tree> {
13430    name: String,
13431    open: Node<'tree>,
13432    raw_supertypes: Option<Vec<String>>,
13433}
13434
13435struct CppSentinelDirectBodyClassRegion {
13436    namespace_components: Vec<String>,
13437    class_start: usize,
13438    class_start_line: usize,
13439    class_close_end: usize,
13440    class_close_line: usize,
13441    name: String,
13442}
13443
13444fn cpp_sentinel_body_class_candidate<'tree>(
13445    child: Node<'tree>,
13446) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13447    if matches!(
13448        child.kind(),
13449        "class_specifier" | "struct_specifier" | "union_specifier"
13450    ) {
13451        return Some((child, None));
13452    }
13453    if child.kind() != "template_declaration" {
13454        if child.kind() == "declaration" {
13455            return Some((first_class_like_child(child)?, None));
13456        }
13457        return None;
13458    }
13459    let mut cursor = child.walk();
13460    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
13461        if matches!(
13462            candidate.kind(),
13463            "class_specifier" | "struct_specifier" | "union_specifier"
13464        ) {
13465            Some(candidate)
13466        } else if candidate.kind() == "declaration" {
13467            first_class_like_child(candidate)
13468        } else {
13469            None
13470        }
13471    })?;
13472    Some((class_node, Some(child)))
13473}
13474
13475/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
13476/// namespace-sentinel body when a later member macro ends the bogus sentinel
13477/// function before the real class close. The anonymous class/open tokens and
13478/// direct identifier are the structural proof; a retained direct close would
13479/// be an ordinary malformed class rather than the fragmented tail handled here.
13480fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
13481    node: Node<'tree>,
13482    source: &str,
13483) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
13484    let name = malformed_class_error_owner_name(node, source)?;
13485    let mut cursor = node.walk();
13486    let children = node.children(&mut cursor).collect::<Vec<_>>();
13487    let keyword = children.first()?;
13488    let open_index = children.iter().position(|child| child.kind() == "{")?;
13489    if children[open_index + 1..]
13490        .iter()
13491        .any(|child| child.kind() == "}")
13492    {
13493        return None;
13494    }
13495    let raw_supertypes =
13496        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
13497    Some(CppSentinelFragmentedClassErrorPrefix {
13498        name,
13499        open: children[open_index],
13500        raw_supertypes,
13501    })
13502}
13503
13504fn cpp_sentinel_direct_body_class_candidate<'tree>(
13505    child: Node<'tree>,
13506) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13507    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
13508        return Some(candidate);
13509    }
13510    if child.kind() != "template_declaration" {
13511        return None;
13512    }
13513    let mut cursor = child.walk();
13514    let wrapper = child
13515        .named_children(&mut cursor)
13516        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
13517    Some((first_class_like_child(wrapper)?, Some(child)))
13518}
13519
13520fn cpp_sentinel_direct_namespace_components(
13521    function: Node<'_>,
13522    body: Node<'_>,
13523    source: &str,
13524) -> Option<Vec<String>> {
13525    let mut cursor = function.walk();
13526    let children = function
13527        .named_children(&mut cursor)
13528        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
13529        .collect::<Vec<_>>();
13530    let sentinel_index = children.iter().rposition(|child| {
13531        direct_identifier_name(*child, source)
13532            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
13533    })?;
13534    let mut identifiers = Vec::new();
13535    let mut stack = children[sentinel_index + 1..]
13536        .iter()
13537        .rev()
13538        .copied()
13539        .collect::<Vec<_>>();
13540    while let Some(current) = stack.pop() {
13541        if let Some(name) = direct_identifier_name(current, source) {
13542            identifiers.push(name);
13543            continue;
13544        }
13545        let mut cursor = current.walk();
13546        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
13547        stack.extend(children.into_iter().rev());
13548    }
13549    let [keyword, namespace] = identifiers.as_slice() else {
13550        return None;
13551    };
13552    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
13553        .then(|| vec![namespace.clone()])
13554}
13555
13556fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
13557    let mut sibling = class_semicolon.next_named_sibling();
13558    let namespace_close = loop {
13559        let Some(current) = sibling else {
13560            return false;
13561        };
13562        sibling = current.next_named_sibling();
13563        if current.kind() != "comment" {
13564            break current;
13565        }
13566    };
13567    if !cpp_is_stray_close_brace(namespace_close, source) {
13568        return false;
13569    }
13570    loop {
13571        let Some(current) = sibling else {
13572            return false;
13573        };
13574        sibling = current.next_named_sibling();
13575        if current.kind() == "comment" {
13576            continue;
13577        }
13578        return direct_identifier_name(current, source)
13579            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
13580    }
13581}
13582
13583fn cpp_sentinel_macro_body_class_region<'tree>(
13584    node: Node<'tree>,
13585    source: &str,
13586    ancestry: &ParentIndex<'tree>,
13587) -> Option<CppSentinelDirectBodyClassRegion> {
13588    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
13589        return None;
13590    };
13591    if node.kind() != "function_definition" || !node.has_error() {
13592        return None;
13593    }
13594    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
13595    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
13596    let mut cursor = body.walk();
13597    let candidates = body
13598        .named_children(&mut cursor)
13599        .filter_map(cpp_sentinel_direct_body_class_candidate)
13600        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
13601        .collect::<Vec<_>>();
13602    let [(class_node, template_node)] = candidates.as_slice() else {
13603        return None;
13604    };
13605    let original_body = cpp_body_node(*class_node)?;
13606    let name = class_like_name(*class_node, source, ancestry)?;
13607    if name.is_empty() || cpp_export_macro_token(&name) {
13608        return None;
13609    }
13610
13611    let mut sibling = node.next_named_sibling();
13612    let (class_close_start, class_close_end, class_close_line) = loop {
13613        let current = sibling?;
13614        let next = current.next_named_sibling();
13615        if cpp_is_stray_close_brace(current, source)
13616            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
13617        {
13618            let semicolon = next.expect("checked above");
13619            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
13620                return None;
13621            }
13622            break (
13623                current.start_byte(),
13624                semicolon.end_byte(),
13625                semicolon.end_position().row + 1,
13626            );
13627        }
13628        sibling = next;
13629    };
13630    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
13631    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
13632    let root = tree.root_node();
13633    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
13634    // The region reparse is its own tree, so it needs its own parent index;
13635    // the caller's index answers nothing about these nodes.
13636    let reparsed_ancestry = ParentIndex::new(root);
13637    let reparsed =
13638        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
13639    if reparsed.name != name
13640        || reparsed.declaration_node.start_byte() != class_node.start_byte()
13641        || reparsed.body.start_byte() != original_body.start_byte()
13642        || class_close_start <= reparsed.body.end_byte()
13643        || class_close_end <= class_node.end_byte()
13644    {
13645        return None;
13646    }
13647    Some(CppSentinelDirectBodyClassRegion {
13648        namespace_components,
13649        class_start: reparse_start,
13650        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
13651            node.start_position().row + 1
13652        }),
13653        class_close_end,
13654        class_close_line,
13655        name,
13656    })
13657}
13658
13659/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
13660/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
13661///
13662/// The parser puts the namespace opener and the malformed function in one root
13663/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
13664/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
13665/// the malformed function must begin with an all-caps type, then an ERROR whose
13666/// sole identifier is `namespace`, followed by the inner namespace identifier
13667/// and a compound body; and that body must contain a complete named class or a
13668/// structurally fragmented class prefix. A text reparse cannot prove any of
13669/// those ownership boundaries.
13670fn cpp_nested_namespace_sentinel<'tree>(
13671    node: Node<'tree>,
13672    source: &str,
13673    ancestry: &ParentIndex<'tree>,
13674) -> Option<CppNestedNamespaceSentinel<'tree>> {
13675    if !node.has_error() {
13676        return None;
13677    }
13678
13679    let (function, mut namespace_components) = if node.kind() == "ERROR" {
13680        let mut cursor = node.walk();
13681        let functions = node
13682            .named_children(&mut cursor)
13683            .filter(|child| child.kind() == "function_definition")
13684            .collect::<Vec<_>>();
13685        let [function] = functions.as_slice() else {
13686            return None;
13687        };
13688        if !function.has_error() {
13689            return None;
13690        }
13691        let mut cursor = node.walk();
13692        let children = node.children(&mut cursor).collect::<Vec<_>>();
13693        let function_index = children
13694            .iter()
13695            .position(|child| same_node(*child, *function))?;
13696        let [outer_keyword, outer_name, outer_open] =
13697            children.get(function_index.checked_sub(3)?..function_index)?
13698        else {
13699            return None;
13700        };
13701        if outer_keyword.kind() != "namespace"
13702            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
13703            || outer_open.kind() != "{"
13704        {
13705            return None;
13706        }
13707        (
13708            *function,
13709            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
13710        )
13711    } else if node.kind() == "function_definition" {
13712        let declaration_list = node.parent()?;
13713        let namespace = declaration_list.parent()?;
13714        if declaration_list.kind() != "declaration_list"
13715            || namespace.kind() != "namespace_definition"
13716            || namespace.child_by_field_name("body") != Some(declaration_list)
13717        {
13718            return None;
13719        }
13720        (node, Vec::new())
13721    } else {
13722        return None;
13723    };
13724
13725    let mut cursor = function.walk();
13726    let named = function
13727        .named_children(&mut cursor)
13728        .filter(|child| child.kind() != "comment")
13729        .collect::<Vec<_>>();
13730    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
13731        return None;
13732    };
13733    if first_type.kind() != "type_identifier" {
13734        return None;
13735    }
13736    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
13737    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
13738        return None;
13739    }
13740    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
13741        return None;
13742    }
13743    let inner_keyword = inner_error.named_child(0)?;
13744    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
13745        return None;
13746    }
13747    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
13748        return None;
13749    }
13750    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
13751    if inner_name.is_empty() || body.kind() != "compound_statement" {
13752        return None;
13753    }
13754    namespace_components.push(inner_name);
13755
13756    let mut cursor = body.walk();
13757    let has_complete_class = body.named_children(&mut cursor).any(|child| {
13758        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13759            cpp_body_node(class_node).is_some()
13760                && class_like_name(class_node, source, ancestry)
13761                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13762        })
13763    });
13764    if !has_complete_class
13765        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
13766    {
13767        return None;
13768    }
13769
13770    Some(CppNestedNamespaceSentinel {
13771        function,
13772        body: *body,
13773        namespace_components,
13774    })
13775}
13776
13777/// Recognize a namespace-begin sentinel directly beneath the translation unit.
13778///
13779/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
13780/// function whose type is the sentinel, whose declarator is the structured
13781/// qualified name `namespace::a::b`, and whose body contains the namespace
13782/// items. Declaration indexing already reparses this bounded region. The
13783/// inverse scanner retains the original tree, so recover the same namespace
13784/// components from the declarator fields for its lexical-scope metadata.
13785fn cpp_root_namespace_sentinel<'tree>(
13786    node: Node<'tree>,
13787    source: &str,
13788    ancestry: &ParentIndex<'tree>,
13789) -> Option<CppNestedNamespaceSentinel<'tree>> {
13790    if node.kind() != "function_definition"
13791        || !node.has_error()
13792        || node.parent()?.kind() != "translation_unit"
13793    {
13794        return None;
13795    }
13796    let first_type = node.child_by_field_name("type")?;
13797    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
13798    if first_type.kind() != "type_identifier"
13799        || sentinel.is_empty()
13800        || !cpp_export_macro_token(&sentinel)
13801    {
13802        return None;
13803    }
13804    let declarator = node.child_by_field_name("declarator")?;
13805    let body = node.child_by_field_name("body")?;
13806    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
13807        return None;
13808    }
13809    let mut cursor = node.walk();
13810    let named = node
13811        .named_children(&mut cursor)
13812        .filter(|child| child.kind() != "comment")
13813        .collect::<Vec<_>>();
13814    let [named_type, named_declarator, named_body] = named.as_slice() else {
13815        return None;
13816    };
13817    if !same_node(*named_type, first_type)
13818        || !same_node(*named_declarator, declarator)
13819        || !same_node(*named_body, body)
13820    {
13821        return None;
13822    }
13823    let mut declarator_components = Vec::new();
13824    let mut valid_components = true;
13825    walk_named_tree_preorder(declarator, true, |component| {
13826        if !matches!(
13827            component.kind(),
13828            "identifier" | "namespace_identifier" | "type_identifier"
13829        ) {
13830            return WalkControl::Continue;
13831        }
13832        let Some(component) = canonical_cpp_qualified_component(component, source) else {
13833            valid_components = false;
13834            return WalkControl::Break;
13835        };
13836        declarator_components.push(component.name);
13837        WalkControl::SkipChildren
13838    });
13839    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
13840        return None;
13841    }
13842    declarator_components.remove(0);
13843    let namespace_components = declarator_components;
13844    if namespace_components.is_empty()
13845        || namespace_components
13846            .iter()
13847            .any(|component| component.is_empty() || cpp_export_macro_token(component))
13848    {
13849        return None;
13850    }
13851
13852    let mut cursor = body.walk();
13853    let has_complete_class = body.named_children(&mut cursor).any(|child| {
13854        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13855            cpp_body_node(class_node).is_some()
13856                && class_like_name(class_node, source, ancestry)
13857                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13858        })
13859    });
13860    if !has_complete_class
13861        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
13862    {
13863        return None;
13864    }
13865
13866    Some(CppNestedNamespaceSentinel {
13867        function: node,
13868        body,
13869        namespace_components,
13870    })
13871}
13872
13873/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
13874/// malformed namespace-sentinel function.  The recovery is deliberately
13875/// structural: the class must be a direct body item, its own class node must be
13876/// erroneous and end before a unique anonymous `}` in the enclosing
13877/// declaration-list, and that namespace's next sibling must be a standalone
13878/// `;`.  The complete interior must pass the existing member-shaped reparse
13879/// gate. This avoids source brace scans and does not borrow a close from an
13880/// unrelated later declaration.
13881fn cpp_sentinel_fragmented_class_tail<'tree>(
13882    function: Node<'tree>,
13883    body: Node<'tree>,
13884    source: &str,
13885    ancestry: &ParentIndex<'tree>,
13886) -> Option<CppSentinelFragmentedClassTail<'tree>> {
13887    let mut cursor = body.walk();
13888    let candidates = body
13889        .named_children(&mut cursor)
13890        .filter_map(|child| {
13891            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
13892                let class_body = cpp_body_node(class_node)?;
13893                if !class_node.has_error() {
13894                    return None;
13895                }
13896                let name = class_like_name(class_node, source, ancestry)?;
13897                let raw_supertypes =
13898                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
13899                        .then(|| extract_cpp_supertypes(class_node, source));
13900                return Some((
13901                    class_node,
13902                    template_node,
13903                    name,
13904                    class_body,
13905                    class_body.start_byte().checked_add(1)?,
13906                    raw_supertypes,
13907                ));
13908            }
13909            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
13910            Some((
13911                child,
13912                None,
13913                prefix.name,
13914                prefix.open,
13915                prefix.open.end_byte(),
13916                prefix.raw_supertypes,
13917            ))
13918        })
13919        .collect::<Vec<_>>();
13920    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
13921        candidates.as_slice()
13922    else {
13923        return None;
13924    };
13925    if name.is_empty() || cpp_export_macro_token(name) {
13926        return None;
13927    }
13928
13929    let (close, semicolon) =
13930        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
13931
13932    let reparse_end = close.start_byte();
13933    if *reparse_start >= reparse_end {
13934        return None;
13935    }
13936    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
13937    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
13938        return None;
13939    }
13940    let class_range = Range {
13941        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13942        end_byte: semicolon.end_byte(),
13943        start_line: template_node.map_or(class_node.start_position().row, |node| {
13944            node.start_position().row
13945        }) + 1,
13946        end_line: semicolon.end_position().row + 1,
13947    };
13948    Some(CppSentinelFragmentedClassTail {
13949        class_node: *class_node,
13950        template_node: *template_node,
13951        name: name.clone(),
13952        raw_supertypes: raw_supertypes.clone(),
13953        fragmented: FragmentedExportBody {
13954            reparse_start: *reparse_start,
13955            reparse_end,
13956            class_range,
13957        },
13958        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13959    })
13960}
13961
13962/// Recover the class and out-of-line owner scopes from every malformed
13963/// namespace-sentinel region in `root`.
13964///
13965/// This is the shared structural counterpart to
13966/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
13967/// reuses the visitor's sentinel/class admission predicates instead of parsing
13968/// source text a second time.  The returned values own only ranges and names, so
13969/// they can be retained by an inverted usage scan after the tree borrow ends.
13970pub fn cpp_sentinel_recovered_classes(
13971    root: Node<'_>,
13972    source: &str,
13973) -> Vec<CppSentinelRecoveredClass> {
13974    if !root.has_error() {
13975        return Vec::new();
13976    }
13977    // This scan owns its walk of `root`, so it owns the parent index that walk
13978    // asks its ancestor questions through. Built after the error gate: a clean
13979    // tree returns without paying for one.
13980    let ancestry = ParentIndex::new(root);
13981    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
13982    let mut stack = vec![root];
13983    while let Some(current) = stack.pop() {
13984        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
13985            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
13986        {
13987            let namespace_components = cpp_sentinel_recovered_namespace_components(
13988                recovered.function,
13989                &recovered.namespace_components,
13990                source,
13991            );
13992            let fragmented = cpp_sentinel_fragmented_class_tail(
13993                recovered.function,
13994                recovered.body,
13995                source,
13996                &ancestry,
13997            );
13998            let mut class_candidates = Vec::new();
13999            let mut cursor = recovered.body.walk();
14000            for (class_node, template_node) in recovered
14001                .body
14002                .named_children(&mut cursor)
14003                .filter_map(cpp_sentinel_body_class_candidate)
14004            {
14005                let Some(name) = class_like_name(class_node, source, &ancestry) else {
14006                    continue;
14007                };
14008                if name.is_empty() || cpp_export_macro_token(&name) {
14009                    continue;
14010                }
14011                let is_fragmented = fragmented
14012                    .as_ref()
14013                    .is_some_and(|tail| same_node(tail.class_node, class_node));
14014                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
14015                    continue;
14016                }
14017                let class_range = if is_fragmented {
14018                    fragmented
14019                        .as_ref()
14020                        .map(|tail| tail.fragmented.class_range)
14021                        .expect("fragmented class range is present when class matches")
14022                } else {
14023                    cpp_declaration_range(template_node.unwrap_or(class_node))
14024                };
14025                class_candidates.push((class_range, name));
14026            }
14027            if let Some(fragmented) = fragmented
14028                .as_ref()
14029                .filter(|tail| tail.class_node.kind() == "ERROR")
14030            {
14031                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
14032            }
14033
14034            let mut owner_ranges =
14035                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
14036            cpp_sentinel_extend_unique_owner_ranges(
14037                &mut owner_ranges,
14038                cpp_sentinel_recovered_sibling_owner_ranges(
14039                    recovered.function,
14040                    &namespace_components,
14041                    source,
14042                ),
14043            );
14044            for (class_range, name) in class_candidates {
14045                push_cpp_sentinel_recovered_class(
14046                    &mut recovered_classes,
14047                    cpp_declaration_range(recovered.body),
14048                    &namespace_components,
14049                    class_range,
14050                    name,
14051                    &owner_ranges,
14052                );
14053            }
14054
14055            if let Some(declaration_list) = recovered
14056                .function
14057                .parent()
14058                .filter(|parent| parent.kind() == "declaration_list")
14059            {
14060                let outer_namespace =
14061                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
14062                push_cpp_sentinel_sibling_classes(
14063                    &mut recovered_classes,
14064                    declaration_list,
14065                    recovered.function,
14066                    &outer_namespace,
14067                    source,
14068                    &ancestry,
14069                );
14070            }
14071        } else if let Some(region) =
14072            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
14073        {
14074            let namespace_components = cpp_sentinel_recovered_namespace_components(
14075                current,
14076                &region.namespace_components,
14077                source,
14078            );
14079            let owner_container = current
14080                .parent()
14081                .filter(|parent| parent.kind() == "declaration_list")
14082                .unwrap_or(current);
14083            let owner_ranges =
14084                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14085            push_cpp_sentinel_recovered_class(
14086                &mut recovered_classes,
14087                cpp_declaration_range(owner_container),
14088                &namespace_components,
14089                Range {
14090                    start_byte: region.class_start,
14091                    end_byte: region.class_close_end,
14092                    start_line: region.class_start_line,
14093                    end_line: region.class_close_line,
14094                },
14095                region.name,
14096                &owner_ranges,
14097            );
14098        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
14099            // A generic sentinel-prefixed class can be reduced as a malformed
14100            // function/ERROR without the explicit `namespace X` token pair.
14101            // Reuse the declaration visitor's bounded reparse and retain only
14102            // the recovered class identity/range here.
14103            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
14104                region;
14105            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14106                continue;
14107            };
14108            let root = tree.root_node();
14109            let template_node = cpp_sentinel_reparsed_leading_template(root);
14110            // A region reparse is its own tree and needs its own parent index.
14111            let reparsed_ancestry = ParentIndex::new(root);
14112            let Some(reparsed_class) =
14113                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
14114            else {
14115                continue;
14116            };
14117            let class_node = reparsed_class.declaration_node;
14118            let name = reparsed_class.name;
14119            let namespace_components =
14120                cpp_sentinel_recovered_namespace_components(current, &[], source);
14121            let owner_container = current
14122                .parent()
14123                .filter(|parent| parent.kind() == "declaration_list")
14124                .unwrap_or(current);
14125            let mut owner_ranges =
14126                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14127            cpp_sentinel_extend_unique_owner_ranges(
14128                &mut owner_ranges,
14129                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
14130            );
14131            push_cpp_sentinel_recovered_class(
14132                &mut recovered_classes,
14133                cpp_declaration_range(owner_container),
14134                &namespace_components,
14135                Range {
14136                    start_byte: class_start,
14137                    end_byte: close_end,
14138                    start_line: class_node.start_position().row + 1,
14139                    end_line: class_node.end_position().row + 1,
14140                },
14141                name,
14142                &owner_ranges,
14143            );
14144            if owner_container.kind() == "declaration_list" {
14145                push_cpp_sentinel_sibling_classes(
14146                    &mut recovered_classes,
14147                    owner_container,
14148                    current,
14149                    &namespace_components,
14150                    source,
14151                    &ancestry,
14152                );
14153            }
14154        }
14155
14156        let mut cursor = current.walk();
14157        stack.extend(current.named_children(&mut cursor));
14158    }
14159    // A shallower sentinel can expose nested classes as apparent namespace
14160    // siblings even after a deeper sentinel proves that a containing class
14161    // owns their ranges. Drop those shadow descriptors; scope recovery starts
14162    // from the proven containing class and appends parser-visible class
14163    // ancestors, preserving the full `Outer::Inner` chain.
14164    let shadowed = recovered_classes
14165        .iter()
14166        .map(|candidate| {
14167            recovered_classes.iter().any(|container| {
14168                container.class_range.start_byte <= candidate.class_range.start_byte
14169                    && container.class_range.end_byte >= candidate.class_range.end_byte
14170                    && container.class_range != candidate.class_range
14171                    && container.namespace_scope_components.len()
14172                        > candidate.namespace_scope_components.len()
14173                    && container
14174                        .namespace_scope_components
14175                        .starts_with(&candidate.namespace_scope_components)
14176            })
14177        })
14178        .collect::<Vec<_>>();
14179    let mut index = 0usize;
14180    recovered_classes.retain(|_| {
14181        let keep = !shadowed[index];
14182        index += 1;
14183        keep
14184    });
14185    recovered_classes
14186}
14187
14188/// A flat sentinel can swallow the first class while leaving later classes and
14189/// their out-of-line definitions as ordinary declaration-list siblings.  Once
14190/// the malformed class proves the sentinel envelope, retain those structurally
14191/// complete sibling classes under the same surviving namespace so every member
14192/// owner in the region uses one recovery contract.
14193fn push_cpp_sentinel_sibling_classes<'tree>(
14194    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14195    declaration_list: Node<'tree>,
14196    sentinel_node: Node<'tree>,
14197    namespace_components: &[String],
14198    source: &str,
14199    ancestry: &ParentIndex<'tree>,
14200) {
14201    let owner_ranges =
14202        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
14203    let namespace_range = cpp_declaration_range(declaration_list);
14204    let mut cursor = declaration_list.walk();
14205    for (class_node, template_node) in declaration_list
14206        .named_children(&mut cursor)
14207        .filter(|child| !same_node(*child, sentinel_node))
14208        .filter_map(cpp_sentinel_body_class_candidate)
14209    {
14210        let Some(name) = class_like_name(class_node, source, ancestry) else {
14211            continue;
14212        };
14213        if name.is_empty()
14214            || cpp_export_macro_token(&name)
14215            || cpp_complete_class_body_close(class_node).is_none()
14216        {
14217            continue;
14218        }
14219        push_cpp_sentinel_recovered_class(
14220            recovered_classes,
14221            namespace_range,
14222            namespace_components,
14223            cpp_declaration_range(template_node.unwrap_or(class_node)),
14224            name,
14225            &owner_ranges,
14226        );
14227    }
14228}
14229
14230fn push_cpp_sentinel_recovered_class(
14231    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14232    namespace_range: Range,
14233    namespace_components: &[String],
14234    class_range: Range,
14235    name: String,
14236    owner_ranges: &[CppSentinelRecoveredOwner],
14237) {
14238    let mut scope_components = namespace_components.to_vec();
14239    scope_components.push(name);
14240    let owner_ranges = owner_ranges
14241        .iter()
14242        .filter(|owner| owner.scope_components.starts_with(&scope_components))
14243        .cloned()
14244        .collect::<Vec<_>>();
14245    if recovered_classes.iter().any(|existing| {
14246        existing.class_range == class_range && existing.scope_components == scope_components
14247    }) {
14248        return;
14249    }
14250    recovered_classes.push(CppSentinelRecoveredClass {
14251        namespace_range,
14252        namespace_scope_components: namespace_components.to_vec(),
14253        class_range,
14254        scope_components,
14255        owner_ranges,
14256    });
14257}
14258
14259fn cpp_sentinel_recovered_namespace_components(
14260    function: Node<'_>,
14261    recovered_components: &[String],
14262    source: &str,
14263) -> Vec<String> {
14264    let mut ancestor_components = Vec::new();
14265    let mut ancestor = function.parent();
14266    while let Some(current) = ancestor {
14267        if current.kind() == "namespace_definition"
14268            && let Some(name_node) = current.child_by_field_name("name")
14269            && let Some(components) = cpp_name_components(name_node, source)
14270        {
14271            ancestor_components.push(
14272                components
14273                    .into_iter()
14274                    .map(|component| component.name)
14275                    .collect::<Vec<_>>(),
14276            );
14277        }
14278        ancestor = current.parent();
14279    }
14280    ancestor_components.reverse();
14281    let mut ancestors = ancestor_components
14282        .into_iter()
14283        .flatten()
14284        .collect::<Vec<_>>();
14285
14286    let overlap = (0..=ancestors.len().min(recovered_components.len()))
14287        .rev()
14288        .find(|length| {
14289            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
14290        })
14291        .unwrap_or(0);
14292    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
14293    ancestors
14294}
14295
14296fn cpp_sentinel_recovered_owner_ranges(
14297    body: Node<'_>,
14298    namespace_components: &[String],
14299    source: &str,
14300) -> Vec<CppSentinelRecoveredOwner> {
14301    let mut owners = Vec::new();
14302    walk_named_tree_preorder(body, true, |node| {
14303        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14304    });
14305    owners
14306}
14307
14308fn cpp_sentinel_collect_owner_range(
14309    node: Node<'_>,
14310    namespace_components: &[String],
14311    source: &str,
14312    owners: &mut Vec<CppSentinelRecoveredOwner>,
14313) -> WalkControl {
14314    if node.kind() != "function_definition" {
14315        return WalkControl::Continue;
14316    }
14317    let Some(function_declarator) = extract_function_declarator(node) else {
14318        return WalkControl::Continue;
14319    };
14320    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
14321        return WalkControl::Continue;
14322    };
14323    let Some(mut components) = cpp_name_components(name_node, source) else {
14324        return WalkControl::Continue;
14325    };
14326    if components.len() <= 1 {
14327        return WalkControl::Continue;
14328    }
14329    components.pop();
14330    let mut owner_components = components
14331        .into_iter()
14332        .map(|component| component.name)
14333        .collect::<Vec<_>>();
14334    let overlap = (0..=namespace_components.len().min(owner_components.len()))
14335        .rev()
14336        .find(|length| {
14337            owner_components[..*length]
14338                == namespace_components[namespace_components.len().saturating_sub(*length)..]
14339        })
14340        .unwrap_or(0);
14341    let mut scope_components = namespace_components.to_vec();
14342    scope_components.extend(owner_components.drain(overlap..));
14343    if scope_components.len() <= namespace_components.len() {
14344        return WalkControl::Continue;
14345    }
14346    let range = cpp_declaration_range(node);
14347    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
14348        existing.range == range && existing.scope_components == scope_components
14349    }) {
14350        owners.push(CppSentinelRecoveredOwner {
14351            range,
14352            owner_name_start_byte: name_node.start_byte(),
14353            namespace_component_count: namespace_components.len(),
14354            scope_components,
14355        });
14356    }
14357    WalkControl::Continue
14358}
14359
14360fn cpp_sentinel_extend_unique_owner_ranges(
14361    owners: &mut Vec<CppSentinelRecoveredOwner>,
14362    additional: Vec<CppSentinelRecoveredOwner>,
14363) {
14364    for owner in additional {
14365        if !owners.iter().any(|existing| {
14366            existing.range == owner.range && existing.scope_components == owner.scope_components
14367        }) {
14368            owners.push(owner);
14369        }
14370    }
14371}
14372
14373fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
14374    if node.kind() != "ERROR" || node.named_child_count() != 1 {
14375        return false;
14376    }
14377    let Some(end_name) = node.named_child(0) else {
14378        return false;
14379    };
14380    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
14381        return false;
14382    }
14383    let mut cursor = node.walk();
14384    node.children(&mut cursor)
14385        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
14386}
14387
14388/// Collect owner definitions that the malformed sentinel left as later
14389/// declaration-list siblings. Parser-visible namespace siblings are a hard
14390/// boundary: their declarations must keep their own lexical namespace.
14391fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14392    parent: Node<'_>,
14393    sentinel_node: Node<'_>,
14394    namespace_components: &[String],
14395    source: &str,
14396) -> Vec<CppSentinelRecoveredOwner> {
14397    let mut owners = Vec::new();
14398    let mut after_sentinel = false;
14399    let mut cursor = parent.walk();
14400    for child in parent.named_children(&mut cursor) {
14401        if !after_sentinel {
14402            if same_node(child, sentinel_node) {
14403                after_sentinel = true;
14404            }
14405            continue;
14406        }
14407        walk_named_tree_preorder(child, true, |node| {
14408            if node.kind() == "namespace_definition" {
14409                return WalkControl::SkipChildren;
14410            }
14411            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14412        });
14413    }
14414    owners
14415}
14416
14417/// Collect owner definitions after a malformed namespace, stopping only at
14418/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
14419/// enclosing container is not trusted to belong to the recovered namespace.
14420fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14421    parent: Node<'_>,
14422    sentinel_node: Node<'_>,
14423    namespace_components: &[String],
14424    source: &str,
14425) -> Option<Vec<CppSentinelRecoveredOwner>> {
14426    let mut owners = Vec::new();
14427    let mut after_namespace = false;
14428    let mut cursor = parent.walk();
14429    for child in parent.named_children(&mut cursor) {
14430        if !after_namespace {
14431            if same_node(child, sentinel_node) {
14432                after_namespace = true;
14433            }
14434            continue;
14435        }
14436        if cpp_sentinel_namespace_end(child, source) {
14437            return Some(owners);
14438        }
14439        walk_named_tree_preorder(child, true, |node| {
14440            if node.kind() == "namespace_definition" {
14441                return WalkControl::SkipChildren;
14442            }
14443            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14444        });
14445    }
14446    None
14447}
14448
14449fn cpp_sentinel_recovered_sibling_owner_ranges(
14450    sentinel_node: Node<'_>,
14451    namespace_components: &[String],
14452    source: &str,
14453) -> Vec<CppSentinelRecoveredOwner> {
14454    let Some(declaration_list) = sentinel_node
14455        .parent()
14456        .filter(|parent| parent.kind() == "declaration_list")
14457    else {
14458        return Vec::new();
14459    };
14460    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14461        declaration_list,
14462        sentinel_node,
14463        namespace_components,
14464        source,
14465    );
14466
14467    let Some(namespace) = declaration_list
14468        .parent()
14469        .filter(|parent| parent.kind() == "namespace_definition")
14470    else {
14471        return owners;
14472    };
14473    let Some(outer_parent) = namespace.parent() else {
14474        return owners;
14475    };
14476    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14477        outer_parent,
14478        namespace,
14479        namespace_components,
14480        source,
14481    ) {
14482        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
14483    }
14484    owners
14485}
14486
14487fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
14488    let mut current = function_declarator.child_by_field_name("declarator")?;
14489    loop {
14490        if let Some(name) = macro_decorated_unqualified_name(current) {
14491            current = name;
14492            continue;
14493        }
14494        if matches!(
14495            current.kind(),
14496            "qualified_identifier"
14497                | "scoped_identifier"
14498                | "scoped_type_identifier"
14499                | "identifier"
14500                | "field_identifier"
14501                | "operator_name"
14502                | "destructor_name"
14503                | "literal_operator_name"
14504        ) {
14505            return Some(current);
14506        }
14507        current = current
14508            .child_by_field_name("declarator")
14509            .or_else(|| current.child_by_field_name("name"))
14510            .or_else(|| last_named_child(current))?;
14511    }
14512}
14513
14514/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
14515/// `::` between the halves. The scope is an attribute-like macro token, not
14516/// a namespace; `Name` is the declared name.
14517///
14518/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
14519/// tree-sitter joins the attribute macro and the constructor name into one
14520/// `qualified_identifier` and marks the separator it had to invent as MISSING.
14521/// That flag is the grammar's own record that no separator is in the source,
14522/// so read it instead of looking for `::` in the node text. A genuine
14523/// `Outer::Inner` keeps a present separator and is declined here, and so is
14524/// the explicit-global `::name`, whose `::` is present and whose scope is
14525/// absent.
14526fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
14527    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
14528        return None;
14529    }
14530    let mut cursor = node.walk();
14531    if node
14532        .children(&mut cursor)
14533        .any(|child| child.kind() == "::" && !child.is_missing())
14534    {
14535        return None;
14536    }
14537    node.child_by_field_name("name")
14538}
14539
14540fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
14541    if let Some(name) = macro_decorated_unqualified_name(node) {
14542        return cpp_name_components(name, source);
14543    }
14544    match node.kind() {
14545        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14546            let mut components = match node.child_by_field_name("scope") {
14547                Some(scope) => cpp_name_components(scope, source)?,
14548                None => Vec::new(),
14549            };
14550            let name = node.child_by_field_name("name")?;
14551            components.push(canonical_cpp_qualified_component(name, source)?);
14552            Some(components)
14553        }
14554        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
14555    }
14556}
14557
14558fn cpp_sentinel_fragment_boundary<'tree>(
14559    function: Node<'tree>,
14560    class_node: Node<'tree>,
14561    class_body: Node<'tree>,
14562    source: &str,
14563) -> Option<(Node<'tree>, Node<'tree>)> {
14564    let declaration_list = function.parent()?;
14565    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
14566        return None;
14567    }
14568    let namespace = declaration_list.parent()?;
14569    if namespace.kind() != "namespace_definition"
14570        || namespace.child_by_field_name("body") != Some(declaration_list)
14571    {
14572        return None;
14573    }
14574    let mut cursor = declaration_list.walk();
14575    let closes = declaration_list
14576        .children(&mut cursor)
14577        .filter(|child| {
14578            !child.is_named()
14579                && child.kind() == "}"
14580                && child.start_byte() >= function.end_byte()
14581                && child.start_byte() > class_node.end_byte()
14582                && child.start_byte() > class_body.start_byte()
14583        })
14584        .collect::<Vec<_>>();
14585    let [close] = closes.as_slice() else {
14586        return None;
14587    };
14588    let semicolon = namespace.next_named_sibling()?;
14589    if !cpp_is_stray_semicolon(semicolon, source)
14590        || close.end_byte() != namespace.end_byte()
14591        || semicolon.start_byte() < namespace.end_byte()
14592    {
14593        return None;
14594    }
14595    Some((*close, semicolon))
14596}
14597
14598/// Detect the bogus declaration/function tree that tree-sitter recovers for a
14599/// region prefixed by an object-like macro sentinel the parser cannot see
14600/// (issue #941), and return the byte range `[start, end)` of the swallowed
14601/// declaration interior to reparse.
14602///
14603/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
14604/// `function_definition` whose first non-comment named child is the sentinel
14605/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
14606/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
14607/// the real items.
14608/// `start` is the end of the sentinel identifier -- everything after it is the
14609/// genuine source. `end` is the node's end, extended across any trailing empty
14610/// `;` statement the mis-parse displaced past the node (the class/struct closing
14611/// semicolon), so the reparse sees a complete, brace-balanced item.
14612///
14613/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
14614/// node (`has_error`). Unknown annotation/export macros can make a real callable
14615/// error-recovered even though tree-sitter still preserves its declarator, so a
14616/// preserved callable is admitted only when a displaced class keyword precedes
14617/// that declarator. The clean-reparse-to-items gate in
14618/// `cpp_reparsed_items_are_indexable` is the final arbiter.
14619/// Return the reparse start and, when present, the structurally recovered class
14620/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
14621/// separately from the reparse start because an opaque template-declaration
14622/// macro may precede it.
14623fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
14624    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
14625    {
14626        return None;
14627    }
14628    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
14629    // retain a valid function declarator despite the unknown export macro making
14630    // the outer node erroneous. Remember that declarator for the ordering gate
14631    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
14632    // class keyword precedes a spurious callable assembled from a later member.
14633    let mut declarator_cursor = node.walk();
14634    let preserved_callable = node
14635        .children_by_field_name("declarator", &mut declarator_cursor)
14636        .find_map(extract_function_declarator);
14637    // Leading documentation comments are attached to the malformed
14638    // `function_definition` as named children.  They are not part of the
14639    // sentinel prefix, so select the first non-comment child structurally
14640    // rather than requiring the sentinel to be child zero.  This is the shape
14641    // emitted for nlohmann/json's `basic_json`: its class documentation comment
14642    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
14643    // envelope otherwise ends at the first nested union.
14644    let mut cursor = node.walk();
14645    let first = node
14646        .named_children(&mut cursor)
14647        .find(|child| child.kind() != "comment")?;
14648    if first.kind() != "type_identifier" {
14649        return None;
14650    }
14651    let sentinel = normalize_cpp_whitespace(node_text(first, source));
14652    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
14653        return None;
14654    }
14655    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
14656    // makes the trailing sentinel of one region and the leading sentinel of the
14657    // next both land as bare macro-token identifiers ahead of the real content.
14658    // Advance past every leading macro-token identifier so the reparse begins at
14659    // genuine source rather than another sentinel that would re-form the bogus
14660    // shape and fail the reparse gate.
14661    let mut start = first.end_byte();
14662    let mut after_first = false;
14663    let mut cursor = node.walk();
14664    for child in node.named_children(&mut cursor) {
14665        if !after_first {
14666            if same_node(child, first) {
14667                after_first = true;
14668            }
14669            continue;
14670        }
14671        if matches!(child.kind(), "identifier" | "type_identifier")
14672            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
14673        {
14674            start = child.end_byte();
14675        } else {
14676            break;
14677        }
14678    }
14679    // An additional opaque template-declaration macro before a class can be
14680    // folded into the bogus function's qualified declarator.  In that shape
14681    // the macro is not a direct sibling we can skip above; tree-sitter exposes
14682    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
14683    // Reparse from that keyword (or a real preceding `template` keyword) so the
14684    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
14685    // a class nested in a genuine sentinel-wrapped namespace lies after the
14686    // body opening and must not change the established region start.
14687    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
14688    let mut class_start = None;
14689    let mut template_start = None;
14690    let mut stack = vec![node];
14691    while let Some(current) = stack.pop() {
14692        if current.start_byte() >= prefix_end {
14693            continue;
14694        }
14695        if matches!(
14696            current.kind(),
14697            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
14698        ) {
14699            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
14700                "class" | "struct" | "union" | "enum" => {
14701                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
14702                        seen.min(current.start_byte())
14703                    }));
14704                }
14705                "template" => {
14706                    template_start =
14707                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
14708                            seen.min(current.start_byte())
14709                        }));
14710                }
14711                _ => {}
14712            }
14713        }
14714        let mut cursor = current.walk();
14715        stack.extend(current.children(&mut cursor));
14716    }
14717    if preserved_callable.is_some_and(|callable| {
14718        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
14719    }) {
14720        return None;
14721    }
14722    if let Some(class_start) = class_start {
14723        start = template_start
14724            .filter(|template_start| *template_start < class_start)
14725            .unwrap_or(class_start);
14726    }
14727    Some((start, class_start))
14728}
14729
14730/// Locate a sentinel-prefixed class whose malformed declaration was split across
14731/// root-level siblings. The true class close is represented structurally as a
14732/// lone `}` error followed by the class's displaced `;`; nested method/body
14733/// errors are not direct siblings of the sentinel node and therefore cannot
14734/// satisfy this pair.
14735fn cpp_sentinel_macro_class_region<'tree>(
14736    node: Node<'tree>,
14737    source: &str,
14738) -> Option<(usize, usize, usize, usize, usize, usize)> {
14739    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
14740        return None;
14741    };
14742    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
14743        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
14744        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
14745    if class_start >= body_open_start {
14746        return None;
14747    }
14748    let sibling_close = {
14749        let mut sibling = node.next_named_sibling();
14750        let mut found = None;
14751        while let Some(current) = sibling {
14752            let next = current.next_named_sibling();
14753            if cpp_is_stray_close_brace(current, source)
14754                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
14755            {
14756                let semicolon = next.expect("checked above");
14757                found = Some((
14758                    current.start_byte(),
14759                    semicolon.end_byte(),
14760                    semicolon.end_position().row + 1,
14761                ));
14762                break;
14763            }
14764            sibling = next;
14765        }
14766        found
14767    };
14768    // A stray `};` sibling is this class's close only when the bounded reparse
14769    // agrees the first body-bearing class ENDS there. When the malformed
14770    // envelope swallowed the class's true close, the scan can promote a much
14771    // later scope's close instead -- in protobuf-generated headers
14772    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
14773    // `struct TableStruct_*` paired with the first message class's `};`, making
14774    // the recovered "class body" span whole `namespace {}` blocks and minting
14775    // namespace-scope classes as nested members of the recovered class, which
14776    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
14777    // (#2275). On disagreement, fall through to the suffix-reparse boundary
14778    // below, which derives the close from the class node's own balanced body
14779    // range.
14780    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
14781        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14782            return false;
14783        };
14784        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14785        // A region reparse is its own tree and needs its own parent index.
14786        let reparsed_ancestry = ParentIndex::new(tree.root_node());
14787        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
14788            tree.root_node(),
14789            template_node,
14790            source,
14791            &reparsed_ancestry,
14792        ) else {
14793            return false;
14794        };
14795        let body = reparsed_class.body;
14796        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
14797    });
14798    let (class_close_start, class_close_end, class_close_line) =
14799        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
14800            (class_close_start, class_close_end, class_close_line)
14801        } else {
14802            // When the malformed envelope itself is an ERROR, tree-sitter can
14803            // leave the class's balanced close in the source while promoting
14804            // all following members to siblings. Reparse the complete suffix
14805            // and use the first body-bearing class node's own field range as
14806            // the partition boundary. This keeps balancing in tree-sitter and
14807            // preserves the source's original byte offsets.
14808            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
14809            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14810            // A region reparse is its own tree and needs its own parent index.
14811            let reparsed_ancestry = ParentIndex::new(tree.root_node());
14812            let reparsed_class = cpp_sentinel_reparsed_class(
14813                tree.root_node(),
14814                template_node,
14815                source,
14816                &reparsed_ancestry,
14817            )?;
14818            let body = reparsed_class.body;
14819            let class_close_end = body.end_byte();
14820            let class_close_start = class_close_end.checked_sub(1)?;
14821            let class_close_line = body.end_position().row + 1;
14822            (class_close_start, class_close_end, class_close_line)
14823        };
14824    if class_close_start <= class_start {
14825        return None;
14826    }
14827
14828    // Reparse only far enough to expose the class body opening. This is a
14829    // structured check that the candidate really begins with a body-bearing
14830    // class-like item; the original malformed tree cannot provide that node.
14831    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
14832    let class_root = tree.root_node();
14833    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
14834    // A region reparse is its own tree and needs its own parent index.
14835    let reparsed_ancestry = ParentIndex::new(class_root);
14836    let reparsed_class =
14837        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
14838    let body = reparsed_class.body;
14839    // The class body opening must agree with the malformed wrapper's structured
14840    // body field. This rejects an inner nested class while permitting later
14841    // members to remain fragmented as root-level siblings in the bounded parse.
14842    if body.start_byte() != body_open_start {
14843        return None;
14844    }
14845    let body_start = body.start_byte().checked_add(1)?;
14846    (body_start < class_close_start).then_some((
14847        reparse_start,
14848        class_start,
14849        body_start,
14850        class_close_start,
14851        class_close_end,
14852        class_close_line,
14853    ))
14854}
14855
14856/// Find the `{` token immediately following the class/struct/union/enum token
14857/// at `class_start` in the malformed tree. The token is anonymous in the C++
14858/// grammar, so this deliberately walks all children (not only named children)
14859/// and relies on sibling structure rather than source-text searching.
14860fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
14861    let mut stack = vec![node];
14862    while let Some(current) = stack.pop() {
14863        if current.start_byte() == class_start
14864            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
14865        {
14866            let mut sibling = current.next_sibling();
14867            while let Some(candidate) = sibling {
14868                if candidate.kind() == "{" {
14869                    return Some(candidate.start_byte());
14870                }
14871                sibling = candidate.next_sibling();
14872            }
14873        }
14874        let mut cursor = current.walk();
14875        stack.extend(current.children(&mut cursor));
14876    }
14877    None
14878}
14879
14880/// The class body that tree-sitter displaced out of a sentinel-prefixed
14881/// declaration and left as the malformed node's next sibling.
14882///
14883/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
14884/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
14885/// the last child of that `ERROR` and its `{` opens a sibling
14886/// `compound_statement` instead. The body is still the malformed tree's own
14887/// structured token, which is what the caller's `body.start_byte() !=
14888/// body_open_start` agreement check needs; it just is not reachable by walking
14889/// forward from the class token inside the node.
14890fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
14891    node.next_named_sibling()
14892        .filter(|sibling| sibling.kind() == "compound_statement")
14893}
14894
14895fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
14896    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
14897    let mut end = if class_start.is_some() {
14898        cpp_macro_prefixed_class_end(source, start)?
14899    } else {
14900        node.end_byte()
14901    };
14902    if class_start.is_none()
14903        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
14904    {
14905        end = end.max(namespace_end);
14906    }
14907    let mut sibling = node.next_named_sibling();
14908    while let Some(current) = sibling {
14909        if !cpp_is_stray_semicolon(current, source) {
14910            break;
14911        }
14912        end = current.end_byte();
14913        sibling = current.next_named_sibling();
14914    }
14915    (start < end).then_some((start, end))
14916}
14917
14918/// Extend a sentinel reparse through a following namespace that tree-sitter
14919/// flattened into the sentinel node's sibling list.
14920///
14921/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
14922/// unknown macro becomes a false function return type and consumes the first
14923/// namespace body. A second `namespace detail` then loses its enclosing node:
14924/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
14925/// attaches its declarations to the surrounding error tree. Reparse from that
14926/// structured keyword so tree-sitter, rather than a source-text brace scan,
14927/// supplies the complete namespace boundary.
14928fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
14929    let mut sibling = node.next_sibling();
14930    let keyword = loop {
14931        let candidate = sibling?;
14932        sibling = candidate.next_sibling();
14933        if candidate.kind() != "comment" {
14934            break candidate;
14935        }
14936    };
14937    if keyword.kind() != "namespace" {
14938        return None;
14939    }
14940    let name = loop {
14941        let candidate = sibling?;
14942        sibling = candidate.next_sibling();
14943        if candidate.kind() != "comment" {
14944            break candidate;
14945        }
14946    };
14947    if cpp_namespace_name_components(name, source).is_empty() {
14948        return None;
14949    }
14950    let open = loop {
14951        let candidate = sibling?;
14952        sibling = candidate.next_sibling();
14953        if candidate.kind() != "comment" {
14954            break candidate;
14955        }
14956    };
14957    if open.kind() != "{" {
14958        return None;
14959    }
14960
14961    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
14962    let root = tree.root_node();
14963    let mut cursor = root.walk();
14964    let namespace = root
14965        .named_children(&mut cursor)
14966        .find(|candidate| candidate.kind() != "comment")?;
14967    (namespace.kind() == "namespace_definition"
14968        && namespace.start_byte() == keyword.start_byte()
14969        && namespace.child_by_field_name("body").is_some())
14970    .then_some(namespace.end_byte())
14971}
14972
14973/// Parse the source suffix beginning at a structurally recovered class/template
14974/// keyword and return the end of its first body-bearing class item.  The parser,
14975/// rather than a brace scanner, owns nested-body balancing.  This is needed when
14976/// the original error tree truncates the class and scatters later members as
14977/// top-level siblings.
14978fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
14979    let tree = cpp_reparse_region_items(source, start, source.len())?;
14980    let root = tree.root_node();
14981    let mut cursor = root.walk();
14982    for item in root.named_children(&mut cursor) {
14983        if item.end_byte() <= start || item.kind() == "comment" {
14984            continue;
14985        }
14986        let mut stack = vec![item];
14987        while let Some(current) = stack.pop() {
14988            if matches!(
14989                current.kind(),
14990                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
14991            ) && cpp_body_node(current).is_some()
14992            {
14993                return Some(current.end_byte());
14994            }
14995            let mut cursor = current.walk();
14996            stack.extend(current.named_children(&mut cursor));
14997        }
14998        // The recovered prefix is required to begin with the class item.  If
14999        // the first real item is something else, fail closed rather than skip
15000        // arbitrary source looking for a later class.
15001        return None;
15002    }
15003    None
15004}
15005
15006/// An empty `;` statement: the displaced closing semicolon of a struct/class that
15007/// the sentinel mis-parse split off past the bogus function node.
15008fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
15009    node.kind() == "expression_statement"
15010        && node.named_child_count() == 0
15011        && node_text(node, source).trim() == ";"
15012}
15013
15014/// Recover the member that follows CPython's object header macro when
15015/// tree-sitter folds both declarations into one malformed field.
15016///
15017/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
15018/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
15019/// sole child is the real field declarator. The tail may wrap that identifier
15020/// in pointer declarators, as in `PyObject_HEAD ImagingObject *image;`; other
15021/// malformed wrappers do not provide the same evidence that this is the
15022/// CPython macro boundary.
15023#[derive(Clone, Copy)]
15024pub(crate) struct RecoveredPyObjectHeadField<'tree> {
15025    pub(crate) type_node: Node<'tree>,
15026    pub(crate) name: Node<'tree>,
15027    pub(crate) declarator: Node<'tree>,
15028}
15029
15030impl RecoveredPyObjectHeadField<'_> {
15031    pub(crate) fn pointer_depth(self) -> i32 {
15032        let mut depth = 0;
15033        let mut current = self.declarator;
15034        while current != self.name {
15035            debug_assert_eq!(current.kind(), "pointer_declarator");
15036            depth += 1;
15037            current = current
15038                .child_by_field_name("declarator")
15039                .expect("recovered PyObject field pointer has an inner declarator");
15040        }
15041        depth
15042    }
15043}
15044
15045pub(crate) fn recovered_pyobject_head_field<'tree>(
15046    node: Node<'tree>,
15047    source: &str,
15048) -> Option<RecoveredPyObjectHeadField<'tree>> {
15049    if node.kind() != "field_declaration" {
15050        return None;
15051    }
15052    let type_node = node.child_by_field_name("type")?;
15053    if type_node.kind() != "type_identifier"
15054        || node_text(type_node, source).trim() != "PyObject_HEAD"
15055    {
15056        return None;
15057    }
15058    let pseudo_declarator = node.child_by_field_name("declarator")?;
15059    let mut cursor = node.walk();
15060    let errors = node
15061        .named_children(&mut cursor)
15062        .filter(|child| child.kind() == "ERROR")
15063        .collect::<Vec<_>>();
15064    let [error] = errors.as_slice() else {
15065        return None;
15066    };
15067    if error.named_child_count() != 1 {
15068        return None;
15069    }
15070    let error_child = error.named_child(0)?;
15071    if pseudo_declarator.kind() == "field_identifier"
15072        && error.start_byte() >= pseudo_declarator.end_byte()
15073        && error_child.kind() == "identifier"
15074    {
15075        return Some(RecoveredPyObjectHeadField {
15076            type_node: pseudo_declarator,
15077            name: error_child,
15078            declarator: error_child,
15079        });
15080    }
15081    if pseudo_declarator.kind() != "pointer_declarator"
15082        || error.end_byte() > pseudo_declarator.start_byte()
15083        || error_child.kind() != "identifier"
15084    {
15085        return None;
15086    }
15087    let mut name = pseudo_declarator;
15088    while name.kind() == "pointer_declarator" {
15089        name = name.child_by_field_name("declarator")?;
15090    }
15091    (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
15092        type_node: error_child,
15093        name,
15094        declarator: pseudo_declarator,
15095    })
15096}
15097
15098/// Recover the real field name when a leading object-like annotation macro
15099/// displaces a qualified type into tree-sitter's bit-field recovery shape.
15100///
15101/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
15102/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
15103/// `bitfield_clause` containing an error plus an assignment.  The assignment's
15104/// left field is the only structured declaration name in that malformed tail.
15105/// A real bit-field is excluded by the all-caps macro type and required error.
15106fn recovered_macro_qualified_field_declarators<'tree>(
15107    node: Node<'tree>,
15108    source: &str,
15109) -> Option<Vec<Node<'tree>>> {
15110    if node.kind() != "field_declaration" {
15111        return None;
15112    }
15113    let macro_type = node.child_by_field_name("type")?;
15114    if macro_type.kind() != "type_identifier"
15115        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15116    {
15117        return None;
15118    }
15119    let pseudo_declarator = node.child_by_field_name("declarator")?;
15120    if pseudo_declarator.kind() != "field_identifier" {
15121        return None;
15122    }
15123    let mut cursor = node.walk();
15124    let clause = node
15125        .named_children(&mut cursor)
15126        .find(|child| child.kind() == "bitfield_clause")?;
15127    if !(0..clause.named_child_count()).any(|index| {
15128        clause
15129            .named_child(index)
15130            .is_some_and(|child| child.kind() == "ERROR")
15131    }) {
15132        return None;
15133    }
15134    let mut recovered = Vec::new();
15135    let mut stack = vec![clause];
15136    while let Some(current) = stack.pop() {
15137        if current.kind() == "assignment_expression"
15138            && let Some(left) = current.child_by_field_name("left")
15139            && extract_variable_name(left, source).is_some()
15140        {
15141            recovered.push(left);
15142            break;
15143        }
15144        let mut cursor = current.walk();
15145        stack.extend(current.named_children(&mut cursor));
15146    }
15147    if recovered.is_empty() {
15148        return None;
15149    }
15150    let mut cursor = node.walk();
15151    recovered.extend(
15152        node.children_by_field_name("declarator", &mut cursor)
15153            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
15154    );
15155    Some(recovered)
15156}
15157
15158/// Recover a macro-qualified constructor that tree-sitter represents as one
15159/// field declaration. The constructor call remains inside the direct recovery
15160/// error, while each member initializer becomes a false function declarator.
15161/// The class owner proves the constructor name and lets the caller ignore those
15162/// initializer declarators.
15163fn recovered_macro_qualified_constructor_call<'tree>(
15164    node: Node<'tree>,
15165    class_name: &str,
15166    source: &str,
15167) -> Option<Node<'tree>> {
15168    if node.kind() != "field_declaration" {
15169        return None;
15170    }
15171    let macro_type = node.child_by_field_name("type")?;
15172    if macro_type.kind() != "type_identifier"
15173        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15174    {
15175        return None;
15176    }
15177    let mut cursor = node.walk();
15178    let bitfield = node
15179        .named_children(&mut cursor)
15180        .find(|child| child.kind() == "bitfield_clause")?;
15181    let error = bitfield
15182        .named_child(0)
15183        .filter(|child| child.kind() == "ERROR")?;
15184    let mut stack = vec![error];
15185    while let Some(current) = stack.pop() {
15186        if current.kind() == "call_expression"
15187            && current
15188                .child_by_field_name("function")
15189                .is_some_and(|function| node_text(function, source) == class_name)
15190            && current
15191                .child_by_field_name("arguments")
15192                .is_some_and(|arguments| arguments.kind() == "argument_list")
15193        {
15194            return Some(current);
15195        }
15196        let mut cursor = current.walk();
15197        stack.extend(current.named_children(&mut cursor));
15198    }
15199    None
15200}
15201
15202/// Recover a macro-qualified member function declaration that tree-sitter
15203/// represents as a pseudo-field. An object-like export macro before a qualified
15204/// return type can displace the namespace and type into an ERROR/bitfield
15205/// recovery, leaving the callable as a structured `call_expression`.
15206///
15207/// The caller must route this shape before ordinary declarator classification;
15208/// otherwise the displaced namespace identifier is published as a field.
15209fn recovered_macro_qualified_function_call<'tree>(
15210    node: Node<'tree>,
15211    source: &str,
15212) -> Option<Node<'tree>> {
15213    if node.kind() != "field_declaration" {
15214        return None;
15215    }
15216    let macro_type = node.child_by_field_name("type")?;
15217    if macro_type.kind() != "type_identifier"
15218        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15219    {
15220        return None;
15221    }
15222    let declarator = node.child_by_field_name("declarator")?;
15223    if declarator.kind() != "field_identifier" {
15224        return None;
15225    }
15226    let mut cursor = node.walk();
15227    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15228    if !named.iter().any(|child| {
15229        child.kind() == "storage_class_specifier"
15230            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
15231    }) {
15232        return None;
15233    }
15234    let bitfield = named
15235        .iter()
15236        .find(|child| child.kind() == "bitfield_clause")?;
15237    let mut bitfield_cursor = bitfield.walk();
15238    let payload = bitfield
15239        .named_children(&mut bitfield_cursor)
15240        .collect::<Vec<_>>();
15241    let [displaced_error, call] = payload.as_slice() else {
15242        return None;
15243    };
15244    if displaced_error.kind() != "ERROR"
15245        || displaced_error.named_child_count() != 1
15246        || displaced_error
15247            .named_child(0)
15248            .is_none_or(|child| child.kind() != "identifier")
15249        || call.kind() != "call_expression"
15250        || call
15251            .child_by_field_name("function")
15252            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
15253        || call
15254            .child_by_field_name("arguments")
15255            .is_none_or(|arguments| arguments.kind() != "argument_list")
15256    {
15257        return None;
15258    }
15259    Some(*call)
15260}
15261
15262fn recovered_macro_qualified_function_parameters(
15263    arguments: Node<'_>,
15264    source: &str,
15265) -> Option<(String, Vec<String>)> {
15266    if arguments.kind() != "argument_list" {
15267        return None;
15268    }
15269    let mut cursor = arguments.walk();
15270    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
15271    if named.is_empty() {
15272        return Some(("()".to_string(), Vec::new()));
15273    }
15274    let mut types = Vec::new();
15275    let mut labels = Vec::new();
15276    let mut index = 0;
15277    while index < named.len() {
15278        let parameter_type = named[index];
15279        let parameter_name = named.get(index + 1).copied()?;
15280        if !matches!(
15281            parameter_type.kind(),
15282            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
15283        ) || parameter_name.kind() != "ERROR"
15284            || parameter_name.named_child_count() != 1
15285            || parameter_name
15286                .named_child(0)
15287                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
15288        {
15289            return None;
15290        }
15291        let parameter_name = parameter_name.named_child(0)?;
15292        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
15293        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
15294        index += 2;
15295    }
15296    Some((format!("({})", types.join(", ")), labels))
15297}
15298
15299/// Recognize the phantom field tree-sitter emits for a macro-qualified
15300/// function return type.  For example,
15301/// `static API result_type ThresholdForSmallA() { ... }` can become a
15302/// `field_declaration` (`API` as the type and `result_type` as a field name)
15303/// followed by a clean `function_definition` for `ThresholdForSmallA`.
15304///
15305/// Keep this predicate entirely tied to the CST envelope: the type must be an
15306/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
15307/// the declaration must carry a missing semicolon rather than a real one, and
15308/// the immediate named sibling must expose a function declarator.  A real
15309/// macro-decorated field with an explicit semicolon therefore remains a field.
15310pub fn recovered_macro_return_type_node<'tree>(
15311    node: Node<'tree>,
15312    source: &str,
15313) -> Option<Node<'tree>> {
15314    if node.kind() != "field_declaration" {
15315        return None;
15316    }
15317    let macro_type = node.child_by_field_name("type")?;
15318    if macro_type.kind() != "type_identifier"
15319        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15320    {
15321        return None;
15322    }
15323    let declarator = node.child_by_field_name("declarator")?;
15324    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
15325        return None;
15326    }
15327    let mut has_missing_semicolon = false;
15328    let mut has_real_semicolon = false;
15329    for child in children_iter(node) {
15330        if child.kind() != ";" {
15331            continue;
15332        }
15333        if child.is_missing() {
15334            has_missing_semicolon = true;
15335        } else {
15336            has_real_semicolon = true;
15337        }
15338    }
15339    if !has_missing_semicolon || has_real_semicolon {
15340        return None;
15341    }
15342    let mut next = node.next_named_sibling();
15343    while next.is_some_and(|sibling| sibling.kind() == "comment") {
15344        next = next.and_then(|sibling| sibling.next_named_sibling());
15345    }
15346    let next = next?;
15347    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
15348        return None;
15349    }
15350    let function_declarator = next.child_by_field_name("declarator")?;
15351    extract_function_declarator(function_declarator).map(|_| declarator)
15352}
15353
15354/// Whether `name` is a type parameter of a template declaration that lexically
15355/// encloses `node`. The malformed macro-return field uses the parameter name as
15356/// its pseudo-declarator; preserving that field is necessary to publish a
15357/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
15358/// ancestors instead of interpreting source text so nested templates and
15359/// parser-recovered regions retain their real lexical scopes.
15360pub(crate) fn cpp_active_template_type_parameter<'tree>(
15361    node: Node<'tree>,
15362    name: &str,
15363    source: &str,
15364    ancestry: &ParentIndex<'tree>,
15365) -> bool {
15366    let mut ancestor = ancestry.parent(node);
15367    while let Some(current) = ancestor {
15368        if current.kind() == "template_declaration"
15369            && let Some(parameters) = current.child_by_field_name("parameters")
15370        {
15371            let mut cursor = parameters.walk();
15372            if parameters.named_children(&mut cursor).any(|parameter| {
15373                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
15374                    && cpp_template_parameter_name(parameter, source)
15375                        .is_some_and(|parameter_name| parameter_name == name)
15376            }) {
15377                return true;
15378            }
15379        }
15380        ancestor = ancestry.parent(current);
15381    }
15382    false
15383}
15384
15385/// Reparse the region `[start, end)` of `source` as C++, confined to the region
15386/// via included ranges so every reparsed node keeps its original byte offset and
15387/// line number. The existing visitors read node text from the original source,
15388/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
15389/// `parse_rust_region_tree` technique.
15390fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
15391    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
15392}
15393
15394fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
15395    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
15396        return None;
15397    }
15398    let semicolon = node.next_sibling()?;
15399    if semicolon.kind() != ";" || semicolon.is_missing() {
15400        return None;
15401    }
15402    let row = node.start_position().row;
15403    let mut start = node.start_byte();
15404    let mut sibling = node.prev_sibling();
15405    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
15406        if previous.kind() == ";" {
15407            break;
15408        }
15409        start = previous.start_byte();
15410        sibling = previous.prev_sibling();
15411    }
15412    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
15413}
15414
15415/// One `RET name _(( args ));`-style K&R prototype-macro invocation
15416/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
15417/// byte spans a clean reparse needs, in original-source order.
15418struct PrototypeMacroCandidate {
15419    /// Where the return type and declared name begin.
15420    run_start: usize,
15421    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
15422    /// exclusive end of the first reparse span.
15423    identifier_start: usize,
15424    /// The inner `(`'s start byte: the start of the second reparse span.
15425    /// This paren is kept so the reparse sees exactly one parameter-list
15426    /// paren pair.
15427    inner_open_start: usize,
15428    /// Byte just past the inner `)`: the end of the second reparse span.
15429    inner_close_end: usize,
15430    /// Byte just past the outer `)`: the start of the third reparse span.
15431    outer_close_end: usize,
15432    /// Byte just past the terminating `;`: the end of the third reparse
15433    /// span, and of the whole candidate.
15434    semicolon_end: usize,
15435}
15436
15437impl PrototypeMacroCandidate {
15438    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
15439    /// one included-range tree: the return type and name, the parenthesized
15440    /// argument list, and the terminating `;`. The macro token and its outer
15441    /// wrapping parenthesis are omitted -- deleting them is exactly what the
15442    /// (absent) preprocessor macro expansion would do.
15443    fn ranges(&self) -> [(usize, usize); 3] {
15444        [
15445            (self.run_start, self.identifier_start),
15446            (self.inner_open_start, self.inner_close_end),
15447            (self.outer_close_end, self.semicolon_end),
15448        ]
15449    }
15450}
15451
15452/// A live terminating semicolon owned directly by `node`.
15453fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
15454    node.child(node.child_count().checked_sub(1)?)
15455        .filter(|child| child.kind() == ";" && !child.is_missing())
15456}
15457
15458/// The known pre-ANSI prototype macros whose expansion is exactly their one
15459/// argument. The malformed CST cannot prove that an arbitrary identifier has
15460/// that definition, so unknown spellings fail closed rather than turning an
15461/// ordinary broken declaration into a Function (issue #2932).
15462fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
15463    matches!(
15464        node.kind(),
15465        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
15466    ) && matches!(
15467        normalize_cpp_whitespace(node_text(node, source)).as_str(),
15468        "_" | "__P" | "OF" | "PROTO"
15469    )
15470}
15471
15472/// Read tree-sitter's recovery for `declared_name MACRO`: a
15473/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
15474/// declared name, and whose `name` is a known prototype macro.
15475fn cpp_prototype_macro_qualified_parts<'tree>(
15476    node: Node<'tree>,
15477    source: &str,
15478) -> Option<(Node<'tree>, Node<'tree>)> {
15479    if node.kind() != "qualified_identifier" {
15480        return None;
15481    }
15482    let declared_name = node
15483        .child_by_field_name("scope")
15484        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
15485    let macro_name = macro_decorated_unqualified_name(node)?;
15486    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
15487}
15488
15489/// The inner parenthesized node of tree-sitter's structured
15490/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
15491/// the macro invocation's outer parentheses while retaining the one pair an
15492/// ordinary function declarator needs. No delimiter search is involved: both
15493/// pairs and their ownership come from the CST.
15494fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
15495    if arguments.kind() != "argument_list"
15496        || arguments.named_child_count() != 1
15497        || arguments.child_count() != 3
15498        || arguments
15499            .child(0)
15500            .is_none_or(|open| open.kind() != "(" || open.is_missing())
15501        || arguments
15502            .child(2)
15503            .is_none_or(|close| close.kind() != ")" || close.is_missing())
15504    {
15505        return None;
15506    }
15507    let inner = arguments.named_child(0)?;
15508    let close_index = match inner.kind() {
15509        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
15510        // tree-sitter completes the C++ cast production with a zero-width
15511        // value after the live close parenthesis.
15512        "cast_expression" => inner.child_count().checked_sub(2)?,
15513        _ => return None,
15514    };
15515    (inner
15516        .child(0)
15517        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
15518        && inner
15519            .child(close_index)
15520            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
15521    .then_some(inner)
15522}
15523
15524fn cpp_prototype_macro_candidate_from_init_declaration(
15525    declaration: Node<'_>,
15526    source: &str,
15527) -> Option<PrototypeMacroCandidate> {
15528    let init = declaration
15529        .child_by_field_name("declarator")
15530        .filter(|declarator| declarator.kind() == "init_declarator")?;
15531    let malformed_declarator = init.child_by_field_name("declarator")?;
15532    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
15533        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
15534    } else {
15535        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
15536            return None;
15537        }
15538        let declared_name_error = init
15539            .prev_named_sibling()
15540            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
15541        let declared_name = declared_name_error
15542            .named_child(0)
15543            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
15544        (declared_name, malformed_declarator)
15545    };
15546    let arguments = init
15547        .child_by_field_name("value")
15548        .filter(|value| value.kind() == "argument_list")?;
15549    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15550    let semicolon = cpp_direct_semicolon(declaration)?;
15551    let return_type = declaration.child_by_field_name("type")?;
15552    if return_type.end_byte() > declared_name.start_byte()
15553        || declared_name.end_byte() > macro_name.start_byte()
15554        || macro_name.end_byte() > arguments.start_byte()
15555        || arguments.end_byte() > semicolon.start_byte()
15556    {
15557        return None;
15558    }
15559    Some(PrototypeMacroCandidate {
15560        run_start: declaration.start_byte(),
15561        identifier_start: macro_name.start_byte(),
15562        inner_open_start: inner.start_byte(),
15563        inner_close_end: inner.end_byte(),
15564        outer_close_end: arguments.end_byte(),
15565        semicolon_end: semicolon.end_byte(),
15566    })
15567}
15568
15569fn cpp_prototype_macro_candidate_from_qualified_declaration(
15570    declaration: Node<'_>,
15571    source: &str,
15572) -> Option<PrototypeMacroCandidate> {
15573    let qualified = declaration
15574        .child_by_field_name("declarator")
15575        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
15576    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15577    let open_error = qualified
15578        .next_named_sibling()
15579        .filter(|next| next.kind() == "ERROR")?;
15580    let close_error = last_named_child(declaration)
15581        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
15582    if open_error.child_count() < 3
15583        || open_error
15584            .child(0)
15585            .is_none_or(|open| open.kind() != "(" || open.is_missing())
15586        || open_error
15587            .child(1)
15588            .is_none_or(|open| open.kind() != "(" || open.is_missing())
15589        || close_error.child_count() != 2
15590        || close_error
15591            .child(0)
15592            .is_none_or(|close| close.kind() != ")" || close.is_missing())
15593        || close_error
15594            .child(1)
15595            .is_none_or(|close| close.kind() != ")" || close.is_missing())
15596    {
15597        return None;
15598    }
15599    let inner_open = open_error.child(1)?;
15600    let inner_close = close_error.child(0)?;
15601    let outer_close = close_error.child(1)?;
15602    let semicolon = cpp_direct_semicolon(declaration)?;
15603    let return_type = declaration.child_by_field_name("type")?;
15604    if return_type.end_byte() > qualified.start_byte()
15605        || macro_name.end_byte() > open_error.start_byte()
15606        || inner_open.start_byte() > inner_close.end_byte()
15607        || inner_close.end_byte() > outer_close.start_byte()
15608        || outer_close.end_byte() > semicolon.start_byte()
15609    {
15610        return None;
15611    }
15612    Some(PrototypeMacroCandidate {
15613        run_start: declaration.start_byte(),
15614        identifier_start: macro_name.start_byte(),
15615        inner_open_start: inner_open.start_byte(),
15616        inner_close_end: inner_close.end_byte(),
15617        outer_close_end: outer_close.end_byte(),
15618        semicolon_end: semicolon.end_byte(),
15619    })
15620}
15621
15622fn cpp_prototype_macro_candidate_from_pointer_expression(
15623    statement: Node<'_>,
15624    source: &str,
15625) -> Option<PrototypeMacroCandidate> {
15626    if statement.kind() != "expression_statement"
15627        || statement.named_child_count() != 1
15628        || !statement.has_error()
15629    {
15630        return None;
15631    }
15632    let expansion = statement
15633        .named_child(0)
15634        .filter(|child| child.kind() == "parameter_pack_expansion")?;
15635    let binary = expansion
15636        .child_by_field_name("pattern")
15637        .filter(|pattern| pattern.kind() == "binary_expression")?;
15638    if binary.child_count() != 3
15639        || binary
15640            .child(1)
15641            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
15642        || expansion
15643            .child(expansion.child_count().checked_sub(1)?)
15644            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
15645    {
15646        return None;
15647    }
15648    let return_type = binary.child_by_field_name("left")?;
15649    let call = binary
15650        .child_by_field_name("right")
15651        .filter(|right| right.kind() == "call_expression")?;
15652    let qualified = call.child_by_field_name("function")?;
15653    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15654    let arguments = call
15655        .child_by_field_name("arguments")
15656        .filter(|arguments| arguments.kind() == "argument_list")?;
15657    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15658    let semicolon = cpp_direct_semicolon(statement)?;
15659    if return_type.end_byte() > declared_name.start_byte()
15660        || macro_name.end_byte() > arguments.start_byte()
15661        || arguments.end_byte() > semicolon.start_byte()
15662    {
15663        return None;
15664    }
15665    Some(PrototypeMacroCandidate {
15666        run_start: statement.start_byte(),
15667        identifier_start: macro_name.start_byte(),
15668        inner_open_start: inner.start_byte(),
15669        inner_close_end: inner.end_byte(),
15670        outer_close_end: arguments.end_byte(),
15671        semicolon_end: semicolon.end_byte(),
15672    })
15673}
15674
15675/// Recover only CST shapes whose fields preserve the declaration name, macro
15676/// invocation, nested argument list, and terminator. Arbitrary flattened
15677/// `ERROR` wreckage stays unsupported rather than being interpreted through a
15678/// token or delimiter scan (issue #2932).
15679fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
15680    let candidate = match node.kind() {
15681        "declaration" if node.has_error() => {
15682            cpp_prototype_macro_candidate_from_init_declaration(node, source)
15683                .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
15684        }
15685        "expression_statement" => {
15686            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
15687        }
15688        _ => None,
15689    };
15690    candidate.into_iter().collect()
15691}
15692
15693fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
15694    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
15695        return false;
15696    }
15697    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
15698        return false;
15699    }
15700    let Some(declarator) = (if node.kind() == "function_definition" {
15701        node.child_by_field_name("declarator")
15702            .and_then(extract_function_declarator)
15703    } else {
15704        node.named_child(0)
15705            .filter(|child| child.kind() == "function_declarator")
15706    }) else {
15707        return false;
15708    };
15709    let Some(name) = cpp_function_declarator_name_node(declarator) else {
15710        return false;
15711    };
15712    declarator.start_byte() == node.start_byte()
15713        && name.kind() == "identifier"
15714        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
15715}
15716
15717/// Reparse a fragmented class-body interior while preserving its original byte
15718/// and line offsets, confined to the region by tree-sitter included ranges.
15719///
15720/// This used to materialize the region's whole file prefix as whitespace and
15721/// make the lexer walk it, the technique #1309 replaced on the other reparse
15722/// path: O(file) per fragmented-class recovery, on files that are already
15723/// error-recovered and already slow (#2788). Included ranges give the parser
15724/// the same view -- the region's bytes, at their original offsets and
15725/// line/column positions -- without materializing or lexing anything before it.
15726///
15727/// The equality of the two views is the claim, so a debug build parses both and
15728/// asserts the trees agree node for node.
15729fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15730    let region = cpp_reparse_region_items(source, start, end);
15731
15732    #[cfg(debug_assertions)]
15733    assert_eq!(
15734        region.as_ref().map(cpp_tree_shape),
15735        cpp_reparse_padded_class_body(source, start, end)
15736            .as_ref()
15737            .map(cpp_tree_shape),
15738        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
15739         prefix produces"
15740    );
15741
15742    region
15743}
15744
15745/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
15746/// replaces, kept as the oracle a debug build asserts every region reparse
15747/// against and as the release-mode parity tests' reference (#2788).
15748#[cfg(any(debug_assertions, test))]
15749fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15750    if start >= end {
15751        // The included-range parser refuses an empty region; the padded one
15752        // returned a tree holding nothing, which every caller read as "no
15753        // members here".
15754        return None;
15755    }
15756    let bytes = source.as_bytes();
15757    let prefix = bytes.get(..start)?;
15758    let interior = bytes.get(start..end)?;
15759    let mut padded = Vec::with_capacity(end);
15760    padded.extend(
15761        prefix
15762            .iter()
15763            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
15764    );
15765    padded.extend_from_slice(interior);
15766    let padded = String::from_utf8(padded).ok()?;
15767    let mut parser = Parser::new();
15768    parser
15769        .set_language(&tree_sitter_cpp::LANGUAGE.into())
15770        .ok()?;
15771    parser.parse(&padded, None)
15772}
15773
15774/// Every node of `tree` in preorder, by kind, span and position, which is what
15775/// two reparses of one region have to agree on for their callers to read the
15776/// same declarations out of either (#2788).
15777#[cfg(any(debug_assertions, test))]
15778fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
15779    let mut shape = Vec::new();
15780    let mut cursor = tree.root_node().walk();
15781    let mut stack = vec![tree.root_node()];
15782    while let Some(node) = stack.pop() {
15783        shape.push((
15784            node.kind(),
15785            node.start_byte(),
15786            node.end_byte(),
15787            node.start_position().row,
15788            node.start_position().column,
15789            node.is_named(),
15790            node.is_missing(),
15791        ));
15792        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
15793        stack.extend(children.into_iter().rev());
15794    }
15795    shape
15796}
15797
15798/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
15799/// reparsed interior is indexed only when every top-level named node is a
15800/// well-formed C++ item (or a comment) and at least one real item is present.
15801/// Expression/statement soup surfaces as a top-level `ERROR` or
15802/// `expression_statement`, neither of which is an item kind, so it is rejected.
15803///
15804/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
15805/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
15806/// swallowed by a preceding dangling sentinel) reparses to a real
15807/// `namespace_definition` whose body still holds a bogus `function_definition`,
15808/// so the subtree legitimately carries an error. Container items are admitted
15809/// even with an internal error; the inner bogus function is recovered recursively
15810/// when `visit_function_definition` walks it. Each recursion strips at least one
15811/// leading sentinel, so the region strictly shrinks and recovery terminates.
15812///
15813/// A top-level `function_definition` is the one place we stay strict: it is
15814/// admitted only when it is clean or is itself a sentinel candidate. A function
15815/// that has an error and is not a sentinel is a real callable with a broken body,
15816/// so we refuse the whole reparse and let the ordinary path handle it (preserving
15817/// its real return type rather than re-deriving an implicit one).
15818fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
15819    let mut cursor = root.walk();
15820    let mut saw_item = false;
15821    for child in root.named_children(&mut cursor) {
15822        match child.kind() {
15823            "comment" => {}
15824            "function_definition" => {
15825                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
15826                    return false;
15827                }
15828                saw_item = true;
15829            }
15830            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
15831            _ => return false,
15832        }
15833    }
15834    saw_item
15835}
15836
15837/// Robustness gate for a reparsed fragmented multiple-base export class body
15838/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
15839/// kinds a class body produces when reparsed at translation-unit scope: the
15840/// access-specifier label preceding the first member surfaces as a
15841/// `labeled_statement` wrapping that member, and members surface as
15842/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
15843/// Statement or expression soup surfaces as other top-level kinds and is rejected,
15844/// so only a genuinely member-shaped body is ever re-owned as members; anything
15845/// ambiguous falls back to indexing the class alone.
15846fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
15847    if node.kind() != "ERROR" {
15848        return false;
15849    }
15850    let mut stack = Vec::new();
15851    let mut saw_function_declarator = false;
15852    let mut cursor = node.walk();
15853    for child in node.named_children(&mut cursor) {
15854        stack.push(child);
15855    }
15856    while let Some(current) = stack.pop() {
15857        match current.kind() {
15858            // Tree-sitter may wrap adjacent copy-control declarations in a
15859            // nested ERROR. Keep descending only through ERROR wrappers; the
15860            // actual declaration payload must be a function_declarator.
15861            "ERROR" => {
15862                let mut cursor = current.walk();
15863                stack.extend(current.named_children(&mut cursor));
15864            }
15865            "function_declarator" => saw_function_declarator = true,
15866            _ => return false,
15867        }
15868    }
15869    saw_function_declarator
15870}
15871
15872fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
15873    if node.kind() != "ERROR" {
15874        return false;
15875    }
15876    let mut cursor = node.walk();
15877    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15878    let [explicit, constructor_error, destructor] = named.as_slice() else {
15879        return false;
15880    };
15881    let Some(constructor) = constructor_error.named_child(0) else {
15882        return false;
15883    };
15884    let Some(constructor_name) =
15885        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
15886    else {
15887        return false;
15888    };
15889    let Some(destructor_name) =
15890        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
15891    else {
15892        return false;
15893    };
15894    let Some(destroyed_type) = destructor_name.named_child(0) else {
15895        return false;
15896    };
15897    explicit.kind() == "explicit_function_specifier"
15898        && constructor_error.kind() == "ERROR"
15899        && constructor_error.named_child_count() == 1
15900        && constructor.kind() == "function_declarator"
15901        && constructor_name.kind() == "identifier"
15902        && destructor.kind() == "function_declarator"
15903        && destructor_name.kind() == "destructor_name"
15904        && destroyed_type.kind() == "identifier"
15905        && node_text(constructor_name, source) == node_text(destroyed_type, source)
15906}
15907
15908fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
15909    if node.kind() != "compound_statement" {
15910        return false;
15911    }
15912    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
15913        return false;
15914    };
15915    if prefix.kind() == "labeled_statement"
15916        && prefix.named_child(0).is_some_and(|label| {
15917            matches!(
15918                node_text(label, source).trim(),
15919                "public" | "private" | "protected"
15920            )
15921        })
15922    {
15923        return prefix.named_children(&mut prefix.walk()).any(|child| {
15924            child.kind() == "declaration"
15925                && child.has_error()
15926                && child
15927                    .named_children(&mut child.walk())
15928                    .any(cpp_reparsed_member_error_is_indexable)
15929        });
15930    }
15931    // A malformed constructor initializer can be split into a declaration
15932    // followed by its compound body when the class prefix already contains
15933    // realistic members. Keep this admission tied to that exact structured
15934    // declaration/error/body chain rather than accepting arbitrary blocks.
15935    prefix.kind() == "declaration"
15936        && prefix.has_error()
15937        && prefix
15938            .named_children(&mut prefix.walk())
15939            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
15940}
15941
15942fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
15943    if !cpp_reparsed_member_error_is_indexable(node) {
15944        return false;
15945    }
15946    let Some(preproc) = node.next_named_sibling() else {
15947        return false;
15948    };
15949    preproc.kind() == "preproc_if"
15950        && preproc.has_error()
15951        && preproc
15952            .named_children(&mut preproc.walk())
15953            .any(|child| child.kind() == "expression_statement" && child.has_error())
15954        && preproc
15955            .next_named_sibling()
15956            .is_some_and(|body| body.kind() == "compound_statement")
15957}
15958
15959/// Return a function body whose braces and ownership are explicit in the
15960/// reparsed class-member tree. An error below a real function envelope is
15961/// recoverable by the ordinary function visitor; a missing/deferred body is
15962/// not, because accepting it would let statement soup masquerade as a member.
15963fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
15964    if node.kind() != "function_definition" {
15965        return None;
15966    }
15967    let body = node.child_by_field_name("body")?;
15968    if body.kind() != "compound_statement" {
15969        return None;
15970    }
15971    let open = body.child(0)?;
15972    let close = body.child(body.child_count().checked_sub(1)?)?;
15973    if open.kind() != "{"
15974        || open.is_missing()
15975        || close.kind() != "}"
15976        || close.is_missing()
15977        || close.end_byte() != body.end_byte()
15978        || body.end_byte() != node.end_byte()
15979    {
15980        return None;
15981    }
15982    Some(body)
15983}
15984
15985fn cpp_reparsed_member_function_errors_are_in_body(
15986    node: Node<'_>,
15987    body: Node<'_>,
15988    source: &str,
15989) -> bool {
15990    let mut cursor = node.walk();
15991    node.children(&mut cursor).all(|child| {
15992        same_node(child, body)
15993            || cpp_reparsed_member_attribute_error(child, source)
15994            || cpp_reparsed_member_signature_identifier_errors(child)
15995            || (!child.has_error() && !child.is_error() && !child.is_missing())
15996    })
15997}
15998
15999/// A complete callable can still carry parser errors in its signature when a
16000/// project annotation is not part of the C++ grammar (`nonneg int`,
16001/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
16002/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
16003/// leaves inside the already-proven callable envelope; structured statements,
16004/// literals, missing tokens, and other malformed signature payload remain
16005/// rejected.
16006fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
16007    if !node.has_error() && !node.is_error() && !node.is_missing() {
16008        return false;
16009    }
16010    let mut stack = vec![node];
16011    let mut saw_error = false;
16012    while let Some(current) = stack.pop() {
16013        if current.is_missing() {
16014            return false;
16015        }
16016        if current.kind() == "ERROR" {
16017            saw_error = true;
16018            let mut cursor = current.walk();
16019            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
16020            if children
16021                .iter()
16022                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
16023            {
16024                return false;
16025            }
16026            stack.extend(children);
16027            continue;
16028        }
16029        let mut cursor = current.walk();
16030        stack.extend(current.children(&mut cursor));
16031    }
16032    saw_error
16033}
16034
16035fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
16036    node.kind() == "ERROR"
16037        && node.named_child_count() == 1
16038        && node.named_child(0).is_some_and(|attribute| {
16039            attribute.kind() == "identifier"
16040                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
16041        })
16042}
16043
16044/// A C++ attribute placed between a member's declarator and body can make
16045/// tree-sitter expose the callable as
16046/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
16047/// Keep this admission tied to that exact node geometry. In particular, an
16048/// arbitrary ERROR or identifier before a compound statement is not enough.
16049fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
16050    let Some(body) = cpp_reparsed_member_function_body(node) else {
16051        return false;
16052    };
16053    let mut cursor = node.walk();
16054    let named = node
16055        .named_children(&mut cursor)
16056        .filter(|child| child.kind() != "comment")
16057        .collect::<Vec<_>>();
16058    let [type_node, error, attribute, body_node] = named.as_slice() else {
16059        return false;
16060    };
16061    if !same_node(*body_node, body)
16062        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16063        || attribute.kind() != "identifier"
16064        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16065        || error.kind() != "ERROR"
16066        || error.named_child_count() != 1
16067    {
16068        return false;
16069    }
16070    error
16071        .named_child(0)
16072        .is_some_and(cpp_reparsed_attribute_callable_declarator)
16073}
16074
16075fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
16076    cpp_structured_type_path(node, source).is_some()
16077        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
16078}
16079
16080fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16081    let Some(body) = cpp_reparsed_member_function_body(node) else {
16082        return false;
16083    };
16084    let mut cursor = node.walk();
16085    let named = node
16086        .named_children(&mut cursor)
16087        .filter(|child| child.kind() != "comment")
16088        .collect::<Vec<_>>();
16089    let [friend, return_error, declarator, body_node] = named.as_slice() else {
16090        return false;
16091    };
16092    let Some(return_type) = return_error.named_child(0) else {
16093        return false;
16094    };
16095    same_node(*body_node, body)
16096        && friend.kind() == "type_identifier"
16097        && node_text(*friend, source) == "friend"
16098        && return_error.kind() == "ERROR"
16099        && return_error.named_child_count() == 1
16100        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16101        && extract_function_declarator(*declarator)
16102            .and_then(cpp_function_declarator_name_node)
16103            .is_some()
16104}
16105
16106fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16107    let Some(body) = cpp_reparsed_member_function_body(node) else {
16108        return false;
16109    };
16110    let mut cursor = node.walk();
16111    let named = node
16112        .named_children(&mut cursor)
16113        .filter(|child| child.kind() != "comment")
16114        .collect::<Vec<_>>();
16115    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
16116        return false;
16117    };
16118    let Some(return_type) = return_error.named_child(0) else {
16119        return false;
16120    };
16121    same_node(*body_node, body)
16122        && prefix
16123            .iter()
16124            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
16125        && attribute.kind() == "type_identifier"
16126        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16127        && return_error.kind() == "ERROR"
16128        && return_error.named_child_count() == 1
16129        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16130        && extract_function_declarator(*declarator)
16131            .and_then(cpp_function_declarator_name_node)
16132            .is_some()
16133}
16134
16135/// An included-range reparse that begins inside a malformed class can merge an
16136/// access label and following template member. Tree-sitter then emits the label
16137/// as the `template_type` name, the template parameter list as its arguments,
16138/// an ERROR-wrapped return type, the callable declarator, and its complete
16139/// body. Admit only that exact structured displacement.
16140fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16141    let Some(body) = cpp_reparsed_member_function_body(node) else {
16142        return false;
16143    };
16144    let mut cursor = node.walk();
16145    let named = node
16146        .named_children(&mut cursor)
16147        .filter(|child| child.kind() != "comment")
16148        .collect::<Vec<_>>();
16149    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
16150        return false;
16151    };
16152    let Some(template_name) = template_type.child_by_field_name("name") else {
16153        return false;
16154    };
16155    let Some(arguments) = template_type.child_by_field_name("arguments") else {
16156        return false;
16157    };
16158    let Some(return_type) = return_error.named_child(0) else {
16159        return false;
16160    };
16161    let mut cursor = template_type.walk();
16162    let template_errors = template_type
16163        .named_children(&mut cursor)
16164        .filter(|child| child.kind() == "ERROR")
16165        .collect::<Vec<_>>();
16166    let [comment_error] = template_errors.as_slice() else {
16167        return false;
16168    };
16169    let mut cursor = comment_error.walk();
16170    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
16171    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
16172        return false;
16173    };
16174    same_node(*body_node, body)
16175        && template_type.kind() == "template_type"
16176        && template_name.kind() == "type_identifier"
16177        && matches!(
16178            node_text(template_name, source).trim(),
16179            "public" | "private" | "protected"
16180        )
16181        && arguments.kind() == "template_argument_list"
16182        && arguments.named_child_count() > 0
16183        && !arguments.has_error()
16184        && !colon.is_named()
16185        && colon.kind() == ":"
16186        && comments.iter().all(|child| child.kind() == "comment")
16187        && !template_keyword.is_named()
16188        && template_keyword.kind() == "template"
16189        && return_error.kind() == "ERROR"
16190        && return_error.named_child_count() == 1
16191        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16192        && extract_function_declarator(*declarator)
16193            .and_then(cpp_function_declarator_name_node)
16194            .is_some()
16195}
16196
16197/// Return the constructor declaration tree-sitter can merge into an access
16198/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
16199/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
16200/// declaration's apparent type; the callable name must still exactly match the
16201/// recovered class, so unrelated labeled statements are never re-owned.
16202fn cpp_reparsed_preprocessor_constructor<'tree>(
16203    node: Node<'tree>,
16204    class_name: &str,
16205    source: &str,
16206) -> Option<Node<'tree>> {
16207    if node.kind() != "labeled_statement" {
16208        return None;
16209    }
16210    let mut cursor = node.walk();
16211    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16212    let [label, directive_error, declaration] = named.as_slice() else {
16213        return None;
16214    };
16215    if label.kind() != "statement_identifier"
16216        || !matches!(
16217            node_text(*label, source),
16218            "public" | "private" | "protected"
16219        )
16220        || directive_error.kind() != "ERROR"
16221        || directive_error.child_count() != 1
16222        || directive_error
16223            .child(0)
16224            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
16225        || declaration.kind() != "declaration"
16226        || declaration.named_child_count() != 2
16227    {
16228        return None;
16229    }
16230    let apparent_type = declaration.child_by_field_name("type")?;
16231    if apparent_type.kind() != "type_identifier"
16232        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
16233    {
16234        return None;
16235    }
16236    let declarator = declaration.child_by_field_name("declarator")?;
16237    let function = extract_function_declarator(declarator)?;
16238    let name = cpp_function_declarator_name_node(function)?;
16239    (node_text(name, source) == class_name).then_some(*declaration)
16240}
16241
16242fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
16243    if extract_function_declarator(node)
16244        .and_then(cpp_function_declarator_name_node)
16245        .is_some()
16246    {
16247        return true;
16248    }
16249    node.kind() == "init_declarator"
16250        && node
16251            .child_by_field_name("declarator")
16252            .is_some_and(|declarator| declarator.kind() == "identifier")
16253        && node
16254            .child_by_field_name("value")
16255            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
16256}
16257
16258/// Return true for the constrained/attribute form that tree-sitter splits into
16259/// an ERROR declaration, a preprocessor `requires` clause, and a following
16260/// compound statement. The three nodes must remain immediate named siblings;
16261/// this deliberately does not search source text or skip unrelated statements.
16262fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
16263    if node.kind() != "ERROR" || node.named_child_count() != 3 {
16264        return false;
16265    }
16266    let mut cursor = node.walk();
16267    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16268    let [type_node, function_declarator, attribute] = named.as_slice() else {
16269        return false;
16270    };
16271    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16272        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
16273        || attribute.kind() != "identifier"
16274        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16275    {
16276        return false;
16277    }
16278    let Some(preproc) =
16279        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16280    else {
16281        return false;
16282    };
16283    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
16284        .filter(|sibling| sibling.kind() == "compound_statement")
16285    else {
16286        return false;
16287    };
16288    let Some(open) = body.child(0) else {
16289        return false;
16290    };
16291    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
16292        return false;
16293    };
16294    let Some(condition) = preproc.child_by_field_name("condition") else {
16295        return false;
16296    };
16297    let mut cursor = preproc.walk();
16298    let payload = preproc
16299        .named_children(&mut cursor)
16300        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
16301        .collect::<Vec<_>>();
16302    let [requires_statement] = payload.as_slice() else {
16303        return false;
16304    };
16305    let requires_clause = requires_statement.named_child(0);
16306
16307    open.kind() == "{"
16308        && !open.is_missing()
16309        && close.kind() == "}"
16310        && !close.is_missing()
16311        && close.end_byte() == body.end_byte()
16312        && requires_statement.kind() == "expression_statement"
16313        && requires_statement.named_child_count() == 1
16314        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
16315}
16316
16317fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16318    let mut sibling = node.next_named_sibling();
16319    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16320        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
16321    }
16322    sibling
16323}
16324
16325fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16326    let mut sibling = node.prev_named_sibling();
16327    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16328        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
16329    }
16330    sibling
16331}
16332
16333fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
16334    let Some(preproc) =
16335        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16336    else {
16337        return false;
16338    };
16339    let Some(error) =
16340        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
16341    else {
16342        return false;
16343    };
16344    cpp_reparsed_attribute_requires_error(error, source)
16345}
16346
16347fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
16348    node: Node<'tree>,
16349    source: &str,
16350) -> Option<Node<'tree>> {
16351    if node.kind() != "ERROR" {
16352        return None;
16353    }
16354    let mut cursor = node.walk();
16355    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16356    let [parameter, macro_name, message] = named.as_slice() else {
16357        return None;
16358    };
16359    let parameter_name = parameter.named_child(0)?;
16360    (parameter.kind() == "type_parameter_declaration"
16361        && parameter_name.kind() == "type_identifier"
16362        && macro_name.kind() == "type_identifier"
16363        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16364        && message.kind() == "string_literal")
16365        .then_some(parameter_name)
16366}
16367
16368/// Recognize the alternate constraint-macro prefix where tree-sitter retains
16369/// the complete qualified constraint as a fourth child instead of moving it
16370/// into the following function. Keep the gate tied to a two-type template
16371/// constraint that names the declared type parameter.
16372fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
16373    node: Node<'tree>,
16374    source: &str,
16375) -> Option<Node<'tree>> {
16376    if node.kind() != "ERROR" {
16377        return None;
16378    }
16379    let mut cursor = node.walk();
16380    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16381    let [parameter, macro_name, message, constraint] = named.as_slice() else {
16382        return None;
16383    };
16384    let parameter_name = parameter.named_child(0)?;
16385    let constraint_scope = constraint.child_by_field_name("scope")?;
16386    let constraint_template = constraint.child_by_field_name("name")?;
16387    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16388    let mut argument_cursor = constraint_arguments.walk();
16389    let constraint_types = constraint_arguments
16390        .named_children(&mut argument_cursor)
16391        .collect::<Vec<_>>();
16392    if parameter.kind() != "type_parameter_declaration"
16393        || parameter_name.kind() != "type_identifier"
16394        || macro_name.kind() != "type_identifier"
16395        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16396        || message.kind() != "string_literal"
16397        || constraint.kind() != "qualified_identifier"
16398        || constraint_scope.kind() != "namespace_identifier"
16399        || !matches!(
16400            constraint_template.kind(),
16401            "template_function" | "template_type"
16402        )
16403        || !matches!(constraint_types.as_slice(), [left, right]
16404            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16405        || constraint_arguments.has_error()
16406    {
16407        return None;
16408    }
16409    let parameter_text = node_text(parameter_name, source);
16410    let mut stack = constraint_types;
16411    while let Some(current) = stack.pop() {
16412        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
16413            return Some(parameter_name);
16414        }
16415        let mut cursor = current.walk();
16416        stack.extend(current.named_children(&mut cursor));
16417    }
16418    None
16419}
16420
16421fn cpp_reparsed_template_macro_companion_is_indexable(
16422    node: Node<'_>,
16423    parameter_name: Node<'_>,
16424    source: &str,
16425) -> bool {
16426    let Some(body) = cpp_reparsed_member_function_body(node) else {
16427        return false;
16428    };
16429    let mut cursor = node.walk();
16430    let named = node
16431        .named_children(&mut cursor)
16432        .filter(|child| child.kind() != "comment")
16433        .collect::<Vec<_>>();
16434    let [
16435        constraint,
16436        close_error,
16437        storage,
16438        return_error,
16439        declarator,
16440        body_node,
16441    ] = named.as_slice()
16442    else {
16443        return false;
16444    };
16445    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
16446        return false;
16447    };
16448    let Some(constraint_template) = constraint.child_by_field_name("name") else {
16449        return false;
16450    };
16451    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
16452        return false;
16453    };
16454    let Some(return_type) = return_error.named_child(0) else {
16455        return false;
16456    };
16457    let mut cursor = constraint_arguments.walk();
16458    let constraint_types = constraint_arguments
16459        .named_children(&mut cursor)
16460        .collect::<Vec<_>>();
16461    same_node(*body_node, body)
16462        && constraint.kind() == "qualified_identifier"
16463        && constraint_scope.kind() == "namespace_identifier"
16464        && constraint_template.kind() == "template_type"
16465        && matches!(constraint_types.as_slice(), [left, right]
16466            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16467        && !constraint_arguments.has_error()
16468        && close_error.kind() == "ERROR"
16469        && close_error.named_child_count() == 0
16470        && storage.kind() == "storage_class_specifier"
16471        && return_error.kind() == "ERROR"
16472        && return_error.named_child_count() == 1
16473        && return_type.kind() == "identifier"
16474        && node_text(return_type, source) == node_text(parameter_name, source)
16475        && extract_function_declarator(*declarator)
16476            .and_then(cpp_function_declarator_name_node)
16477            .is_some()
16478}
16479
16480fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
16481    node: Node<'tree>,
16482    parameter_name: Node<'_>,
16483    source: &str,
16484) -> Option<Node<'tree>> {
16485    let body = cpp_reparsed_member_function_body(node)?;
16486    let constraint = node.child_by_field_name("type")?;
16487    let constraint_template = constraint.child_by_field_name("name")?;
16488    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16489    let mut argument_cursor = constraint_arguments.walk();
16490    let constraint_types = constraint_arguments
16491        .named_children(&mut argument_cursor)
16492        .collect::<Vec<_>>();
16493    if constraint.kind() != "qualified_identifier"
16494        || constraint_template.kind() != "template_type"
16495        || !matches!(constraint_types.as_slice(), [left, right]
16496            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16497        || constraint_arguments.has_error()
16498        || node
16499            .child_by_field_name("body")
16500            .is_none_or(|candidate| !same_node(candidate, body))
16501    {
16502        return None;
16503    }
16504
16505    let mut cursor = node.walk();
16506    let recovery_errors = node
16507        .named_children(&mut cursor)
16508        .filter(|child| child.kind() == "ERROR")
16509        .collect::<Vec<_>>();
16510    if !recovery_errors
16511        .iter()
16512        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
16513        || !recovery_errors.iter().all(|error| {
16514            error.named_child_count() == 0
16515                || cpp_reparsed_constraint_macro_error(*error, source)
16516                || (error.named_child_count() == 1
16517                    && error
16518                        .named_child(0)
16519                        .is_some_and(|child| child.kind() == "function_declarator"))
16520        })
16521    {
16522        return None;
16523    }
16524
16525    let parameter_text = node_text(parameter_name, source);
16526    let mut declarators = node
16527        .child_by_field_name("declarator")
16528        .and_then(extract_function_declarator)
16529        .into_iter()
16530        .collect::<Vec<_>>();
16531    for error in recovery_errors {
16532        let mut stack = vec![error];
16533        while let Some(current) = stack.pop() {
16534            if current.kind() == "function_declarator" {
16535                declarators.push(current);
16536            }
16537            let mut cursor = current.walk();
16538            stack.extend(current.named_children(&mut cursor));
16539        }
16540    }
16541    declarators.into_iter().find(|declarator| {
16542        cpp_function_declarator_name_node(*declarator)
16543            .is_some_and(|name| name.kind() == "identifier")
16544            && declarator
16545                .child_by_field_name("parameters")
16546                .is_some_and(|parameters| {
16547                    parameters
16548                        .named_children(&mut parameters.walk())
16549                        .filter_map(|parameter| parameter.child_by_field_name("type"))
16550                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
16551                })
16552    })
16553}
16554
16555fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
16556    node: Node<'_>,
16557    parameter_name: Node<'_>,
16558    source: &str,
16559) -> bool {
16560    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
16561}
16562
16563fn cpp_reparsed_template_macro_function_companion_is_indexable(
16564    node: Node<'_>,
16565    parameter_name: Node<'_>,
16566    source: &str,
16567) -> bool {
16568    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
16569        return false;
16570    }
16571    let Some(return_type) = node.child_by_field_name("type") else {
16572        return false;
16573    };
16574    let Some(function_declarator) = node
16575        .child_by_field_name("declarator")
16576        .and_then(extract_function_declarator)
16577    else {
16578        return false;
16579    };
16580    if cpp_function_declarator_name_node(function_declarator).is_none()
16581        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
16582    {
16583        return false;
16584    }
16585    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
16586        return false;
16587    };
16588    let parameter_text = node_text(parameter_name, source);
16589    parameters
16590        .named_children(&mut parameters.walk())
16591        .any(|parameter| {
16592            parameter
16593                .child_by_field_name("type")
16594                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
16595        })
16596}
16597
16598fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
16599    if node.kind() != "ERROR" {
16600        return false;
16601    }
16602    let mut stack = vec![node];
16603    while let Some(current) = stack.pop() {
16604        let macro_shape = match current.kind() {
16605            "call_expression" => current
16606                .child_by_field_name("function")
16607                .zip(current.child_by_field_name("arguments")),
16608            "init_declarator" => current
16609                .child_by_field_name("declarator")
16610                .zip(current.child_by_field_name("value")),
16611            _ => None,
16612        };
16613        if let Some((name, arguments)) = macro_shape
16614            && name.kind() == "identifier"
16615            && arguments.kind() == "argument_list"
16616            && arguments.named_child_count() >= 2
16617            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
16618        {
16619            return true;
16620        }
16621        let mut cursor = current.walk();
16622        stack.extend(current.named_children(&mut cursor));
16623    }
16624    false
16625}
16626
16627fn cpp_recovered_template_macro_constructor<'tree>(
16628    node: Node<'tree>,
16629    source: &str,
16630) -> Option<(Node<'tree>, Node<'tree>)> {
16631    let mut prefix = node.prev_named_sibling()?;
16632    while prefix.kind() == "comment" {
16633        prefix = prefix.prev_named_sibling()?;
16634    }
16635    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
16636    let parameter = parameter_name
16637        .parent()
16638        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
16639    let declarator =
16640        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
16641    Some((declarator, parameter))
16642}
16643
16644fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
16645    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
16646        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16647            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
16648                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
16649                    function,
16650                    parameter_name,
16651                    source,
16652                )
16653        });
16654    }
16655    let Some(parameter_name) =
16656        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
16657    else {
16658        return false;
16659    };
16660    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16661        cpp_reparsed_template_macro_function_companion_is_indexable(
16662            function,
16663            parameter_name,
16664            source,
16665        )
16666    })
16667}
16668
16669fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16670    let function_name = node
16671        .child_by_field_name("declarator")
16672        .and_then(extract_function_declarator)
16673        .and_then(cpp_function_declarator_name_node);
16674    if let Some(body) = cpp_reparsed_member_function_body(node)
16675        && function_name.is_some()
16676        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
16677    {
16678        return true;
16679    }
16680    cpp_reparsed_attribute_member_function(node, source)
16681        || cpp_reparsed_friend_function_is_indexable(node, source)
16682        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
16683        || cpp_reparsed_access_template_function_is_indexable(node, source)
16684        || cpp_recovered_template_macro_constructor(node, source).is_some()
16685}
16686
16687/// Recognize the three top-level nodes produced when an unknown attribute
16688/// macro separates an inline member's declarator from its body in a reparsed
16689/// class interior: an errorful declaration with a missing semicolon, the macro
16690/// call expression, and the complete compound body. Their adjacency and exact
16691/// structured shapes prove one recoverable member envelope; arbitrary calls or
16692/// blocks do not pass this gate.
16693fn cpp_reparsed_macro_attribute_member_sequence(
16694    children: &[Node<'_>],
16695    index: usize,
16696    source: &str,
16697) -> bool {
16698    let Some(prefix) = children.get(index).copied() else {
16699        return false;
16700    };
16701    let declaration = if prefix.kind() == "labeled_statement" {
16702        prefix
16703            .named_child(prefix.named_child_count().saturating_sub(1))
16704            .filter(|child| child.kind() == "declaration")
16705    } else {
16706        (prefix.kind() == "declaration").then_some(prefix)
16707    };
16708    let Some(declaration) = declaration else {
16709        return false;
16710    };
16711    if !declaration.has_error()
16712        || declaration
16713            .child_by_field_name("declarator")
16714            .and_then(extract_function_declarator)
16715            .and_then(cpp_function_declarator_name_node)
16716            .is_none()
16717    {
16718        return false;
16719    }
16720    let Some(attribute_statement) = children.get(index + 1).copied() else {
16721        return false;
16722    };
16723    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
16724        .then(|| attribute_statement.named_child(0))
16725        .flatten()
16726        .filter(|child| child.kind() == "call_expression")
16727    else {
16728        return false;
16729    };
16730    let Some(attribute_name) = attribute_call
16731        .child_by_field_name("function")
16732        .filter(|function| function.kind() == "identifier")
16733        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
16734    else {
16735        return false;
16736    };
16737    if !cpp_export_macro_token(&attribute_name) {
16738        return false;
16739    }
16740    let Some(body) = children.get(index + 2).copied() else {
16741        return false;
16742    };
16743    body.kind() == "compound_statement"
16744        && body.child(0).is_some_and(|open| open.kind() == "{")
16745        && body
16746            .child(body.child_count().saturating_sub(1))
16747            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
16748        && declaration.end_byte() <= attribute_statement.start_byte()
16749        && attribute_statement.end_byte() <= body.start_byte()
16750}
16751
16752/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
16753/// types and specifiers followed by a declarator, over and over, with nothing
16754/// left over. A string-argument attribute macro is what strands them there, and
16755/// the walk indexes exactly what this reads, so admitting the region is safe
16756/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
16757/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
16758/// the whole class body.
16759fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
16760    if node.kind() != "ERROR" {
16761        return false;
16762    }
16763    let run = stranded_declaration_run(node, source);
16764    run.complete && !run.declarations.is_empty()
16765}
16766
16767fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
16768    let mut cursor = root.walk();
16769    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
16770    let mut saw_member = false;
16771    let mut index = 0;
16772    while index < children.len() {
16773        let child = children[index];
16774        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
16775            saw_member = true;
16776            index += 3;
16777            continue;
16778        }
16779        if let Some(recovered) = fragmented_class_body(child, source) {
16780            let Some(tree) = cpp_reparse_fragmented_class_body(
16781                source,
16782                recovered.body.reparse_start,
16783                recovered.body.reparse_end,
16784            ) else {
16785                return false;
16786            };
16787            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
16788                return false;
16789            }
16790            saw_member = true;
16791            index += 1;
16792            while index < children.len()
16793                && children[index].end_byte() <= recovered.body.class_range.end_byte
16794            {
16795                index += 1;
16796            }
16797            continue;
16798        }
16799        match child.kind() {
16800            "comment" => {}
16801            "labeled_statement" => saw_member = true,
16802            "function_definition" => {
16803                if child.has_error()
16804                    && !cpp_reparsed_member_function_is_indexable(child, source)
16805                    && cpp_sentinel_macro_region(child, source).is_none()
16806                {
16807                    return false;
16808                }
16809                saw_member = true;
16810            }
16811            // The attribute a string-argument macro leaves as a call statement
16812            // of its own. It declares nothing; the member it decorated is the
16813            // sibling after it, checked on its own turn.
16814            "expression_statement" if is_string_attribute_macro_statement(child) => {}
16815            "ERROR"
16816                if (cpp_reparsed_member_error_is_indexable(child)
16817                    || cpp_reparsed_adjacent_copy_control_error(child, source)
16818                    || cpp_reparsed_stranded_member_error(child, source))
16819                    && (child
16820                        .next_named_sibling()
16821                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
16822                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
16823            {
16824                saw_member = true;
16825            }
16826            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
16827                saw_member = true;
16828            }
16829            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
16830                saw_member = true;
16831            }
16832            "expression_statement"
16833                if cpp_is_stray_semicolon(child, source)
16834                    && child.prev_named_sibling().is_some_and(|error| {
16835                        cpp_reparsed_member_error_is_indexable(error)
16836                            || cpp_reparsed_adjacent_copy_control_error(error, source)
16837                            || cpp_reparsed_stranded_member_error(error, source)
16838                    }) =>
16839            {
16840                saw_member = true;
16841            }
16842            "compound_statement"
16843                if cpp_reparsed_constructor_body_is_indexable(child, source)
16844                    || cpp_reparsed_attribute_requires_body(child, source) =>
16845            {
16846                saw_member = true;
16847            }
16848            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
16849            _ => return false,
16850        }
16851        index += 1;
16852    }
16853    saw_member
16854}
16855
16856/// Detect the malformed constructor shape that tree-sitter exposes as an
16857/// access-label statement followed by initializer-looking declarations. The
16858/// declarations are not class members: visiting their `location(loc)` and
16859/// `string(s)` function declarators would publish synthetic functions. The
16860/// export-class fallback keeps the original sibling nodes and therefore avoids
16861/// this parser artifact. The returned range identifies the real constructor
16862/// header, which can be reparsed independently as a structured declarator.
16863fn cpp_reparsed_synthetic_initializer_constructor_range(
16864    root: Node<'_>,
16865    class_name: &str,
16866    source: &str,
16867    constructor_end: usize,
16868) -> Option<std::ops::Range<usize>> {
16869    let mut stack = {
16870        let mut cursor = root.walk();
16871        root.named_children(&mut cursor).collect::<Vec<_>>()
16872    };
16873    while let Some(current) = stack.pop() {
16874        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
16875            current,
16876            class_name,
16877            source,
16878            constructor_end,
16879        ) {
16880            return Some(range);
16881        }
16882        if current.kind() == "ERROR" {
16883            let mut cursor = current.walk();
16884            stack.extend(current.named_children(&mut cursor));
16885        }
16886    }
16887    None
16888}
16889
16890/// Recover an inline constructor that a function-like export macro makes
16891/// tree-sitter merge with the following overload. In the reparsed class-body
16892/// region, the access label wraps one declaration whose ERROR contains the
16893/// constructor declarator and its base-initializer/body, while the declaration's
16894/// ordinary declarator is the following overload. Every boundary below comes
16895/// from that CST; no source syntax is reparsed by hand.
16896fn cpp_reparsed_merged_inline_constructor<'tree>(
16897    root: Node<'tree>,
16898    class_name: &str,
16899    source: &str,
16900) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
16901    let mut stack = vec![root];
16902    while let Some(current) = stack.pop() {
16903        if current.kind() != "labeled_statement" {
16904            let mut cursor = current.walk();
16905            stack.extend(current.named_children(&mut cursor));
16906            continue;
16907        }
16908        let declaration = current
16909            .named_children(&mut current.walk())
16910            .find(|child| child.kind() == "declaration")?;
16911        if declaration
16912            .child_by_field_name("type")
16913            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
16914        {
16915            continue;
16916        }
16917        let following = declaration
16918            .child_by_field_name("declarator")
16919            .and_then(extract_function_declarator)
16920            .and_then(cpp_function_declarator_name_node);
16921        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
16922            continue;
16923        }
16924        let mut declaration_cursor = declaration.walk();
16925        let Some(error) = declaration
16926            .named_children(&mut declaration_cursor)
16927            .find(|child| child.kind() == "ERROR")
16928        else {
16929            continue;
16930        };
16931        let mut error_cursor = error.walk();
16932        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
16933        let Some(constructor) = error_children.iter().copied().find(|child| {
16934            child.kind() == "function_declarator"
16935                && cpp_function_declarator_name_node(*child)
16936                    .is_some_and(|name| node_text(name, source).trim() == class_name)
16937        }) else {
16938            continue;
16939        };
16940        let Some(body) = error_children.iter().copied().find_map(|child| {
16941            (child.kind() == "init_declarator")
16942                .then(|| child.child_by_field_name("value"))
16943                .flatten()
16944                .filter(|value| value.kind() == "initializer_list")
16945        }) else {
16946            continue;
16947        };
16948        if constructor.end_byte() > body.start_byte() {
16949            continue;
16950        }
16951        return Some((constructor.start_byte()..body.end_byte(), body));
16952    }
16953    None
16954}
16955
16956fn cpp_reparsed_synthetic_initializer_constructor(
16957    node: Node<'_>,
16958    class_name: &str,
16959    source: &str,
16960    constructor_end: usize,
16961) -> Option<std::ops::Range<usize>> {
16962    if node.kind() != "labeled_statement" {
16963        return None;
16964    }
16965    let mut cursor = node.walk();
16966    let named = node
16967        .named_children(&mut cursor)
16968        .filter(|child| child.kind() != "comment")
16969        .collect::<Vec<_>>();
16970    let label = named.first()?;
16971    if label.kind() != "statement_identifier"
16972        || !matches!(
16973            node_text(*label, source).trim(),
16974            "public" | "private" | "protected"
16975        )
16976    {
16977        return None;
16978    }
16979    let call_error_index = named.iter().position(|child| {
16980        if child.kind() != "ERROR" {
16981            return false;
16982        }
16983        let mut stack = vec![*child];
16984        while let Some(current) = stack.pop() {
16985            if current.kind() == "call_expression"
16986                && current
16987                    .child_by_field_name("function")
16988                    .is_some_and(|function| {
16989                        function.kind() == "identifier"
16990                            && node_text(function, source).trim() == class_name
16991                    })
16992            {
16993                return true;
16994            }
16995            let mut cursor = current.walk();
16996            stack.extend(current.named_children(&mut cursor));
16997        }
16998        false
16999    })?;
17000    let constructor_call = {
17001        let mut stack = vec![named[call_error_index]];
17002        let mut found = None;
17003        while let Some(current) = stack.pop() {
17004            if current.kind() == "call_expression"
17005                && current
17006                    .child_by_field_name("function")
17007                    .is_some_and(|function| {
17008                        function.kind() == "identifier"
17009                            && node_text(function, source).trim() == class_name
17010                    })
17011            {
17012                found = Some(current);
17013                break;
17014            }
17015            let mut cursor = current.walk();
17016            stack.extend(current.named_children(&mut cursor));
17017        }
17018        found
17019    };
17020    let constructor_call = constructor_call?;
17021    named.iter().skip(call_error_index + 1).find(|child| {
17022        child.kind() == "declaration" && child.has_error() && {
17023            let mut cursor = child.walk();
17024            child.named_children(&mut cursor).any(|declarator| {
17025                declarator.kind() == "init_declarator"
17026                    && declarator
17027                        .child_by_field_name("declarator")
17028                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
17029                    && declarator
17030                        .child_by_field_name("value")
17031                        .is_some_and(|value| value.kind() == "initializer_list")
17032            })
17033        }
17034    })?;
17035    Some(constructor_call.start_byte()..constructor_end)
17036}
17037
17038fn cpp_reparsed_exact_constructor_declarator<'tree>(
17039    root: Node<'tree>,
17040    start: usize,
17041    class_name: &str,
17042    source: &str,
17043) -> Option<Node<'tree>> {
17044    let mut candidate = None;
17045    let mut stack = vec![root];
17046    while let Some(current) = stack.pop() {
17047        if current.kind() == "function_declarator"
17048            && current.start_byte() == start
17049            && cpp_function_declarator_name_node(current)
17050                .is_some_and(|name| node_text(name, source).trim() == class_name)
17051        {
17052            if candidate.is_some() {
17053                return None;
17054            }
17055            candidate = Some(current);
17056            continue;
17057        }
17058        let mut cursor = current.walk();
17059        stack.extend(current.named_children(&mut cursor));
17060    }
17061    candidate
17062}
17063
17064fn cpp_is_indexable_item_kind(kind: &str) -> bool {
17065    matches!(
17066        kind,
17067        "namespace_definition"
17068            | "class_specifier"
17069            | "struct_specifier"
17070            | "union_specifier"
17071            | "enum_specifier"
17072            | "function_definition"
17073            | "template_declaration"
17074            | "declaration"
17075            | "field_declaration"
17076            | "alias_declaration"
17077            | "static_assert_declaration"
17078            | "type_definition"
17079            | "using_declaration"
17080            | "linkage_specification"
17081            | "preproc_def"
17082            | "preproc_function_def"
17083            | "preproc_include"
17084            | "preproc_if"
17085            | "preproc_ifdef"
17086            | "preproc_call"
17087    )
17088}
17089
17090#[cfg(test)]
17091mod tests {
17092    use super::*;
17093    use crate::adapter::parse_cpp_file;
17094    use brokk_bifrost_core::analyzer::parsed_file::{
17095        finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
17096        start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
17097    };
17098    use std::fmt::Write;
17099
17100    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
17101        let mut parser = tree_sitter::Parser::new();
17102        parser
17103            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17104            .unwrap();
17105        let tree = parser.parse(source, None).unwrap();
17106        let file = ProjectFile::new(std::env::temp_dir(), name);
17107        parse_cpp_file(&file, source, &tree)
17108    }
17109
17110    #[test]
17111    fn displaced_terminator_ignores_nested_initializer_endif() {
17112        let source = "#ifdef ENABLE_ITEMS\n\
17113struct Item { int reg; };\n\
17114static const struct Item items[] = {{1}};\n\
17115static const struct Item extras[] = {\n\
17116  {0},\n\
17117#ifndef REDUCED\n\
17118  {1},\n\
17119#endif\n\
17120};\n\
17121int read_item(int i) { return items[i].reg; }\n\
17122#endif\n";
17123        let mut parser = tree_sitter::Parser::new();
17124        parser
17125            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17126            .expect("C++ grammar");
17127        let tree = parser.parse(source, None).expect("fixture tree");
17128        let conditional = tree
17129            .root_node()
17130            .named_child(0)
17131            .filter(|node| node.kind() == "preproc_ifdef")
17132            .expect("outer conditional");
17133
17134        assert!(conditional.has_error());
17135        assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
17136        assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
17137    }
17138
17139    #[test]
17140    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
17141        let source = "struct Image { PyObject_HEAD Imaging image; };";
17142        let parsed = parse_cpp_declarations(source, "image.h");
17143        let names = parsed
17144            .declarations()
17145            .iter()
17146            .map(|unit| unit.fq_name())
17147            .collect::<Vec<_>>();
17148
17149        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
17150        assert!(
17151            names.iter().all(|name| name != "Image.Imaging"),
17152            "the pseudo-declarator must not become a field: {names:#?}"
17153        );
17154
17155        let pointer = parse_cpp_declarations(
17156            "struct Image { PyObject_HEAD Imaging *image; };",
17157            "image-pointer.h",
17158        );
17159        let pointer_names = pointer
17160            .declarations()
17161            .iter()
17162            .map(|unit| unit.fq_name())
17163            .collect::<Vec<_>>();
17164        assert!(
17165            pointer_names.iter().any(|name| name == "Image.image"),
17166            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
17167        );
17168        assert!(
17169            pointer_names.iter().all(|name| name != "Image.Imaging"),
17170            "the pointer recovery error must not become a field: {pointer_names:#?}"
17171        );
17172
17173        let unrelated_macro = parse_cpp_declarations(
17174            "struct Image { OTHER_HEAD Imaging other; };",
17175            "image-near-miss.h",
17176        );
17177        let unrelated_names = unrelated_macro
17178            .declarations()
17179            .iter()
17180            .map(|unit| unit.fq_name())
17181            .collect::<Vec<_>>();
17182        assert!(
17183            unrelated_names.iter().all(|name| name != "Image.other"),
17184            "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
17185        );
17186    }
17187
17188    #[test]
17189    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
17190        let source = r#"namespace testing {
17191namespace internal {
17192    namespace detail {
17193        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
17194        public:
17195            int value() const { return count_ + 1; }
17196        private:
17197            int count_;
17198        };
17199        class GTEST_API_ [[nodiscard]] OtherReporter {
17200        public:
17201            int value() const { return count_ + 2; }
17202        private:
17203            int count_;
17204        };
17205    }
17206
17207    template <typename T>
17208    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
17209
17210    class TailReporter {};
17211}
17212}
17213"#;
17214        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
17215        let declarations = parsed.declarations();
17216        let mut parser = tree_sitter::Parser::new();
17217        parser
17218            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17219            .unwrap();
17220        let tree = parser.parse(source, None).unwrap();
17221        let tail_start = source.find("TailReporter").expect("tail class");
17222        let tail_node = tree
17223            .root_node()
17224            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
17225            .expect("tail class AST node");
17226        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
17227        assert!(
17228            tree.root_node().has_error(),
17229            "the malformed class must exercise recovery"
17230        );
17231        assert!(index.region_at(tail_start).is_some());
17232        assert_eq!(
17233            index.enclosing_namespace_components(tail_node, source),
17234            ["testing", "internal"]
17235        );
17236        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
17237        let mut recovered_parsed = ParsedFile::new(String::new());
17238        let class_unit = CodeUnit::new_fq(
17239            file.clone(),
17240            CodeUnitType::Class,
17241            "testing",
17242            "ScopedFakeTestPartResultReporter",
17243            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
17244        );
17245        let scope = ScopeInfo {
17246            package_name: "testing".to_string(),
17247            module: None,
17248            class_unit: Some(class_unit),
17249            template_signature: Some("<typename T>".to_string()),
17250            template_metadata: Some(CppTemplateMetadata {
17251                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
17252                primary_fq_name: String::new(),
17253                parameters: Vec::new(),
17254                specialization_arguments: Vec::new(),
17255                alias_target: None,
17256            }),
17257            declarations_are_fields: true,
17258            recovered_specialization_member_scope: true,
17259            visible_using_namespaces: Vec::new(),
17260        };
17261        let mut visitor = CppVisitor {
17262            file: &file,
17263            source,
17264            parsed: &mut recovered_parsed,
17265            c_tag_semantics: false,
17266            recovered_class_sibling_scopes: HashMap::default(),
17267            consumed_fragment_regions: Vec::new(),
17268            orphaned_namespaces: &index,
17269            partitioned_regions: Vec::new(),
17270            namespace_forward_scans: HashMap::default(),
17271            field_owners: None,
17272            recovery_captures: Vec::new(),
17273            object_macro_fields: HashMap::default(),
17274            ambiguous_object_macro_fields: HashSet::default(),
17275        };
17276        let recovered = visitor
17277            .recovered_namespace_scope(tail_node, &scope)
17278            .expect("the tail must use the stolen namespace scope");
17279        assert_eq!(recovered.package_name, "testing::internal");
17280        assert!(
17281            recovered.class_unit.is_none(),
17282            "recovered namespace declarations cannot retain the malformed class owner"
17283        );
17284        assert!(recovered.template_signature.is_none());
17285        assert!(recovered.template_metadata.is_none());
17286        assert!(!recovered.declarations_are_fields);
17287        assert!(!recovered.recovered_specialization_member_scope);
17288        assert!(
17289            declarations
17290                .iter()
17291                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
17292            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
17293        );
17294        assert!(
17295            declarations
17296                .iter()
17297                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
17298            "the recovered free function remains in its namespace: {declarations:#?}"
17299        );
17300        assert!(
17301            declarations
17302                .iter()
17303                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
17304            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
17305        );
17306        assert!(
17307            declarations.iter().all(|unit| {
17308                !unit
17309                    .short_name()
17310                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
17311            }),
17312            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
17313        );
17314        assert!(
17315            declarations
17316                .iter()
17317                .all(|unit| !unit.identifier().is_empty()),
17318            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
17319        );
17320    }
17321
17322    #[test]
17323    fn object_like_field_macros_materialize_owner_specific_declarations() {
17324        let source = r#"#define PUBLIC_FIELDS int public_value;
17325#define PRIVATE_FIELDS int private_value;
17326#define NOT_A_FIELD_LIST not a declaration
17327
17328struct First {
17329  PUBLIC_FIELDS
17330  PRIVATE_FIELDS
17331};
17332struct Second {
17333  PUBLIC_FIELDS
17334  NOT_A_FIELD_LIST
17335};
17336#undef PUBLIC_FIELDS
17337struct Third {
17338  PUBLIC_FIELDS
17339};
17340"#;
17341        let parsed = parse_cpp_declarations(source, "macro-fields.c");
17342        let fields = parsed
17343            .declarations()
17344            .iter()
17345            .filter(|unit| unit.is_field())
17346            .map(|unit| unit.fq_name())
17347            .collect::<Vec<_>>();
17348
17349        assert!(
17350            fields.contains(&"First.public_value".to_string()),
17351            "{fields:?}"
17352        );
17353        assert!(
17354            fields.contains(&"First.private_value".to_string()),
17355            "{fields:?}"
17356        );
17357        assert!(
17358            fields.contains(&"Second.public_value".to_string()),
17359            "{fields:?}"
17360        );
17361        assert!(
17362            !fields.iter().any(|field| field.contains("not_a_field")),
17363            "malformed macro must fail closed: {fields:?}"
17364        );
17365        assert!(
17366            !fields.iter().any(|field| field.starts_with("Third.")),
17367            "undefined macro must fail closed: {fields:?}"
17368        );
17369    }
17370
17371    #[test]
17372    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
17373        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
17374        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
17375        let mut macros = parsed
17376            .declarations()
17377            .iter()
17378            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
17379            .collect::<Vec<_>>();
17380        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
17381
17382        assert_eq!(macros.len(), 2, "{macros:#?}");
17383        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
17384        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
17385        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
17386        assert_eq!(
17387            parsed.declaration_ranges(macros[1])[0].start_byte,
17388            source.rfind("#define VALUE 2").expect("second definition")
17389        );
17390    }
17391
17392    #[test]
17393    fn identifies_export_macro_class_base_displaced_into_declarator() {
17394        let source = r#"#define PROJECT_API_
17395namespace project {
17396namespace internal {
17397template <typename T>
17398class Base {};
17399}
17400template <typename T>
17401class Wrapper;
17402template <>
17403class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
17404}
17405"#;
17406        let mut parser = tree_sitter::Parser::new();
17407        parser
17408            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17409            .unwrap();
17410        let tree = parser.parse(source, None).unwrap();
17411        let start = source.find("internal::Base<int>").expect("base");
17412        let mut base = tree
17413            .root_node()
17414            .descendant_for_byte_range(start, start + 8)
17415            .expect("base syntax");
17416        while base.kind() != "qualified_identifier" {
17417            base = base.parent().expect("qualified base ancestor");
17418        }
17419        assert!(
17420            is_recovered_exported_class_base_type_node(base, source),
17421            "{}",
17422            tree.root_node().to_sexp()
17423        );
17424    }
17425
17426    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
17427        let mut identities = parsed
17428            .declarations()
17429            .iter()
17430            .filter(|unit| unit.is_function())
17431            .map(|unit| {
17432                (
17433                    unit.fq_name(),
17434                    unit.signature().unwrap_or_default().to_string(),
17435                )
17436            })
17437            .collect::<Vec<_>>();
17438        identities.sort();
17439        identities
17440    }
17441
17442    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
17443    /// prototype macros. Each case is parsed independently so a preceding
17444    /// malformed line cannot flatten the next declaration's fields into one
17445    /// cascading `ERROR`; that shape has no structural proof and must fail
17446    /// closed rather than fall back to a token scan. Parameter names are
17447    /// dropped from signatures just as they are for ordinary C prototypes.
17448    #[test]
17449    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
17450        let cases = [
17451            (
17452                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
17453                "pg_typemap_fit_to_result",
17454                "(VALUE, VALUE)",
17455            ),
17456            (
17457                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
17458                "pg_typemap_result_value",
17459                "(t_typemap *, VALUE, int, int)",
17460            ),
17461            (
17462                "void pg_typemap_mark _(( void * ));",
17463                "pg_typemap_mark",
17464                "(void *)",
17465            ),
17466            (
17467                "void init_pg_type_map _(( void ));",
17468                "init_pg_type_map",
17469                "(void)",
17470            ),
17471            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
17472            (
17473                "extern VALUE pg_extern _(( VALUE, VALUE ));",
17474                "pg_extern",
17475                "(VALUE, VALUE)",
17476            ),
17477            (
17478                "size_t pg_typemap_memsize _(( const void * ));",
17479                "pg_typemap_memsize",
17480                "(const void *)",
17481            ),
17482            (
17483                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
17484                "pg_wrap_socket_io",
17485                "(int, VALUE, VALUE *, int *)",
17486            ),
17487            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
17488            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
17489            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
17490        ];
17491
17492        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
17493            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
17494            assert_eq!(
17495                function_identities(&parsed),
17496                vec![(name.to_string(), signature.to_string())],
17497                "{source}: {:#?}",
17498                parsed.declarations()
17499            );
17500            assert!(
17501                parsed.declarations().iter().all(|unit| !unit.is_field()),
17502                "{source} must not retain the malformed field: {:#?}",
17503                parsed.declarations()
17504            );
17505        }
17506    }
17507
17508    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
17509    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
17510    /// it as a multiplication of a type by a call
17511    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
17512    /// wrapped in an `expression_statement` that `has_error()` only because
17513    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
17514    /// here with a clean preceding field so the prototype remains the
17515    /// parser-owned `expression_statement` this recovery requires.
17516    #[test]
17517    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
17518        let cases = [
17519            (
17520                "PGconn *pg_get_pgconn _(( VALUE ));",
17521                "pg_get_pgconn",
17522                "(VALUE)",
17523            ),
17524            (
17525                "PGresult* pgresult_get _(( VALUE ));",
17526                "pgresult_get",
17527                "(VALUE)",
17528            ),
17529        ];
17530        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
17531            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
17532            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
17533            assert_eq!(
17534                function_identities(&parsed),
17535                vec![(name.to_string(), signature.to_string())],
17536                "{source}: {:#?}",
17537                parsed.declarations()
17538            );
17539            assert_eq!(
17540                parsed
17541                    .declarations()
17542                    .iter()
17543                    .filter(|unit| unit.is_field())
17544                    .map(|unit| unit.fq_name())
17545                    .collect::<Vec<_>>(),
17546                vec!["rb_mPG".to_string()],
17547                "{source}: {:#?}",
17548                parsed.declarations()
17549            );
17550        }
17551    }
17552
17553    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
17554    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
17555    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
17556    /// regions. The witnessed declaration must still become a Function and
17557    /// its type must not survive as the name of a bogus Field. This test does
17558    /// not claim that structurally flattened neighboring prototypes are safe
17559    /// to recover.
17560    #[test]
17561    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
17562        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
17563VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
17564int pg_typemap_fit_to_copy_get                         _(( VALUE ));
17565VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
17566t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
17567VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
17568void pg_typemap_mark                                   _(( void * ));
17569size_t pg_typemap_memsize                              _(( const void * ));
17570void pg_typemap_compact                                _(( void * ));
17571
17572PGconn *pg_get_pgconn                                  _(( VALUE ));
17573t_pg_connection *pg_get_connection                     _(( VALUE ));
17574VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
17575#ifdef __GNUC__
17576__attribute__((format(printf, 3, 4)))
17577#endif
17578NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
17579VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
17580void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
17581
17582
17583VALUE pg_new_result                                    _(( PGresult *, VALUE ));
17584VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
17585PGresult* pgresult_get                                 _(( VALUE ));
17586VALUE pg_result_check                                  _(( VALUE ));
17587VALUE pg_result_clear                                  _(( VALUE ));
17588VALUE pg_tuple_new                                     _(( VALUE, int ));
17589
17590/*
17591 * Fetch the data pointer for the result object
17592 */
17593static inline t_pg_result *
17594pgresult_get_this( VALUE self )
17595{
17596	return RTYPEDDATA_DATA(self);
17597}
17598
17599
17600rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
17601const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
17602rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
17603
17604"#;
17605        let parsed = parse_cpp_declarations(source, "pg.h");
17606        assert!(
17607            function_identities(&parsed)
17608                .iter()
17609                .any(|(name, signature)| name == "pg_typemap_result_value"
17610                    && signature == "(t_typemap *, VALUE, int, int)"),
17611            "the real issue witness must be a Function with its C signature: {:#?}",
17612            parsed.declarations()
17613        );
17614        assert!(
17615            parsed
17616                .declarations()
17617                .iter()
17618                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
17619            "the issue witness must not leave its return type as a Field name: {:#?}",
17620            parsed.declarations()
17621        );
17622    }
17623
17624    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
17625    /// between `explicit` and a constructor name onto the name, producing a
17626    /// `qualified_identifier` whose `::` it had to invent. The declared member
17627    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
17628    /// macro in type position, was already recovered and is the control that
17629    /// both spellings agree.
17630    #[test]
17631    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
17632        let source = r#"class SIMD_4x26 final {
17633   public:
17634      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
17635      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
17636      int m_v;
17637};
17638"#;
17639        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
17640        assert_eq!(
17641            function_identities(&parsed),
17642            vec![
17643                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
17644                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
17645            ],
17646            "{:#?}",
17647            parsed.declarations()
17648        );
17649    }
17650
17651    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
17652    /// `ERROR` holding the wrapped declaration and every declaration after it
17653    /// until the parser recovers. All of them must be indexed, each with the
17654    /// byte range that spells it.
17655    #[test]
17656    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
17657        let source = r#"#include <cstdint>
17658struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
17659    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
17660                     struct llama_model * model,
17661              struct llama_context_params   params),
17662            "use llama_init_from_model instead");
17663    LLAMA_API int32_t llama_tokenize(
17664        const struct llama_vocab * vocab,
17665                      const char * text,
17666                            bool   parse_special);
17667    LLAMA_API int32_t llama_other(int a);
17668"#;
17669        let parsed = parse_cpp_declarations(source, "llama.h");
17670        assert_eq!(
17671            function_identities(&parsed),
17672            vec![
17673                (
17674                    "llama_new_context_with_model".to_string(),
17675                    "(struct llama_model *, struct llama_context_params)".to_string()
17676                ),
17677                ("llama_other".to_string(), "(int)".to_string()),
17678                (
17679                    "llama_tokenize".to_string(),
17680                    "(const struct llama_vocab *, const char *, bool)".to_string()
17681                ),
17682            ],
17683            "{:#?}",
17684            parsed.declarations()
17685        );
17686
17687        // Each recovered declaration owns the source that spells it, so
17688        // navigation lands on the declaration and not on the macro envelope.
17689        for (name, expected) in [
17690            (
17691                "llama_new_context_with_model",
17692                "LLAMA_API struct llama_context * llama_new_context_with_model(",
17693            ),
17694            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
17695            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
17696        ] {
17697            let unit = parsed
17698                .declarations()
17699                .iter()
17700                .find(|unit| unit.is_function() && unit.fq_name() == name)
17701                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
17702            let [range] = parsed.declaration_ranges(unit) else {
17703                panic!("{name} must have exactly one range");
17704            };
17705            let text = &source[range.start_byte..range.end_byte];
17706            assert!(
17707                text.starts_with(expected),
17708                "{name} range is {text:?}, expected it to start with {expected:?}"
17709            );
17710            assert!(
17711                text.ends_with(')') || text.ends_with(';'),
17712                "{name}: {text:?}"
17713            );
17714        }
17715    }
17716
17717    /// whisper.cpp's own `include/whisper.h` writes the same construct with the
17718    /// invocation's `(` at the end of its line, and the parser then keeps no
17719    /// invocation whole. It hands the first one's hint and its own `)` and `;`
17720    /// to the `expression_statement` beside it, and flattens the ones after
17721    /// that into bare `identifier`/`type_identifier`, `(`,
17722    /// `parameter_declaration`, `,` and `ERROR` parts of whatever node it is
17723    /// already inside. No part of that is a head the grouping can be read from,
17724    /// which is why the invocation is read from the token order instead
17725    /// (#3094).
17726    ///
17727    /// The identifiers are `library_*` at whisper's own lengths on purpose:
17728    /// tree-sitter prices error recovery partly by how much input a repair
17729    /// skips, and the same construct written with shorter names parses cleanly.
17730    #[test]
17731    fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
17732        let source = r#"    LIBRARY_DEPRECATED(
17733        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17734        "use library_init_from_file_with_params instead"
17735    );
17736    LIBRARY_DEPRECATED(
17737        LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
17738        "use library_init_from_buffer_with_params instead"
17739    );
17740    LIBRARY_DEPRECATED(
17741        LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
17742        "use library_init_with_params instead"
17743    );
17744"#;
17745        let mut parser = tree_sitter::Parser::new();
17746        parser
17747            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17748            .expect("C++ grammar");
17749        let tree = parser.parse(source, None).expect("C++ tree");
17750        let root = tree.root_node();
17751
17752        // The shape this recovery exists for: the second invocation is not a
17753        // node of its own, and the third is a run of bare siblings inside the
17754        // second one's `ERROR`.
17755        let mut cursor = root.walk();
17756        let items = root.children(&mut cursor).collect::<Vec<_>>();
17757        let [first, hint_statement, rest @ ..] = items.as_slice() else {
17758            panic!("{}", root.to_sexp());
17759        };
17760        assert_eq!(first.kind(), "ERROR");
17761        assert_eq!(hint_statement.kind(), "expression_statement");
17762        assert!(
17763            node_text(*first, source).ends_with(','),
17764            "the first invocation's own `)` and `;` are the statement's, not its own: {}",
17765            root.to_sexp()
17766        );
17767        let swallowing = rest
17768            .iter()
17769            .find(|item| item.kind() == "ERROR")
17770            .unwrap_or_else(|| panic!("{}", root.to_sexp()));
17771        let mut swallowing_cursor = swallowing.walk();
17772        let flattened = swallowing
17773            .children(&mut swallowing_cursor)
17774            .map(|child| child.kind())
17775            .collect::<Vec<_>>();
17776        assert_eq!(
17777            flattened,
17778            vec![
17779                "identifier",
17780                "(",
17781                "parameter_declaration",
17782                ",",
17783                "ERROR",
17784                "type_identifier",
17785                "(",
17786                "parameter_declaration",
17787                ",",
17788                "\"",
17789                "identifier",
17790                "identifier",
17791                "identifier",
17792                "\"",
17793                ")",
17794            ],
17795            "the third invocation must be flattened into the second one's node: {}",
17796            root.to_sexp()
17797        );
17798
17799        // Both are admitted, and each names the byte just past its own `;`.
17800        let ancestry = ParentIndex::new(root);
17801        let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
17802            .expect("the first invocation");
17803        assert_eq!(
17804            &source[..first_run.invocation_end],
17805            &source[..source.find("instead\"\n    );").expect("first hint")
17806                + "instead\"\n    );".len()]
17807        );
17808        assert_eq!(
17809            first_run.region_end, first_run.invocation_end,
17810            "the first invocation swallowed nothing, so the recovery owns only its own bytes"
17811        );
17812        let swallowing_run = collapsed_macro_declaration_run(*swallowing, source, &ancestry)
17813            .expect("the second invocation");
17814        assert!(
17815            swallowing_run.invocation_end < swallowing.end_byte(),
17816            "the second invocation swallowed the third"
17817        );
17818        assert_eq!(
17819            swallowing_run.region_end,
17820            root.end_byte(),
17821            "a swallowing invocation owns the region to the close of its declaration scope"
17822        );
17823
17824        let parsed = parse_cpp_declarations(source, "library.h");
17825        assert_eq!(
17826            function_identities(&parsed),
17827            vec![
17828                (
17829                    "library_init".to_string(),
17830                    "(struct library_model_loader *)".to_string()
17831                ),
17832                (
17833                    "library_init_from_buffer".to_string(),
17834                    "(void *, size_t)".to_string()
17835                ),
17836                (
17837                    "library_init_from_file".to_string(),
17838                    "(const char *)".to_string()
17839                ),
17840            ],
17841            "{:#?}",
17842            parsed.declarations()
17843        );
17844    }
17845
17846    /// The hand-off that keeps the scan finite. An invocation that reaches the
17847    /// end of its own declaration scope swallowed nothing, and the ordinary
17848    /// `macro_wrapped_declarations` reader has it. The scan reparses exactly
17849    /// that region for every invocation it finds, so admitting it here would
17850    /// hand the scan its own input back.
17851    #[test]
17852    fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
17853        let source = r#"LIBRARY_DEPRECATED(
17854        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17855        "use library_init_from_file_with_params instead"
17856    );"#;
17857        let mut parser = tree_sitter::Parser::new();
17858        parser
17859            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17860            .expect("C++ grammar");
17861        let tree = parser.parse(source, None).expect("C++ tree");
17862        let root = tree.root_node();
17863        let head = root.named_child(0).expect("the invocation");
17864        let ancestry = ParentIndex::new(root);
17865        assert!(
17866            collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
17867            "{}",
17868            root.to_sexp()
17869        );
17870        assert!(
17871            !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
17872            "the ordinary reader must be the one that has it: {}",
17873            root.to_sexp()
17874        );
17875        // The index is a speed substitution, never a semantic one: the
17876        // unindexed path is `Node::parent` itself and must agree.
17877        let unindexed = ParentIndex::unindexed();
17878        assert!(
17879            collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
17880            "indexed and unindexed climbs must agree"
17881        );
17882        assert_eq!(
17883            macro_wrapped_declarations(head, source, &ancestry).len(),
17884            macro_wrapped_declarations(head, source, &unindexed).len(),
17885            "indexed and unindexed climbs must agree"
17886        );
17887    }
17888
17889    /// Negative controls for the same recovery: a macro invocation whose
17890    /// arguments are not a declaration recovers nothing, whether the parser
17891    /// keeps it clean, reads the arguments as a type, or reads them as a bare
17892    /// declarator.
17893    #[test]
17894    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
17895        for source in [
17896            "int before;\nFOO(1, 2);\nint after;\n",
17897            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
17898            "int before;\nMACRO(int a, int b);\nint after;\n",
17899            "DECLARE_HANDLE(HWND);\nint after;\n",
17900        ] {
17901            let parsed = parse_cpp_declarations(source, "macro-call.h");
17902            assert_eq!(
17903                function_identities(&parsed),
17904                Vec::new(),
17905                "{source:?} must declare no function: {:#?}",
17906                parsed.declarations()
17907            );
17908        }
17909    }
17910
17911    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
17912    /// makes tree-sitter read the macro as the member's type and its argument
17913    /// list as a parenthesized declarator, which then swallows the attributed
17914    /// member and the member written after it. Both are members.
17915    #[test]
17916    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
17917        let source = r#"#include <string_view>
17918namespace Botan {
17919class DL_Group final {
17920   public:
17921      DL_Group() = default;
17922      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17923      DL_Group(std::string_view pem, int format);
17924      size_t get_p() const;
17925};
17926}
17927"#;
17928        let parsed = parse_cpp_declarations(source, "dl_group.h");
17929        assert_eq!(
17930            function_identities(&parsed),
17931            vec![
17932                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
17933                (
17934                    "Botan.DL_Group.DL_Group".to_string(),
17935                    "(std::string_view)".to_string()
17936                ),
17937                (
17938                    "Botan.DL_Group.DL_Group".to_string(),
17939                    "(std::string_view, int)".to_string()
17940                ),
17941                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17942            ],
17943            "{:#?}",
17944            parsed.declarations()
17945        );
17946    }
17947
17948    /// #2552 shape 3, export-macro class. The class head macro makes the body a
17949    /// `compound_statement`, so members come from the region reparse, and one
17950    /// string-attribute member used to make that reparse unindexable -- which
17951    /// left the class with no members at all.
17952    #[test]
17953    fn an_export_macro_class_keeps_its_string_attribute_members() {
17954        let source = r#"#include <string_view>
17955namespace Botan {
17956class BOTAN_PUBLIC_API(2, 0) DL_Group final {
17957   public:
17958      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17959      DL_Group(std::string_view pem, int format);
17960      size_t get_p() const;
17961};
17962}
17963"#;
17964        let parsed = parse_cpp_declarations(source, "dl_group.h");
17965        assert!(
17966            parsed
17967                .declarations()
17968                .iter()
17969                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
17970            "{:#?}",
17971            parsed.declarations()
17972        );
17973        assert_eq!(
17974            function_identities(&parsed),
17975            vec![
17976                (
17977                    "Botan.DL_Group.DL_Group".to_string(),
17978                    "(std::string_view)".to_string()
17979                ),
17980                (
17981                    "Botan.DL_Group.DL_Group".to_string(),
17982                    "(std::string_view, int)".to_string()
17983                ),
17984                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17985            ],
17986            "{:#?}",
17987            parsed.declarations()
17988        );
17989    }
17990
17991    /// #2552 shape 3, the `= default` variant plus the access-label
17992    /// constructor. The attributed defaulted constructor separates cleanly, but
17993    /// it strands the next member in a bare `ERROR`, and a constructor written
17994    /// with a member-initializer list under `private:` dissolves into
17995    /// expression soup that the reparse recovers from its own source range.
17996    #[test]
17997    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
17998        let source = r#"namespace Botan {
17999class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
18000   public:
18001      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
18002      XMSS_Parameters(int oid, int len);
18003      size_t len() const;
18004
18005   private:
18006      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
18007            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
18008
18009      int m_oid;
18010      int m_wots_oid;
18011      size_t m_element_size;
18012      size_t m_tree_height;
18013};
18014}
18015"#;
18016        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
18017        let constructors = function_identities(&parsed)
18018            .into_iter()
18019            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
18020            .map(|(_, signature)| signature)
18021            .collect::<Vec<_>>();
18022        assert_eq!(
18023            constructors,
18024            vec![
18025                "()".to_string(),
18026                "(int, int)".to_string(),
18027                "(int, int, size_t, size_t)".to_string(),
18028            ],
18029            "{:#?}",
18030            parsed.declarations()
18031        );
18032    }
18033
18034    /// Negative control for the same rule: a real qualified name spells its
18035    /// `::` in the source, so the separator is present rather than MISSING and
18036    /// the out-of-line definition keeps its owner.
18037    #[test]
18038    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
18039        let source = r#"namespace shell {
18040struct Outer {
18041   struct Inner {
18042      Inner(int v);
18043      void run(int v);
18044   };
18045};
18046Outer::Inner::Inner(int v) {}
18047void Outer::Inner::run(int v) {}
18048}
18049"#;
18050        let parsed = parse_cpp_declarations(source, "outer.cpp");
18051        let names = function_identities(&parsed)
18052            .into_iter()
18053            .map(|(fq_name, _)| fq_name)
18054            .collect::<Vec<_>>();
18055        assert!(
18056            names
18057                .iter()
18058                .all(|name| name.starts_with("shell.Outer$Inner.")),
18059            "{names:#?}"
18060        );
18061    }
18062
18063    #[test]
18064    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
18065        let source = r#"namespace control {
18066template <typename T>
18067class AnySpan;
18068template <typename T>
18069class ABSL_ATTRIBUTE_VIEW AnySpan {
18070 public:
18071  int begin() const;
18072};
18073}
18074
18075namespace absl {
18076ABSL_NAMESPACE_BEGIN
18077template <typename T>
18078class ABSL_ATTRIBUTE_VIEW Span {
18079 public:
18080  int begin() const;
18081  int back() const;
18082};
18083
18084int begin();
18085int back();
18086}
18087"#;
18088        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
18089        let declarations = parsed.declarations();
18090        assert!(
18091            declarations
18092                .iter()
18093                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
18094        );
18095        for method in ["begin", "back"] {
18096            assert!(declarations.iter().any(|unit| {
18097                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
18098            }));
18099            assert!(
18100                declarations.iter().any(|unit| {
18101                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
18102                })
18103            );
18104        }
18105        assert!(
18106            declarations
18107                .iter()
18108                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
18109        );
18110        assert!(
18111            declarations
18112                .iter()
18113                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
18114        );
18115        assert!(
18116            declarations
18117                .iter()
18118                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
18119        );
18120    }
18121
18122    #[test]
18123    fn explicit_global_member_definition_has_canonical_package_boundary() {
18124        let source = r#"
18125namespace arangodb::aql {
18126class ExecutionPlan {
18127 public:
18128  template<class... Args> Node* createNode(Args&&... args);
18129};
18130}
18131
18132template<class... Args>
18133Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
18134"#;
18135        let parsed = parse_cpp_declarations(source, "global-member.cpp");
18136
18137        assert!(parsed.declarations().iter().any(|unit| {
18138            unit.is_function()
18139                && unit.package_name() == "arangodb::aql"
18140                && unit.short_name() == "ExecutionPlan.createNode"
18141                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
18142        }));
18143    }
18144
18145    #[test]
18146    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
18147        let source = r#"
18148#ifndef TINYXML2_INCLUDED
18149#define TINYXML2_INCLUDED
18150namespace tinyxml2 {
18151class TINYXML2_LIB XMLUtil {
18152 public:
18153  static const char* SkipWhiteSpace(const char* p) {
18154    while (*p) {
18155      if (*p == ' ') {
18156        ++p;
18157      }
18158    }
18159    return p;
18160  }
18161  static bool StringEqual(const char* p, const char* q) {
18162    return p == q;
18163  }
18164  class TINYXML2_LIB Helper {
18165   public:
18166    void Touch();
18167  };
18168  static void ToStr(int value, char* buffer);
18169 private:
18170  static const char* writeBoolTrue;
18171};
18172
18173class TINYXML2_LIB XMLNode {
18174 public:
18175  virtual XMLNode* ShallowClone() const = 0;
18176  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
18177};
18178}
18179#endif
18180"#;
18181        let mut parser = tree_sitter::Parser::new();
18182        parser
18183            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18184            .unwrap();
18185        let tree = parser.parse(source, None).unwrap();
18186        let mut boundary_found = false;
18187        walk_named_tree_preorder(tree.root_node(), true, |node| {
18188            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
18189                && name == "XMLUtil"
18190            {
18191                boundary_found = fragmented_export_sibling_class_boundary(node, source)
18192                    .and_then(|boundary| {
18193                        recover_exported_class_function_definition(boundary, source)
18194                    })
18195                    .is_some_and(|(_, name, _)| name == "XMLNode");
18196            }
18197            WalkControl::Continue
18198        });
18199        assert!(
18200            boundary_found,
18201            "fixture must exercise the recovered sibling boundary"
18202        );
18203
18204        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
18205        assert!(
18206            parsed
18207                .declarations()
18208                .iter()
18209                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
18210            "{:#?}",
18211            parsed.declarations()
18212        );
18213        assert!(
18214            parsed
18215                .declarations()
18216                .iter()
18217                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
18218            "{:#?}",
18219            parsed.declarations()
18220        );
18221        assert!(parsed.declarations().iter().any(|unit| {
18222            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
18223        }));
18224        assert!(
18225            parsed
18226                .declarations()
18227                .iter()
18228                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
18229        );
18230        assert!(
18231            parsed
18232                .declarations()
18233                .iter()
18234                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
18235        );
18236    }
18237
18238    #[test]
18239    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
18240        // Clang's diagnostic suite intentionally contains this ill-formed
18241        // spelling. The analyzer must retain the parser's explicit-global AST
18242        // boundary instead of constructing `cwg311::::cwg311::X`.
18243        let parsed = parse_cpp_declarations(
18244            r#"
18245namespace cwg311 {
18246namespace X { namespace Y {} }
18247namespace ::cwg311::X {}
18248}
18249"#,
18250            "explicit-global-namespace.cpp",
18251        );
18252
18253        assert!(parsed.declarations().iter().any(|unit| {
18254            unit.kind() == CodeUnitType::Module
18255                && unit.short_name() == "cwg311::X"
18256                && unit.fq_name() == "cwg311::X"
18257        }));
18258        assert!(
18259            parsed
18260                .declarations()
18261                .iter()
18262                .all(|unit| !unit.short_name().contains("::::")),
18263            "recovered namespace names must not retain empty scope components: {:#?}",
18264            parsed.declarations()
18265        );
18266    }
18267
18268    #[test]
18269    fn repeated_scope_separator_does_not_create_empty_function_owner() {
18270        let scope = ScopeInfo {
18271            package_name: "X".to_string(),
18272            module: None,
18273            class_unit: None,
18274            template_signature: None,
18275            template_metadata: None,
18276            declarations_are_fields: false,
18277            recovered_specialization_member_scope: false,
18278            visible_using_namespaces: Vec::new(),
18279        };
18280
18281        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
18282
18283        assert!(owner.is_none());
18284        assert_eq!(name, "doit");
18285        assert_eq!(package, "X");
18286    }
18287
18288    #[test]
18289    fn trailing_decltype_expression_is_not_a_function_declarator() {
18290        let source = r#"
18291namespace boost { namespace detail {
18292#if ! defined(BOOST_NO_SFINAE_EXPR) && \
18293    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
18294    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
18295#define BOOST_THREAD_PROVIDES_INVOKE
18296#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
18297template <class Fp, class A0, class ...Args>
18298inline auto
18299invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
18300       BOOST_THREAD_RV_REF(Args) ...args)
18301    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
18302{
18303    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
18304}
18305#endif
18306#endif
18307}}
18308"#;
18309        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
18310
18311        assert!(
18312            parsed
18313                .declarations()
18314                .iter()
18315                .all(|unit| unit.short_name() != ".*f")
18316        );
18317    }
18318
18319    fn find_class_named<'tree>(
18320        root: Node<'tree>,
18321        source: &str,
18322        expected_name: &str,
18323    ) -> Option<Node<'tree>> {
18324        let mut stack = vec![root];
18325        while let Some(node) = stack.pop() {
18326            if node.kind() == "class_specifier"
18327                && node
18328                    .child_by_field_name("name")
18329                    .is_some_and(|name| node_text(name, source) == expected_name)
18330            {
18331                return Some(node);
18332            }
18333            let mut cursor = node.walk();
18334            stack.extend(node.named_children(&mut cursor));
18335        }
18336        None
18337    }
18338
18339    #[test]
18340    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
18341        let source = r#"EXPORT void definition(struct Value value) {}
18342EXPORT void prototype(struct Value value);
18343"#;
18344        let mut parser = tree_sitter::Parser::new();
18345        parser
18346            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18347            .unwrap();
18348        let tree = parser.parse(source, None).unwrap();
18349        let root = tree.root_node();
18350        let mut cursor = root.walk();
18351        let callables = root
18352            .named_children(&mut cursor)
18353            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
18354            .collect::<Vec<_>>();
18355
18356        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
18357        for callable in callables {
18358            assert!(callable.has_error(), "fixture must exercise error recovery");
18359            assert!(
18360                cpp_sentinel_macro_parts(callable, source).is_none(),
18361                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
18362            );
18363        }
18364    }
18365
18366    #[test]
18367    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
18368        let source = r#"namespace absl {
18369ABSL_NAMESPACE_BEGIN
18370// Generate a floating-point variate conforming to a Beta distribution:
18371template <typename RealType = double>
18372class beta_distribution {
18373 public:
18374  using result_type = RealType;
18375
18376
18377  beta_distribution() : beta_distribution(1) {}
18378
18379  explicit beta_distribution(result_type alpha, result_type beta = 1)
18380      : param_(alpha, beta) {}
18381
18382  explicit beta_distribution(const param_type& p) : param_(p) {}
18383
18384  void reset() {}
18385
18386  // Generating functions
18387  template <typename URBG>
18388  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
18389    return (*this)(g, param_);
18390  }
18391
18392};
18393ABSL_NAMESPACE_END
18394}  // namespace absl
18395"#;
18396        let mut parser = tree_sitter::Parser::new();
18397        parser
18398            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18399            .unwrap();
18400        let tree = parser.parse(source, None).unwrap();
18401        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18402        let body = namespace
18403            .child_by_field_name("body")
18404            .expect("fixture namespace body");
18405        let sentinel = body.named_child(0).expect("sentinel envelope");
18406        let callable = sentinel
18407            .child_by_field_name("declarator")
18408            .and_then(extract_function_declarator)
18409            .and_then(cpp_function_declarator_name_node)
18410            .expect("preserved callable name");
18411
18412        assert_eq!(sentinel.kind(), "function_definition");
18413        assert_eq!(callable.kind(), "operator_name");
18414        assert!(
18415            cpp_sentinel_macro_parts(sentinel, source).is_some(),
18416            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
18417        );
18418    }
18419
18420    #[test]
18421    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
18422        let source = r#"namespace absl {
18423ABSL_NAMESPACE_BEGIN
18424// absl::discrete_distribution
18425//
18426// A discrete distribution produces random integers i, where 0 <= i < n
18427template <typename IntType = int>
18428class discrete_distribution {
18429 public:
18430  using result_type = IntType;
18431  class param_type {
18432   public:
18433    param_type() { init(); }
18434    template <typename InputIterator>
18435    explicit param_type(InputIterator begin, InputIterator end)
18436        : p_(begin, end) {
18437      init();
18438    }
18439  };
18440  discrete_distribution() : param_() {}
18441  explicit discrete_distribution(const param_type& p) : param_(p) {}
18442};
18443ABSL_NAMESPACE_END
18444}  // namespace absl
18445"#;
18446        let mut parser = tree_sitter::Parser::new();
18447        parser
18448            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18449            .unwrap();
18450        let tree = parser.parse(source, None).unwrap();
18451        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18452        let body = namespace
18453            .child_by_field_name("body")
18454            .expect("fixture namespace body");
18455        let sentinel = body.named_child(0).expect("sentinel envelope");
18456        let callable = sentinel
18457            .child_by_field_name("declarator")
18458            .and_then(extract_function_declarator)
18459            .and_then(cpp_function_declarator_name_node)
18460            .expect("preserved callable name");
18461
18462        assert_eq!(sentinel.kind(), "function_definition");
18463        assert_eq!(callable.kind(), "identifier");
18464        assert!(
18465            cpp_sentinel_macro_parts(sentinel, source).is_some(),
18466            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
18467        );
18468    }
18469
18470    #[test]
18471    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
18472        let source = r#"
18473#define CPPCHECKLIB
18474class Library {
18475    struct Container {
18476        CPPCHECKLIB static std::string toString(Yield yield);
18477        CPPCHECKLIB static std::string toString(Action action);
18478    };
18479};
18480"#;
18481        let mut parser = tree_sitter::Parser::new();
18482        parser
18483            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18484            .unwrap();
18485        let tree = parser.parse(source, None).unwrap();
18486        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
18487        let parsed = parse_cpp_file(&file, source, &tree);
18488        assert!(
18489            parsed
18490                .declarations()
18491                .iter()
18492                .all(|unit| unit.fq_name() != "Library$Container.std"),
18493            "the qualified return-type namespace must not become a field: {:#?}",
18494            parsed.declarations()
18495        );
18496        for expected in ["(Yield)", "(Action)"] {
18497            assert!(
18498                parsed.declarations().iter().any(|unit| {
18499                    unit.is_function()
18500                        && unit.fq_name() == "Library$Container.toString"
18501                        && unit.signature() == Some(expected)
18502                }),
18503                "recovered toString overload {expected} is missing: {:#?}",
18504                parsed.declarations()
18505            );
18506        }
18507    }
18508
18509    #[test]
18510    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
18511        let source = r#"
18512#define SIMPLECPP_LIB
18513namespace simplecpp {
18514using TokenString = std::string;
18515struct Location { int line{}; };
18516class SIMPLECPP_LIB Token {
18517  TokenString prefix;
18518  void prefix_method() {}
18519 public:
18520  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18521      whitespaceahead(wsahead), location(loc), string(s)
18522      // The comment must not hide the constructor body from recovery.
18523      {
18524      flags();
18525  }
18526  TokenString string;
18527  bool whitespaceahead;
18528  Location location;
18529  Token *previous{};
18530 private:
18531  void flags() {
18532      whitespaceahead = true;
18533  }
18534};
18535}
18536"#;
18537        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
18538
18539        let location_fields = parsed
18540            .declarations()
18541            .iter()
18542            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
18543            .collect::<Vec<_>>();
18544        assert_eq!(
18545            location_fields.len(),
18546            1,
18547            "location should have one class-owned declaration: {:#?}",
18548            parsed.declarations()
18549        );
18550        assert!(
18551            location_fields[0].is_field(),
18552            "location has wrong kind: {:#?}",
18553            parsed.declarations()
18554        );
18555        assert!(
18556            parsed.declarations().iter().all(|unit| {
18557                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
18558            })
18559        );
18560        assert!(
18561            parsed.declarations().iter().all(|unit| {
18562                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18563            })
18564        );
18565        assert!(
18566            parsed
18567                .declarations()
18568                .iter()
18569                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
18570        );
18571        assert!(
18572            parsed
18573                .declarations()
18574                .iter()
18575                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
18576            "the recovered class must retain its constructor: {:#?}",
18577            parsed.declarations()
18578        );
18579        assert!(
18580            parsed
18581                .declarations()
18582                .iter()
18583                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
18584        );
18585        assert!(parsed.declarations().iter().any(|unit| {
18586            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
18587        }));
18588        let constructor = parsed
18589            .declarations()
18590            .iter()
18591            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
18592            .expect("recovered constructor");
18593        let constructor_start = source.find("Token(const").expect("constructor start");
18594        let constructor_end = source
18595            .get(
18596                ..source
18597                    .find("  TokenString string;")
18598                    .expect("constructor end"),
18599            )
18600            .expect("constructor slice")
18601            .trim_end()
18602            .len();
18603        assert!(
18604            parsed
18605                .navigation_ranges
18606                .get(constructor)
18607                .is_some_and(|ranges| {
18608                    ranges.iter().any(|range| {
18609                        range.start_byte == constructor_start && range.end_byte == constructor_end
18610                    })
18611                }),
18612            "constructor navigation must span the full body: {:#?}",
18613            parsed.navigation_ranges
18614        );
18615        assert_eq!(
18616            parsed
18617                .signature_metadata
18618                .get(constructor)
18619                .and_then(|metadata| metadata.first())
18620                .and_then(SignatureMetadata::callable_linkage),
18621            Some(CallableLinkage::External)
18622        );
18623        let token_class = parsed
18624            .declarations()
18625            .iter()
18626            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
18627            .expect("recovered Token class");
18628        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
18629        assert!(
18630            parsed
18631                .navigation_ranges
18632                .get(token_class)
18633                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
18634            "class navigation must include the terminating semicolon: {:#?}",
18635            parsed.navigation_ranges
18636        );
18637    }
18638
18639    #[test]
18640    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
18641        let source = r#"
18642#define SIMPLECPP_LIB
18643namespace simplecpp {
18644using TokenString = std::string;
18645class Macro;
18646struct Location {
18647  unsigned int fileIndex{};
18648  unsigned int line{};
18649  unsigned int col{};
18650};
18651struct Output {
18652  int type;
18653};
18654class SIMPLECPP_LIB Token {
18655 public:
18656  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18657      whitespaceahead(wsahead), location(loc), string(s) {
18658      flags();
18659  }
18660  Token(const Token &tok) :
18661      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
18662      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
18663      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
18664  Token &operator=(const Token &tok) = delete;
18665  const TokenString& str() const { return string; }
18666  void setstr(const std::string &s) { string = s; flags(); }
18667  bool isOneOf(const char ops[]) const;
18668  TokenString macro;
18669  char op;
18670  bool comment;
18671  bool name;
18672  bool number;
18673  bool whitespaceahead;
18674  Location location;
18675  Token *previous{};
18676  Token *next{};
18677 private:
18678  void flags() {
18679      name = !string.empty();
18680      comment = false;
18681      number = false;
18682      op = 0;
18683  }
18684  TokenString string;
18685};
18686}
18687struct Following {
18688  int type;
18689};
18690class SIMPLECPP_LIB Later {
18691 public:
18692  Later(int value) : value(value) {}
18693  int value;
18694};
18695"#;
18696        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
18697        assert!(
18698            parsed
18699                .declarations()
18700                .iter()
18701                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
18702        );
18703        assert!(
18704            !parsed
18705                .declarations()
18706                .iter()
18707                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
18708        );
18709        assert!(
18710            parsed
18711                .declarations()
18712                .iter()
18713                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
18714        );
18715        assert!(
18716            !parsed
18717                .declarations()
18718                .iter()
18719                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18720        );
18721        assert!(
18722            parsed
18723                .declarations()
18724                .iter()
18725                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
18726        );
18727        assert!(
18728            parsed
18729                .declarations()
18730                .iter()
18731                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
18732        );
18733        assert!(
18734            parsed
18735                .declarations()
18736                .iter()
18737                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
18738        );
18739        assert!(
18740            parsed
18741                .declarations()
18742                .iter()
18743                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
18744        );
18745        assert!(
18746            parsed
18747                .declarations()
18748                .iter()
18749                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
18750        );
18751        assert!(
18752            parsed
18753                .declarations()
18754                .iter()
18755                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
18756        );
18757        assert!(parsed.declarations().iter().all(|unit| {
18758            !matches!(
18759                unit.fq_name().as_str(),
18760                "simplecpp.Token.Following" | "simplecpp.Token.Later"
18761            )
18762        }));
18763        assert!(
18764            !parsed
18765                .declarations()
18766                .iter()
18767                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
18768            "the following struct must remain outside the recovered Token class"
18769        );
18770    }
18771
18772    #[test]
18773    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
18774        let source = r#"
18775#define SIMPLECPP_LIB
18776namespace {
18777namespace simplecpp {
18778using TokenString = std::string;
18779struct Location { int line{}; };
18780class SIMPLECPP_LIB HiddenToken {
18781 public:
18782  HiddenToken(const TokenString &s, const Location &loc) :
18783      location(loc), string(s) {
18784      flags();
18785  }
18786  TokenString string;
18787  Location location;
18788  HiddenToken *previous{};
18789 private:
18790  void flags() {}
18791};
18792}
18793}
18794"#;
18795        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
18796        let constructor = parsed
18797            .declarations()
18798            .iter()
18799            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
18800            .expect("recovered anonymous-namespace constructor");
18801        assert_eq!(
18802            parsed
18803                .signature_metadata
18804                .get(constructor)
18805                .and_then(|metadata| metadata.first())
18806                .and_then(SignatureMetadata::callable_linkage),
18807            Some(CallableLinkage::Internal)
18808        );
18809    }
18810
18811    #[test]
18812    fn macro_qualified_static_field_keeps_real_declarator() {
18813        let source = r#"#define JSON_INLINE_VARIABLE
18814struct Reader {
18815static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
18816static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
18817static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
18818};"#;
18819        let mut parser = tree_sitter::Parser::new();
18820        parser
18821            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18822            .unwrap();
18823        let tree = parser.parse(source, None).unwrap();
18824        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
18825        let parsed = parse_cpp_file(&file, source, &tree);
18826        for expected in [
18827            "Reader.npos",
18828            "Reader.other",
18829            "Reader.pointer",
18830            "Reader.reference",
18831        ] {
18832            assert!(
18833                parsed
18834                    .declarations()
18835                    .iter()
18836                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
18837                "real macro-decorated field {expected} is missing: {:#?}",
18838                parsed.declarations()
18839            );
18840        }
18841        assert!(
18842            parsed
18843                .declarations()
18844                .iter()
18845                .all(|unit| unit.fq_name() != "Reader.std"),
18846            "qualified type prefix became a pseudo-field: {:#?}",
18847            parsed.declarations()
18848        );
18849        let root = tree.root_node();
18850        let mut stack = vec![root];
18851        let mut signatures = Vec::new();
18852        while let Some(current) = stack.pop() {
18853            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
18854            {
18855                signatures.extend(
18856                    declarators
18857                        .into_iter()
18858                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
18859                );
18860            }
18861            let mut cursor = current.walk();
18862            stack.extend(current.named_children(&mut cursor));
18863        }
18864        signatures.sort();
18865        assert_eq!(
18866            signatures,
18867            [
18868                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
18869                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
18870                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
18871                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
18872            ]
18873        );
18874    }
18875
18876    fn member_function_linkage(source: &str) -> CallableLinkage {
18877        let mut parser = tree_sitter::Parser::new();
18878        parser
18879            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18880            .unwrap();
18881        let tree = parser.parse(source, None).unwrap();
18882        let ancestry = ParentIndex::new(tree.root_node());
18883        let mut stack = vec![tree.root_node()];
18884        while let Some(node) = stack.pop() {
18885            if node.kind() == "function_definition" {
18886                let mut current = node.parent();
18887                while let Some(parent) = current {
18888                    if matches!(
18889                        parent.kind(),
18890                        "class_specifier" | "struct_specifier" | "union_specifier"
18891                    ) {
18892                        return cpp_callable_linkage(node, source, &ancestry);
18893                    }
18894                    current = parent.parent();
18895                }
18896            }
18897            let mut cursor = node.walk();
18898            stack.extend(node.named_children(&mut cursor));
18899        }
18900        panic!("fixture has no member function definition");
18901    }
18902
18903    #[test]
18904    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
18905        assert_eq!(
18906            member_function_linkage("struct Named { int method() { return 1; } };"),
18907            CallableLinkage::External
18908        );
18909        assert_eq!(
18910            member_function_linkage(
18911                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
18912            ),
18913            CallableLinkage::Internal
18914        );
18915        assert_eq!(
18916            member_function_linkage("struct { int method() { return 1; } } instance;"),
18917            CallableLinkage::Internal
18918        );
18919        assert_eq!(
18920            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
18921            CallableLinkage::Internal
18922        );
18923    }
18924
18925    #[test]
18926    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
18927        let source = r#"
18928#ifndef PROTON_VALUE_HPP
18929#define PROTON_VALUE_HPP
18930namespace proton {
18931namespace internal {
18932class value_base {
18933  protected:
18934    internal::data& data();
18935    internal::data data_;
18936  friend class codec::encoder;
18937  friend class codec::decoder;
18938};
18939}
18940class value : public internal::value_base, private internal::comparable<value> {
18941  private:
18942    template<class T, class U=void> struct assignable :
18943        public std::enable_if<codec::is_encodable<T>::value, U> {};
18944    template<class U> struct assignable<value, U> {};
18945  public:
18946    PN_CPP_EXTERN value();
18947    PN_CPP_EXTERN value(const value&);
18948    PN_CPP_EXTERN value& operator=(const value&);
18949    PN_CPP_EXTERN value(value&&);
18950    PN_CPP_EXTERN value& operator=(value&&);
18951    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
18952    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
18953        codec::encoder e(*this);
18954        e << x;
18955        return *this;
18956    }
18957    PN_CPP_EXTERN type_id type() const;
18958    PN_CPP_EXTERN bool empty() const;
18959    PN_CPP_EXTERN void clear();
18960    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
18961    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
18962  friend PN_CPP_EXTERN void swap(value&, value&);
18963  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
18964  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
18965  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
18966    value(pn_data_t* d);
18967    void reset(pn_data_t* d = 0);
18968};
18969}
18970#endif
18971"#;
18972        let mut parser = tree_sitter::Parser::new();
18973        parser
18974            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18975            .unwrap();
18976        let tree = parser.parse(source, None).unwrap();
18977        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
18978        let parsed = parse_cpp_file(&file, source, &tree);
18979        let macro_constructors = parsed
18980            .signature_metadata
18981            .iter()
18982            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
18983            .flat_map(|(_, metadata)| metadata)
18984            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
18985            .collect::<Vec<_>>();
18986
18987        assert_eq!(
18988            macro_constructors.len(),
18989            3,
18990            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
18991            parsed.declarations()
18992        );
18993        assert!(
18994            macro_constructors.iter().all(|metadata| {
18995                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
18996            }),
18997            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
18998        );
18999    }
19000
19001    #[test]
19002    fn recovered_export_class_typedef_uses_displaced_alias_name() {
19003        let source = r#"
19004namespace spi {
19005class Filter {
19006public:
19007    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
19008};
19009}
19010namespace filter {
19011class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
19012{
19013public:
19014    typedef spi::Filter BASE_CLASS;
19015    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
19016    BEGIN_LOG4CXX_CAST_MAP()
19017    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
19018    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
19019    END_LOG4CXX_CAST_MAP()
19020    FilterDecision decide() const;
19021};
19022}
19023"#;
19024        let mut parser = tree_sitter::Parser::new();
19025        parser
19026            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19027            .unwrap();
19028        let tree = parser.parse(source, None).unwrap();
19029        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
19030        let parsed = parse_cpp_file(&file, source, &tree);
19031        assert!(
19032            parsed.declarations().iter().any(|unit| {
19033                unit.is_class()
19034                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
19035                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
19036            }),
19037            "the displaced typedef alias must retain its declared name: {:#?}",
19038            parsed.declarations()
19039        );
19040        assert!(
19041            parsed
19042                .declarations()
19043                .iter()
19044                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
19045            "the qualified underlying type must not become a false nested alias: {:#?}",
19046            parsed.declarations()
19047        );
19048    }
19049
19050    #[test]
19051    fn exported_single_base_recovery_uses_displaced_class_name() {
19052        let source = r#"
19053class CORE_EXPORT QgsPoint : public AbstractGeometry
19054{
19055    Q_GADGET
19056
19057    Q_PROPERTY( double x READ x WRITE setX )
19058    Q_PROPERTY( double y READ y WRITE setY )
19059    Q_PROPERTY( double z READ z WRITE setZ )
19060    Q_PROPERTY( double m READ m WRITE setM )
19061
19062  public:
19063#ifndef SIP_RUN
19064    QgsPoint(
19065      double x = std::numeric_limits<double>::quiet_NaN(),
19066      double y = std::numeric_limits<double>::quiet_NaN(),
19067      double z = std::numeric_limits<double>::quiet_NaN(),
19068      double m = std::numeric_limits<double>::quiet_NaN(),
19069      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
19070    );
19071#else
19072    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 )];
19073    % MethodCode
19074    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
19075    {
19076      int state;
19077      sipIsErr = 0;
19078      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
19079      if ( !sipIsErr )
19080      {
19081        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
19082      }
19083      sipReleaseType( p, sipType_QgsPointXY, state );
19084    }
19085    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
19086    {
19087      int state;
19088      sipIsErr = 0;
19089
19090      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
19091      if ( !sipIsErr )
19092      {
19093        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
19094      }
19095      sipReleaseType( p, sipType_QPointF, state );
19096    }
19097    else if (
19098      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
19099      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
19100      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
19101      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
19102    {
19103      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
19104      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
19105      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
19106      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
19107      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
19108      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
19109    }
19110    else // Invalid ctor arguments
19111    {
19112      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
19113      sipIsErr = 1;
19114    }
19115    % End
19116#endif
19117
19118    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
19119    explicit QgsPoint( QPointF p ) SIP_SKIP;
19120    explicit QgsPoint(
19121      Qgis::WkbType wkbType,
19122      double x = std::numeric_limits<double>::quiet_NaN(),
19123      double y = std::numeric_limits<double>::quiet_NaN(),
19124      double z = std::numeric_limits<double>::quiet_NaN(),
19125      double m = std::numeric_limits<double>::quiet_NaN()
19126    ) SIP_SKIP;
19127    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19128    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
19129    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19130#ifndef SIP_RUN
19131  private:
19132    bool fuzzyHelper(
19133      double epsilon,
19134      const AbstractGeometry &other,
19135      bool is3DFlag,
19136      bool isMeasureFlag
19137    ) const
19138    {
19139      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
19140    }
19141#endif
19142};
19143class Ordinary : public Base { public: Ordinary(); };
19144class API_EXPORT Plain { public: Plain(); };
19145class API_EXPORT : public Base {};
19146class
19147PN_CPP_CLASS_EXTERN Sender : public Link {
19148    Sender();
19149    struct impl;
19150    struct impl& get_impl() const;
19151};
19152class thread_ctx_t {};
19153class ctx_t ZMQ_FINAL : public thread_ctx_t {
19154    bool start();
19155};
19156"#;
19157        let mut parser = tree_sitter::Parser::new();
19158        parser
19159            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19160            .unwrap();
19161        let tree = parser.parse(source, None).unwrap();
19162        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
19163        let parsed = parse_cpp_file(&file, source, &tree);
19164        let declarations = parsed.declarations();
19165
19166        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
19167            assert!(
19168                declarations
19169                    .iter()
19170                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
19171                "missing recovered class {expected}: {declarations:#?}"
19172            );
19173        }
19174        let qgs_point = declarations
19175            .iter()
19176            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
19177            .expect("recovered QgsPoint class");
19178        assert_eq!(
19179            parsed.raw_supertypes.get(qgs_point),
19180            Some(&vec!["AbstractGeometry".to_string()]),
19181            "single-base export recovery must retain its displaced base"
19182        );
19183        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
19184        assert!(
19185            parsed
19186                .navigation_ranges
19187                .get(qgs_point)
19188                .is_some_and(|ranges| {
19189                    !ranges.is_empty()
19190                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
19191                }),
19192            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
19193            parsed.navigation_ranges.get(qgs_point)
19194        );
19195        let sender = declarations
19196            .iter()
19197            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
19198            .expect("recovered Sender class");
19199        assert_eq!(
19200            parsed.raw_supertypes.get(sender),
19201            Some(&vec!["Link".to_string()]),
19202            "post-declarator export recovery must retain its displaced base"
19203        );
19204        let recovered_member = declarations
19205            .iter()
19206            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
19207            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
19208        assert_eq!(
19209            parsed
19210                .signature_metadata
19211                .get(recovered_member)
19212                .and_then(|metadata| metadata.first())
19213                .and_then(SignatureMetadata::callable_linkage),
19214            Some(CallableLinkage::External),
19215            "a named recovered class's members have external linkage"
19216        );
19217        let ctx = declarations
19218            .iter()
19219            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
19220            .expect("recovered ctx_t class");
19221        assert_eq!(
19222            parsed.raw_supertypes.get(ctx),
19223            Some(&vec!["thread_ctx_t".to_string()]),
19224            "postfix export-macro recovery must retain its displaced base"
19225        );
19226        assert!(
19227            declarations.iter().any(|unit| {
19228                unit.is_function()
19229                    && unit.fq_name() == "QgsPoint.QgsPoint"
19230                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
19231            }),
19232            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
19233        );
19234        assert!(
19235            declarations.iter().all(|unit| {
19236                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
19237            }),
19238            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
19239        );
19240    }
19241
19242    #[test]
19243    fn function_like_export_macro_classes_keep_names_and_base_edges() {
19244        // Every sibling shape tree-sitter produces after the `class MACRO(2, 0)`
19245        // error: a plain body, a single base, `final` without a base, `final`
19246        // with one base, and `final` with a comma-separated base list.
19247        let source = r#"
19248namespace api {
19249class PROJECT_PUBLIC_API(2, 0) Prelude {
19250  public:
19251    Prelude();
19252};
19253class PROJECT_PUBLIC_API(2, 0) Base {
19254  public:
19255    Base(int value);
19256};
19257class PROJECT_PUBLIC_API(2, 0) Mixin {
19258  public:
19259    Mixin();
19260};
19261class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
19262  public:
19263    Adopted(int value);
19264};
19265class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
19266  public:
19267    Derived(int value);
19268};
19269class PROJECT_PUBLIC_API(2, 0) Solo final {
19270  public:
19271    Solo();
19272};
19273class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
19274  public:
19275    Blended(int value);
19276};
19277class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
19278  public:
19279    Woven(int value);
19280};
19281} // namespace api
19282"#;
19283        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
19284        let declarations = parsed.declarations();
19285        let class_named = |name: &str| {
19286            declarations
19287                .iter()
19288                .find(|unit| unit.is_class() && unit.fq_name() == name)
19289                .unwrap_or_else(|| {
19290                    panic!("missing function-like export macro class {name}: {declarations:#?}")
19291                })
19292        };
19293        let base = class_named("api.Base");
19294        class_named("api.Prelude");
19295        class_named("api.Mixin");
19296
19297        assert_eq!(
19298            parsed.raw_supertypes.get(class_named("api.Adopted")),
19299            Some(&vec!["Base".to_string()])
19300        );
19301        assert_eq!(
19302            parsed.raw_supertypes.get(class_named("api.Derived")),
19303            Some(&vec!["Base".to_string()])
19304        );
19305        assert_eq!(
19306            parsed.raw_supertypes.get(class_named("api.Solo")),
19307            None,
19308            "a final class without a base list must not invent a supertype"
19309        );
19310        assert_eq!(
19311            parsed.raw_supertypes.get(class_named("api.Blended")),
19312            Some(&vec!["Base".to_string(), "Mixin".to_string()])
19313        );
19314        assert_eq!(
19315            parsed.raw_supertypes.get(class_named("api.Woven")),
19316            Some(&vec!["Base".to_string(), "Mixin".to_string()])
19317        );
19318        assert!(
19319            declarations
19320                .iter()
19321                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19322            "the export macro must not become a declaration: {declarations:#?}"
19323        );
19324        assert!(
19325            declarations.iter().all(|unit| !matches!(
19326                unit.identifier(),
19327                "final" | "public" | "protected" | "private"
19328            )),
19329            "the head specifiers must not become declarations: {declarations:#?}"
19330        );
19331        assert!(
19332            parsed
19333                .navigation_ranges
19334                .get(base)
19335                .is_some_and(|ranges| !ranges.is_empty()),
19336            "the recovered base must retain a navigable declaration range"
19337        );
19338    }
19339
19340    #[test]
19341    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
19342        // #2557: the class name is the last identifier before the head ends
19343        // (`final`, the base clause `:`, or the body), whatever its spelling.
19344        // A class named in capitals (`X509_CA`) is a class, and an object-like
19345        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
19346        // the control whose spelling never mattered.
19347        let source = r#"
19348namespace api {
19349class PROJECT_PUBLIC_API(2, 0) Base {
19350  public:
19351    Base();
19352};
19353class PROJECT_PUBLIC_API(2, 0) Mixin {
19354  public:
19355    Mixin();
19356};
19357class PROJECT_PUBLIC_API(2, 0) Name {
19358  public:
19359    Name();
19360};
19361class PROJECT_PUBLIC_API(2, 0) X509_CA final {
19362  public:
19363    X509_CA();
19364};
19365class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
19366  public:
19367    HSS_LMS_KEY();
19368};
19369class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
19370  public:
19371    GOST_3410();
19372};
19373class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
19374  public:
19375    PKCS11_RSA();
19376};
19377class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
19378  public:
19379    Plain();
19380};
19381class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
19382  public:
19383    Decorated();
19384};
19385class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
19386  public:
19387    Layered();
19388};
19389} // namespace api
19390"#;
19391        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
19392        let declarations = parsed.declarations();
19393        let class_named = |name: &str| {
19394            declarations
19395                .iter()
19396                .find(|unit| unit.is_class() && unit.fq_name() == name)
19397                .unwrap_or_else(|| {
19398                    panic!("missing function-like export macro class {name}: {declarations:#?}")
19399                })
19400        };
19401        for (name, bases) in [
19402            ("api.Name", None),
19403            ("api.X509_CA", None),
19404            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
19405            ("api.GOST_3410", Some(vec!["Base"])),
19406            ("api.PKCS11_RSA", None),
19407            ("api.Plain", None),
19408            ("api.Decorated", Some(vec!["Base"])),
19409            ("api.Layered", Some(vec!["Base", "Mixin"])),
19410        ] {
19411            let expected =
19412                bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
19413            assert_eq!(
19414                parsed.raw_supertypes.get(class_named(name)),
19415                expected.as_ref(),
19416                "{name}"
19417            );
19418        }
19419        assert!(
19420            declarations.iter().all(|unit| !matches!(
19421                unit.identifier(),
19422                "PROJECT_PUBLIC_API"
19423                    | "OTHER_MACRO"
19424                    | "FIRST_MACRO"
19425                    | "SECOND_MACRO"
19426                    | "final"
19427                    | "public"
19428            )),
19429            "macros and head specifiers must not become declarations: {declarations:#?}"
19430        );
19431    }
19432
19433    #[test]
19434    fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
19435        // #2924: `class MACRO(2, 0) Name : public virtual Base {` leaves the
19436        // head, the body's `{` and its first member in one declaration-scope
19437        // ERROR, scatters the remaining members across the container's
19438        // siblings, and ends the container on the class's own `}`. The class
19439        // was lost and the macro minted a class of its own.
19440        let source = r#"
19441namespace api {
19442
19443/**
19444* Doc comment
19445*/
19446class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
19447   public:
19448      /**
19449      * Construct from a point.
19450      */
19451      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
19452
19453#if defined(PROJECT_HAS_LEGACY_POINT)
19454      /**
19455      * Construct from a legacy point.
19456      */
19457      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
19458#endif
19459
19460      std::string algo_name() const override;
19461
19462      AlgorithmIdentifier algorithm_identifier() const override;
19463};
19464
19465}
19466"#;
19467        let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
19468        let declarations = parsed.declarations();
19469        let class = declarations
19470            .iter()
19471            .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
19472            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19473        assert_eq!(
19474            parsed.raw_supertypes.get(class),
19475            Some(&vec!["BaseKey".to_string()]),
19476            "the virtual base is the class's base: {declarations:#?}"
19477        );
19478        for member in [
19479            "api.VirtualBased.algo_name",
19480            "api.VirtualBased.algorithm_identifier",
19481        ] {
19482            assert!(
19483                declarations
19484                    .iter()
19485                    .any(|unit| unit.is_function() && unit.fq_name() == member),
19486                "{member} must be owned by the recovered class: {declarations:#?}"
19487            );
19488        }
19489        assert!(
19490            declarations
19491                .iter()
19492                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19493            "an unrecovered head must not mint a macro-named class: {declarations:#?}"
19494        );
19495    }
19496
19497    #[test]
19498    fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
19499        // #2924: object-like macro lines before the head demote the `class`
19500        // keyword to a bare identifier and make the macro invocation an
19501        // `init_declarator`, so the head arrives as a `declaration` rather than
19502        // as an ERROR carrying a `class_specifier`. The name is still the last
19503        // identifier before `final`, and `virtual` is a base specifier rather
19504        // than a base.
19505        let source = r#"
19506namespace api {
19507
19508DIAGNOSTIC_PUSH
19509DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
19510
19511class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
19512                                               public virtual api::Inner::Key {
19513   public:
19514      std::string algo_name() const override;
19515};
19516
19517DIAGNOSTIC_POP
19518
19519}
19520"#;
19521        let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
19522        let declarations = parsed.declarations();
19523        let class = declarations
19524            .iter()
19525            .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
19526            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19527        assert_eq!(
19528            parsed.raw_supertypes.get(class),
19529            Some(&vec![
19530                "api::Outer::Key".to_string(),
19531                "api::Inner::Key".to_string()
19532            ]),
19533            "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
19534        );
19535        assert!(
19536            declarations
19537                .iter()
19538                .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
19539            "the member is owned by the recovered class: {declarations:#?}"
19540        );
19541        assert!(
19542            declarations
19543                .iter()
19544                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19545            "the macro invocation must not mint a declaration: {declarations:#?}"
19546        );
19547    }
19548
19549    #[test]
19550    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
19551        // The class embedded in a preceding malformed body follows the same
19552        // rule (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration.
19553        let fixture = |head: &str, name: &str| {
19554            format!(
19555                r#"
19556namespace api {{
19557class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
19558   public:
19559      /** Return a descriptive string. */
19560      const char* what() const noexcept override {{ return m_msg.c_str(); }}
19561
19562      /** Return the type of error. */
19563      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
19564
19565      /** Return an associated error code. */
19566      virtual int error_code() const noexcept {{ return 0; }}
19567
19568      /** Avoid throwing the base directly. */
19569      explicit Exception(std::string_view msg);
19570
19571      /** Avoid throwing the base directly. */
19572      Exception(const char* prefix, std::string_view msg);
19573
19574      /** Avoid throwing the base directly. */
19575      Exception(std::string_view msg, const std::exception& e);
19576
19577   private:
19578      std::string m_msg;
19579}};
19580
19581class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
19582   public:
19583      explicit {name}(std::string_view msg);
19584
19585      explicit {name}(std::string_view msg, std::string_view where);
19586
19587      {name}(std::string_view msg, const std::exception& e);
19588
19589      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
19590}};
19591}} // namespace api
19592"#
19593            )
19594        };
19595        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
19596            let source = fixture(head, name);
19597            let mut parser = Parser::new();
19598            parser
19599                .set_language(&tree_sitter_cpp::LANGUAGE.into())
19600                .expect("set C++ grammar");
19601            let tree = parser.parse(&source, None).expect("parse fixture");
19602            let mut embedded = Vec::new();
19603            let mut stack = vec![tree.root_node()];
19604            while let Some(node) = stack.pop() {
19605                embedded.extend(
19606                    recover_embedded_function_like_export_classes(node, &source)
19607                        .into_iter()
19608                        .map(|recovered| (recovered.name, recovered.raw_supertypes)),
19609                );
19610                let mut cursor = node.walk();
19611                stack.extend(node.named_children(&mut cursor));
19612            }
19613            assert!(
19614                embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
19615                "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
19616                tree.root_node().to_sexp()
19617            );
19618            assert!(
19619                embedded
19620                    .iter()
19621                    .all(|(recovered, _)| recovered != "OTHER_MACRO"),
19622                "{head}: the object-like macro is not a class: {embedded:#?}"
19623            );
19624
19625            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
19626            let declarations = parsed.declarations();
19627            let class = declarations
19628                .iter()
19629                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
19630                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
19631            assert_eq!(
19632                parsed.raw_supertypes.get(class),
19633                Some(&vec!["Exception".to_string()]),
19634                "{head}"
19635            );
19636            assert!(
19637                declarations
19638                    .iter()
19639                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
19640                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
19641            );
19642        }
19643    }
19644
19645    #[test]
19646    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
19647        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar keeps
19648        // `class MACRO(3, 6) Name final :` in the error, the first base as the
19649        // `type` of the following declaration, and closes that declaration with
19650        // a zero-width `MISSING identifier`. Reading the head by position must
19651        // not take that missing node for the class name: an empty name panics
19652        // at `FqName` construction, which took the Botan corpus replay down
19653        // (#2557). The head's tokens straddle the two nodes, so the positional
19654        // read spans both and recovers the class rather than leaving the
19655        // bodyless `class PROJECT_PUBLIC_API` specifier behind (#2924).
19656        let source = r#"
19657namespace api {
19658class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
19659                                                    public virtual Botan::EC_PublicKey {
19660   public:
19661      std::string algo_name() const override { return "ECDSA"; }
19662};
19663} // namespace api
19664"#;
19665        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
19666        let declarations = parsed.declarations();
19667        assert!(
19668            declarations
19669                .iter()
19670                .all(|unit| !unit.identifier().is_empty()),
19671            "no declaration may carry an empty name: {declarations:#?}"
19672        );
19673        assert!(
19674            declarations
19675                .iter()
19676                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
19677            "macros and head specifiers must not become declarations: {declarations:#?}"
19678        );
19679        let class = declarations
19680            .iter()
19681            .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
19682            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19683        assert_eq!(
19684            parsed.raw_supertypes.get(class),
19685            Some(&vec![
19686                "Botan::TPM2::PublicKey".to_string(),
19687                "Botan::EC_PublicKey".to_string()
19688            ]),
19689            "both qualified virtual bases are bases: {declarations:#?}"
19690        );
19691        assert!(
19692            declarations
19693                .iter()
19694                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19695            "the head must not mint a macro-named class: {declarations:#?}"
19696        );
19697    }
19698
19699    #[test]
19700    fn function_like_export_class_survives_a_preceding_malformed_body() {
19701        let source = r#"
19702namespace api {
19703class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
19704   public:
19705      /** Return a descriptive string. */
19706      const char* what() const noexcept override { return m_msg.c_str(); }
19707
19708      /** Return the type of error. */
19709      virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
19710
19711      /** Return an associated error code. */
19712      virtual int error_code() const noexcept { return 0; }
19713
19714      /** Avoid throwing the base directly. */
19715      explicit Exception(std::string_view msg);
19716
19717      /** Avoid throwing the base directly. */
19718      Exception(const char* prefix, std::string_view msg);
19719
19720      /** Avoid throwing the base directly. */
19721      Exception(std::string_view msg, const std::exception& e);
19722
19723   private:
19724      std::string m_msg;
19725};
19726
19727class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
19728   public:
19729      explicit Invalid_Argument(std::string_view msg);
19730
19731      explicit Invalid_Argument(std::string_view msg, std::string_view where);
19732
19733      Invalid_Argument(std::string_view msg, const std::exception& e);
19734
19735      ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
19736};
19737} // namespace api
19738"#;
19739        let mut parser = Parser::new();
19740        parser
19741            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19742            .expect("set C++ grammar");
19743        let tree = parser.parse(source, None).expect("parse fixture");
19744        let mut stack = vec![tree.root_node()];
19745        let mut saw_embedded_shape = false;
19746        while let Some(node) = stack.pop() {
19747            saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
19748                .iter()
19749                .any(|recovered| recovered.name == "Invalid_Argument");
19750            let mut cursor = node.walk();
19751            stack.extend(node.named_children(&mut cursor));
19752        }
19753        assert!(
19754            saw_embedded_shape,
19755            "fixture must retain the embedded error geometry: {}",
19756            tree.root_node().to_sexp()
19757        );
19758
19759        let parsed = parse_cpp_file(
19760            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
19761            source,
19762            &tree,
19763        );
19764        let declarations = parsed.declarations();
19765        let exception = declarations
19766            .iter()
19767            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
19768            .expect("qualified-base export class");
19769        let invalid = declarations
19770            .iter()
19771            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
19772            .expect("class embedded in the preceding malformed body");
19773
19774        assert_eq!(
19775            parsed.raw_supertypes.get(exception),
19776            Some(&vec!["std::exception".to_string()])
19777        );
19778        assert_eq!(
19779            parsed.raw_supertypes.get(invalid),
19780            Some(&vec!["Exception".to_string()])
19781        );
19782        assert!(
19783            parsed.materialization_records.iter().any(|record| matches!(
19784                record,
19785                MaterializationRecord::RecoveredDeclaration { unit, .. }
19786                    if unit == invalid
19787            )),
19788            "the embedded class must retain recovery provenance: {:#?}",
19789            parsed.materialization_records
19790        );
19791    }
19792
19793    #[test]
19794    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
19795        let source = r#"
19796public:
19797   explicit Lookup_Error(std::string_view err) : Exception(err) {}
19798
19799   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
19800"#;
19801        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
19802            .expect("reparse merged constructor body");
19803        let (range, body) =
19804            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
19805                .unwrap_or_else(|| {
19806                    panic!(
19807                        "the merged constructor must retain its structured declarator/body: {}",
19808                        tree.root_node().to_sexp()
19809                    )
19810                });
19811        assert_eq!(
19812            source.get(range).expect("constructor range"),
19813            "Lookup_Error(std::string_view err) : Exception(err) {}"
19814        );
19815        assert_eq!(node_text(body, source), "{}");
19816    }
19817
19818    #[test]
19819    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
19820        let positive_source =
19821            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
19822        let mut parser = tree_sitter::Parser::new();
19823        parser
19824            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19825            .unwrap();
19826        let positive_tree = parser.parse(positive_source, None).unwrap();
19827        assert!(cpp_reparsed_members_are_indexable(
19828            positive_tree.root_node(),
19829            positive_source
19830        ));
19831
19832        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
19833        let negative_tree = parser.parse(negative_source, None).unwrap();
19834        assert!(!cpp_reparsed_members_are_indexable(
19835            negative_tree.root_node(),
19836            negative_source
19837        ));
19838    }
19839
19840    #[test]
19841    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
19842        let copy_control_source = r#"
19843public:
19844    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
19845    explicit Token(const Token* tok);
19846    ~Token();
19847    Token* astOperand1() { return nullptr; }
19848"#;
19849        let constraint_source = r#"
19850private:
19851    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19852    static T *tokAtImpl(T *tok, int index) {
19853        return tok;
19854    }
19855
19856    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19857    static T *linkAtImpl(T *tok, int index) {
19858        return tok;
19859    }
19860
19861public:
19862    int late() const { return 1; }
19863"#;
19864        let mut parser = tree_sitter::Parser::new();
19865        parser
19866            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19867            .unwrap();
19868        let copy_control_tree = parser
19869            .parse(copy_control_source, None)
19870            .expect("parse copy-control fixture");
19871        assert!(
19872            copy_control_tree.root_node().has_error(),
19873            "fixture must exercise adjacent copy-control recovery"
19874        );
19875        assert!(
19876            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
19877            "a complete late getter must remain recoverable after adjacent copy-control declarations"
19878        );
19879        let mut cursor = copy_control_tree.root_node().walk();
19880        assert!(
19881            copy_control_tree
19882                .root_node()
19883                .named_children(&mut cursor)
19884                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
19885            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
19886            copy_control_tree.root_node().to_sexp()
19887        );
19888        let constraint_tree = parser
19889            .parse(constraint_source, None)
19890            .expect("parse constraint-macro fixture");
19891        assert!(constraint_tree.root_node().has_error());
19892        assert!(
19893            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
19894            "complete constraint-macro members must not hide a later ordinary member"
19895        );
19896        let mut cursor = constraint_tree.root_node().walk();
19897        assert!(
19898            constraint_tree
19899                .root_node()
19900                .named_children(&mut cursor)
19901                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
19902                    child,
19903                    constraint_source
19904                )),
19905            "fixture must retain the split constraint-macro prefix/function geometry"
19906        );
19907    }
19908
19909    #[test]
19910    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
19911        let source = r#"
19912struct Analyzer {
19913    struct Action {
19914        Action() = default;
19915        Action(const Action&) = default;
19916        Action& operator=(const Action& rhs) & = default;
19917
19918        template<class T,
19919                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
19920                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
19921        // NOLINTNEXTLINE(google-explicit-constructor)
19922        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
19923        {}
19924
19925        enum : std::uint16_t { None = 0, Read = (1 << 0) };
19926        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
19927
19928    private:
19929        unsigned int mFlag{};
19930    };
19931
19932    enum class Direction : unsigned char { Forward, Reverse };
19933    virtual Action analyze(Direction d) const = 0;
19934};
19935"#;
19936        let mut parser = tree_sitter::Parser::new();
19937        parser
19938            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19939            .unwrap();
19940        let tree = parser.parse(source, None).unwrap();
19941        assert!(tree.root_node().has_error());
19942        let root = tree.root_node();
19943        let outer = root
19944            .named_children(&mut root.walk())
19945            .find(|child| child.kind() == "ERROR")
19946            .expect("fragmented Analyzer prefix");
19947        let outer_recovered =
19948            fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
19949        assert_eq!(outer_recovered.name, "Analyzer");
19950        let outer_tree = cpp_reparse_fragmented_class_body(
19951            source,
19952            outer_recovered.body.reparse_start,
19953            outer_recovered.body.reparse_end,
19954        )
19955        .expect("reparse Analyzer body");
19956        let outer_root = outer_tree.root_node();
19957        let action_prefix = outer_root
19958            .named_children(&mut outer_root.walk())
19959            .find(|child| child.kind() == "ERROR")
19960            .expect("fragmented Action prefix");
19961        let action_recovered = fragmented_class_body(action_prefix, source)
19962            .expect("structured Action fragment boundary");
19963        assert_eq!(action_recovered.name, "Action");
19964        let action_tree = cpp_reparse_fragmented_class_body(
19965            source,
19966            action_recovered.body.reparse_start,
19967            action_recovered.body.reparse_end,
19968        )
19969        .expect("reparse Action body");
19970        let action_root = action_tree.root_node();
19971        let macro_prefix = action_root
19972            .named_children(&mut action_root.walk())
19973            .find(|child| child.kind() == "ERROR")
19974            .expect("constraint macro prefix");
19975        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
19976            .expect("structured template macro prefix");
19977        let macro_companion =
19978            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
19979        assert!(
19980            cpp_reparsed_template_macro_constructor_companion_is_indexable(
19981                macro_companion,
19982                macro_parameter,
19983                source,
19984            ),
19985            "split constrained constructor must be admitted: {}",
19986            macro_companion.to_sexp()
19987        );
19988        assert!(
19989            cpp_reparsed_members_are_indexable(action_root, source),
19990            "complete Action body must pass the recovery gate: {}",
19991            action_tree.root_node().to_sexp()
19992        );
19993        assert!(
19994            cpp_reparsed_members_are_indexable(outer_root, source),
19995            "complete Analyzer body must pass the recovery gate: {}",
19996            outer_tree.root_node().to_sexp()
19997        );
19998        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
19999        let parsed = parse_cpp_file(&file, source, &tree);
20000        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
20001            assert!(
20002                parsed
20003                    .declarations()
20004                    .iter()
20005                    .any(|unit| unit.fq_name() == expected),
20006                "missing recovered declaration {expected}: {:#?}",
20007                parsed.declarations()
20008            );
20009        }
20010        assert!(
20011            parsed
20012                .declarations()
20013                .iter()
20014                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
20015            "nested members must not remain flattened: {:#?}",
20016            parsed.declarations()
20017        );
20018    }
20019
20020    #[test]
20021    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
20022        let source = r#"
20023raw_hash_set& operator=(raw_hash_set&& that) {
20024  return move_assign(
20025      std::move(that),
20026      typename AllocTraits::propagate_on_container_move_assignment());
20027}
20028
20029iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
20030  return {};
20031}
20032
20033void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
20034
20035iterator insert(const_iterator hint, value_type&& value)
20036    ABSL_ATTRIBUTE_LIFETIME_BOUND {
20037  return {};
20038}
20039
20040friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
20041  return left.size() == right.size();
20042}
20043
20044static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
20045  return static_cast<slot_type*>(buffer);
20046}
20047
20048protected:
20049// Included-range recovery can attach this comment to the template prefix.
20050template <class K>
20051void AssertOnFind([[maybe_unused]] const K& key) {
20052  Check(key);
20053}
20054"#;
20055        let mut parser = tree_sitter::Parser::new();
20056        parser
20057            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20058            .unwrap();
20059        let tree = parser.parse(source, None).unwrap();
20060        assert!(
20061            tree.root_node().has_error(),
20062            "the fixture must exercise tree-sitter's errorful member shapes"
20063        );
20064        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
20065
20066        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
20067        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
20068        assert!(!cpp_reparsed_members_are_indexable(
20069            incomplete_tree.root_node(),
20070            incomplete_source
20071        ));
20072
20073        let outside_error_source = "int foo() stray_attribute {}\n";
20074        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
20075        assert!(outside_error_tree.root_node().has_error());
20076        assert!(!cpp_reparsed_members_are_indexable(
20077            outside_error_tree.root_node(),
20078            outside_error_source
20079        ));
20080
20081        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
20082        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20083        assert!(!cpp_reparsed_members_are_indexable(
20084            variable_initializer_tree.root_node(),
20085            variable_initializer_source
20086        ));
20087    }
20088
20089    #[test]
20090    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
20091        let positive_source = r#"
20092std::pair<iterator, bool> insert(init_type&& value)
20093    ABSL_ATTRIBUTE_LIFETIME_BOUND
20094#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20095  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20096#endif
20097{
20098  return emplace(std::move(value));
20099}
20100"#;
20101        let mut parser = tree_sitter::Parser::new();
20102        parser
20103            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20104            .unwrap();
20105        let positive_tree = parser.parse(positive_source, None).unwrap();
20106        assert!(
20107            positive_tree.root_node().has_error(),
20108            "the fixture must exercise the split attribute/requires shape"
20109        );
20110        assert!(cpp_reparsed_members_are_indexable(
20111            positive_tree.root_node(),
20112            positive_source
20113        ));
20114
20115        let template_return_source = r#"
20116pair<int> insert(init_type&& value)
20117    ABSL_ATTRIBUTE_LIFETIME_BOUND
20118#if LANGUAGE_LEVEL >= 202002L
20119  requires(!Predicate<init_type>::value)
20120#endif
20121// Attributes and the function body may be separated by comments.
20122{
20123  return {};
20124}
20125"#;
20126        let template_return_tree = parser.parse(template_return_source, None).unwrap();
20127        assert!(
20128            cpp_reparsed_members_are_indexable(
20129                template_return_tree.root_node(),
20130                template_return_source
20131            ),
20132            "template-return attribute/requires tree: {}",
20133            template_return_tree.root_node().to_sexp()
20134        );
20135
20136        let no_body_source = r#"
20137std::pair<iterator, bool> insert(init_type&& value)
20138    ABSL_ATTRIBUTE_LIFETIME_BOUND
20139#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20140  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20141#endif
20142+ 0;
20143"#;
20144        let no_body_tree = parser.parse(no_body_source, None).unwrap();
20145        assert!(!cpp_reparsed_members_are_indexable(
20146            no_body_tree.root_node(),
20147            no_body_source
20148        ));
20149
20150        let extra_payload_source = r#"
20151pair<int> insert(init_type&& value)
20152    ABSL_ATTRIBUTE_LIFETIME_BOUND
20153#if LANGUAGE_LEVEL >= 202002L
20154  int unrelated;
20155  requires(Predicate<init_type>::value)
20156#endif
20157{
20158  return {};
20159}
20160"#;
20161        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
20162        assert!(!cpp_reparsed_members_are_indexable(
20163            extra_payload_tree.root_node(),
20164            extra_payload_source
20165        ));
20166
20167        let variable_initializer_source = r#"
20168int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
20169#if LANGUAGE_LEVEL >= 202002L
20170  requires(true)
20171#endif
20172{
20173  bad;
20174}
20175"#;
20176        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20177        assert!(!cpp_reparsed_members_are_indexable(
20178            variable_initializer_tree.root_node(),
20179            variable_initializer_source
20180        ));
20181    }
20182
20183    #[test]
20184    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
20185        let source = r#"namespace absl {
20186ABSL_NAMESPACE_BEGIN namespace container_internal {
20187
20188class raw_hash_set : public Base {
20189 public:
20190  using value_type = int;
20191
20192  template <class U,
20193            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
20194  void insert(U value) { (void)value; }
20195
20196  struct InsertSlot {
20197    raw_hash_set& s;
20198  };
20199};
20200
20201}
20202ABSL_NAMESPACE_END
20203}"#;
20204        let mut parser = tree_sitter::Parser::new();
20205        parser
20206            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20207            .unwrap();
20208        let tree = parser.parse(source, None).unwrap();
20209        let root = tree.root_node();
20210        let outer_namespace = root
20211            .named_children(&mut root.walk())
20212            .find(|child| child.kind() == "namespace_definition")
20213            .expect("outer absl namespace");
20214        let declaration_list = outer_namespace
20215            .child_by_field_name("body")
20216            .expect("outer namespace body");
20217        let sentinel_function = declaration_list
20218            .named_children(&mut declaration_list.walk())
20219            .find(|child| child.kind() == "function_definition")
20220            .expect("malformed namespace sentinel function");
20221        let ancestry = ParentIndex::new(root);
20222        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
20223            .expect("structured nested namespace sentinel");
20224        let fragmented =
20225            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
20226                .expect("fragmented raw_hash_set class");
20227        assert_eq!(fragmented.class_node.kind(), "ERROR");
20228        assert_eq!(fragmented.name, "raw_hash_set");
20229        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
20230
20231        let outer_scope =
20232            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
20233        let mut outer_siblings = Vec::new();
20234        push_cpp_sentinel_sibling_classes(
20235            &mut outer_siblings,
20236            declaration_list,
20237            sentinel.function,
20238            &outer_scope,
20239            source,
20240            &ancestry,
20241        );
20242        let [outer_shadow] = outer_siblings.as_slice() else {
20243            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
20244        };
20245        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
20246        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
20247
20248        let field = "    raw_hash_set& s;";
20249        let start = source.find(field).expect("InsertSlot field") + 4;
20250        let node = root
20251            .descendant_for_byte_range(start, start + "raw_hash_set".len())
20252            .expect("raw_hash_set type node");
20253        let recovered = cpp_sentinel_recovered_classes(root, source);
20254        let [deep_class] = recovered.as_slice() else {
20255            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
20256        };
20257        assert_eq!(
20258            deep_class.namespace_scope_components,
20259            vec!["absl", "container_internal"]
20260        );
20261        assert_eq!(
20262            deep_class.scope_components,
20263            vec!["absl", "container_internal", "raw_hash_set"]
20264        );
20265        assert!(
20266            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
20267                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
20268        );
20269
20270        assert_eq!(
20271            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
20272            Some(vec![
20273                "absl".to_string(),
20274                "container_internal".to_string(),
20275                "raw_hash_set".to_string(),
20276                "InsertSlot".to_string(),
20277            ])
20278        );
20279
20280        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
20281        let parsed = parse_cpp_file(&file, source, &tree);
20282        let raw_hash_set = parsed
20283            .declarations()
20284            .iter()
20285            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
20286            .expect("recovered raw_hash_set class");
20287        assert_eq!(
20288            raw_hash_set.fq_name(),
20289            "absl::container_internal.raw_hash_set",
20290            "the recovered declaration must publish under the deeper sentinel namespace"
20291        );
20292        assert_eq!(
20293            parsed.raw_supertypes.get(raw_hash_set),
20294            Some(&vec!["Base".to_string()]),
20295            "the structured base clause on the fragmented ERROR prefix must survive publication"
20296        );
20297        assert!(
20298            parsed.materialization_records.iter().any(|record| matches!(
20299                record,
20300                MaterializationRecord::RecoveredDeclaration { recovery, unit }
20301                    if unit == raw_hash_set && *recovery == deep_class.class_range
20302            )),
20303            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
20304            parsed.materialization_records
20305        );
20306    }
20307
20308    /// Issue #2358: recording an aggregate definition must not walk the whole
20309    /// file.
20310    ///
20311    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
20312    /// class-like shape that has a body, so the removal step runs once per
20313    /// aggregate. It used to `retain` over `top_level_declarations` and over
20314    /// *every* child list in the file on each of those calls, comparing whole
20315    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
20316    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
20317    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
20318    /// tens of thousands of times over and the C forward differential never
20319    /// finished.
20320    ///
20321    /// A definition the file has not already declared removes nothing, so the
20322    /// honest cost is zero regardless of how many other aggregates surround it.
20323    /// Two sizes an order of magnitude apart pin that the count is not merely
20324    /// small but independent of the file.
20325    ///
20326    /// The declaration walk answers every ancestor question from a
20327    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
20328    /// the root for each one (#2361). Substituting the index is only safe
20329    /// because it answers the identical question, so pin that on the shapes
20330    /// this file's recovery paths care about: anonymous and named aggregates,
20331    /// nested namespaces, templates, macro-displaced declarations and the
20332    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
20333    /// too -- `Node::parent` walks the visible tree, not the named one.
20334    #[test]
20335    fn the_parent_index_answers_what_tree_sitter_answers() {
20336        const SHAPES: [&str; 5] = [
20337            "namespace outer { namespace inner { struct Tag { int field; }; } }",
20338            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
20339            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
20340            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
20341            "class API Broken : public First, public Second {\n  void member();\n",
20342        ];
20343        for source in SHAPES {
20344            let mut parser = tree_sitter::Parser::new();
20345            parser
20346                .set_language(&tree_sitter_cpp::LANGUAGE.into())
20347                .unwrap();
20348            let tree = parser.parse(source, None).unwrap();
20349            let root = tree.root_node();
20350            let ancestry = ParentIndex::new(root);
20351            let mut nodes = 0usize;
20352            let mut stack = vec![root];
20353            while let Some(node) = stack.pop() {
20354                nodes += 1;
20355                assert_eq!(
20356                    node.parent().map(|parent| parent.id()),
20357                    ancestry.parent(node).map(|parent| parent.id()),
20358                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
20359                );
20360                let mut cursor = node.walk();
20361                stack.extend(node.children(&mut cursor));
20362            }
20363            assert!(nodes > 1, "{source:?} produced no tree to compare");
20364        }
20365    }
20366
20367    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
20368    /// index for every ancestor edge. Asking tree-sitter directly makes each
20369    /// edge re-descend from the root, turning declaration extraction on a
20370    /// deeply nested generated header from quadratic output work into a cubic
20371    /// tree walk. Exact query counts pin the route without a machine-dependent
20372    /// wall-clock ceiling.
20373    #[test]
20374    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
20375        const DEPTH: usize = 64;
20376        let mut source = String::new();
20377        for level in 0..DEPTH {
20378            writeln!(source, "namespace n{level} {{").unwrap();
20379        }
20380        source.push_str("int deepest(int value);\n");
20381        for _ in 0..DEPTH {
20382            source.push_str("}\n");
20383        }
20384
20385        let mut parser = tree_sitter::Parser::new();
20386        parser
20387            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20388            .unwrap();
20389        let tree = parser.parse(&source, None).unwrap();
20390        let root = tree.root_node();
20391        let ancestry = ParentIndex::new(root);
20392        let mut function_declarator = None;
20393        walk_named_tree_preorder(root, true, |node| {
20394            if node.kind() == "function_declarator" {
20395                function_declarator = Some(node);
20396                WalkControl::Break
20397            } else {
20398                WalkControl::Continue
20399            }
20400        });
20401        let function_declarator = function_declarator.expect("deepest function declarator");
20402        let ancestor_count =
20403            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
20404
20405        ancestry.reset_parent_query_count_for_test();
20406        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
20407        assert_eq!(DEPTH, lexical_scope.len());
20408        assert_eq!(
20409            ancestor_count + 1,
20410            ancestry.parent_query_count_for_test(),
20411            "lexical-scope ancestry bypassed the parent index"
20412        );
20413
20414        ancestry.reset_parent_query_count_for_test();
20415        assert_eq!(
20416            DispatchExtensibility::Closed,
20417            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
20418        );
20419        assert_eq!(
20420            ancestor_count,
20421            ancestry.parent_query_count_for_test(),
20422            "dispatch ancestry bypassed the parent index"
20423        );
20424
20425        ancestry.reset_parent_query_count_for_test();
20426        assert_eq!(
20427            CallableLinkage::External,
20428            cpp_callable_linkage(function_declarator, &source, &ancestry)
20429        );
20430        assert_eq!(
20431            ancestor_count + 1,
20432            ancestry.parent_query_count_for_test(),
20433            "linkage ancestry bypassed the parent index"
20434        );
20435
20436        ancestry.reset_parent_query_count_for_test();
20437        assert!(!cpp_callable_is_structural_constructor(
20438            function_declarator,
20439            &source,
20440            &ancestry
20441        ));
20442        assert_eq!(
20443            ancestor_count + 1,
20444            ancestry.parent_query_count_for_test(),
20445            "constructor ancestry bypassed the parent index"
20446        );
20447    }
20448
20449    /// Forward declarations followed by definitions are compacted as one
20450    /// batch, without rescanning the shared namespace/top-level lists for each
20451    /// tag. Definitions are intentionally visited in reverse order so the
20452    /// assertion also pins eager remove-and-reappend ordering.
20453    #[test]
20454    fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
20455        for aggregates in [64usize, 512] {
20456            let mut source =
20457                String::from("typedef unsigned long long u64;\nnamespace generated {\n");
20458            for index in 0..aggregates {
20459                writeln!(source, "struct tag{index};").unwrap();
20460            }
20461            for index in (0..aggregates).rev() {
20462                writeln!(
20463                    source,
20464                    "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
20465                )
20466                .unwrap();
20467            }
20468            source.push_str("}\n");
20469
20470            start_code_unit_removal_scan_probe();
20471            let parsed = parse_cpp_declarations(&source, "vmlinux.h");
20472            let scanned = finish_code_unit_removal_scan_probe();
20473
20474            let expected_names: Vec<String> = (0..aggregates)
20475                .rev()
20476                .map(|index| format!("tag{index}"))
20477                .collect();
20478            let top_level_names: Vec<String> = parsed
20479                .top_level_declarations
20480                .iter()
20481                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20482                .map(|unit| unit.short_name().to_string())
20483                .collect();
20484            let namespace = parsed
20485                .declarations()
20486                .iter()
20487                .find(|unit| {
20488                    unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
20489                })
20490                .expect("generated namespace should be declared");
20491            let child_names: Vec<String> = parsed.children[namespace]
20492                .iter()
20493                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20494                .map(|unit| unit.short_name().to_string())
20495                .collect();
20496            assert_eq!(
20497                aggregates,
20498                parsed
20499                    .declarations()
20500                    .iter()
20501                    .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20502                    .count(),
20503                "every aggregate must still be declared at {aggregates} aggregates"
20504            );
20505            assert_eq!(expected_names, top_level_names);
20506            assert_eq!(expected_names, child_names);
20507            assert_eq!(
20508                0, scanned,
20509                "replacing {aggregates} forward declarations must compact their shared lists once"
20510            );
20511        }
20512    }
20513
20514    #[test]
20515    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
20516        const DISTINCT_PER_KIND: usize = 64;
20517        let mut source = String::new();
20518        for index in 0..DISTINCT_PER_KIND {
20519            writeln!(source, "typedef int Alias{index};").unwrap();
20520        }
20521        writeln!(source, "typedef long Alias0;").unwrap();
20522        for index in 0..DISTINCT_PER_KIND {
20523            writeln!(source, "#define MACRO_{index} {index}").unwrap();
20524        }
20525        writeln!(source, "#define MACRO_0 duplicate").unwrap();
20526        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
20527
20528        let mut parser = tree_sitter::Parser::new();
20529        parser
20530            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20531            .unwrap();
20532        let tree = parser.parse(&source, None).unwrap();
20533        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
20534
20535        start_declaration_identity_comparison_probe();
20536        let parsed = parse_cpp_file(&file, &source, &tree);
20537        let comparisons = finish_declaration_identity_comparison_probe();
20538
20539        assert_eq!(
20540            DISTINCT_PER_KIND + 1,
20541            parsed
20542                .declarations()
20543                .iter()
20544                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
20545                .count(),
20546            "every physical typedef alias declaration must be retained so \
20547             conditional branch guards stay available to the resolver"
20548        );
20549        assert_eq!(
20550            DISTINCT_PER_KIND + 1,
20551            parsed
20552                .declarations()
20553                .iter()
20554                .filter(|unit| {
20555                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
20556                })
20557                .count(),
20558            "distinct macro redefinitions must remain available to temporal lookup"
20559        );
20560        assert_eq!(
20561            2,
20562            parsed
20563                .declarations()
20564                .iter()
20565                .filter(|unit| {
20566                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
20567                })
20568                .count(),
20569            "function overloads must remain distinct"
20570        );
20571
20572        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
20573        assert!(
20574            comparisons <= dedup_inputs * 4,
20575            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
20576        );
20577    }
20578
20579    #[test]
20580    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
20581        let source = r#"namespace absl {
20582ABSL_NAMESPACE_BEGIN namespace container_internal {
20583template <typename T>
20584class broken {
20585 public:
20586  using value_type = T;
20587  T operator->() const { return &operator*(); }
20588  using alias = value_type;
20589};
20590}
20591}
20592"#;
20593        let mut parser = tree_sitter::Parser::new();
20594        parser
20595            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20596            .unwrap();
20597        let tree = parser.parse(source, None).unwrap();
20598        let broken = find_class_named(tree.root_node(), source, "broken")
20599            .expect("the positive fixture must expose the broken class node");
20600        assert!(
20601            broken.has_error(),
20602            "the positive fixture must retain an internal parser error"
20603        );
20604        assert!(
20605            cpp_complete_class_body_close(broken).is_some(),
20606            "the positive fixture must expose a real class body close"
20607        );
20608        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20609        assert!(
20610            recovered.iter().any(|class| {
20611                class.scope_components == ["absl", "container_internal", "broken"]
20612            }),
20613            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
20614        );
20615    }
20616
20617    #[test]
20618    fn sentinel_recovery_keeps_members_after_nested_body_close() {
20619        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
20620NLOHMANN_BASIC_JSON_TPL_DECLARATION
20621class basic_json {
20622 private:
20623  union storage {
20624    int value;
20625  } data;
20626 public:
20627  using late_alias = int;
20628  late_alias value() const;
20629};
20630NLOHMANN_JSON_NAMESPACE_END
20631"#;
20632        let mut parser = tree_sitter::Parser::new();
20633        parser
20634            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20635            .unwrap();
20636        let tree = parser.parse(source, None).unwrap();
20637        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20638        let basic_json = recovered
20639            .iter()
20640            .find(|class| {
20641                class
20642                    .scope_components
20643                    .last()
20644                    .is_some_and(|name| name == "basic_json")
20645            })
20646            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
20647        let late_alias = source
20648            .find("late_alias value")
20649            .expect("late alias reference");
20650        assert!(
20651            basic_json.class_range.start_byte < late_alias
20652                && late_alias < basic_json.class_range.end_byte,
20653            "the recovered class range must include members after a nested close: {basic_json:#?}"
20654        );
20655    }
20656
20657    #[test]
20658    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
20659        let source = r#"namespace absl {
20660ABSL_NAMESPACE_BEGIN namespace container_internal {
20661template <typename T>
20662class broken {
20663 public:
20664  using value_type = T;
20665  T operator->() const { return &operator*(); }
20666}
20667}
20668"#;
20669        let mut parser = tree_sitter::Parser::new();
20670        parser
20671            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20672            .unwrap();
20673        let tree = parser.parse(source, None).unwrap();
20674        let broken = find_class_named(tree.root_node(), source, "broken")
20675            .expect("the negative fixture must expose the malformed class node");
20676        assert!(
20677            broken.has_error(),
20678            "the negative fixture must retain a parser error"
20679        );
20680        assert!(
20681            cpp_complete_class_body_close(broken).is_none(),
20682            "the malformed class must not expose a real body close"
20683        );
20684        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20685        assert!(
20686            recovered
20687                .iter()
20688                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
20689            "an incomplete class must not borrow the namespace close: {recovered:#?}"
20690        );
20691    }
20692
20693    #[test]
20694    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
20695        let source = r#"namespace absl {
20696ABSL_NAMESPACE_BEGIN namespace container_internal {
20697template <typename T>
20698struct broken {
20699  using value_type = T;
20700};
20701}
20702
20703#ifdef OWNER_DEF
20704template <typename T>
20705typename broken<T>::value_type broken<T>::method() {
20706  value_type value{};
20707  return value;
20708}
20709#endif
20710
20711namespace sibling {
20712template <typename T>
20713typename broken<T>::value_type broken<T>::other() {
20714  value_type value{};
20715  return value;
20716}
20717}
20718
20719ABSL_NAMESPACE_END
20720}
20721"#;
20722        let mut parser = tree_sitter::Parser::new();
20723        parser
20724            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20725            .unwrap();
20726        let tree = parser.parse(source, None).unwrap();
20727        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20728        let broken = recovered
20729            .iter()
20730            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20731            .expect("the sentinel class must be recovered");
20732        let method_start = source
20733            .find("typename broken<T>::value_type broken<T>::method()")
20734            .expect("guarded sibling owner");
20735        let method_end = source[method_start..]
20736            .find("\n}")
20737            .map(|offset| method_start + offset + 2)
20738            .expect("guarded sibling owner close");
20739        assert!(
20740            broken
20741                .owner_ranges
20742                .iter()
20743                .any(|owner| owner.range.start_byte <= method_start
20744                    && method_end <= owner.range.end_byte),
20745            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
20746        );
20747        let sibling_start = source
20748            .find("typename broken<T>::value_type broken<T>::other()")
20749            .expect("nested namespace sibling owner");
20750        assert!(
20751            broken
20752                .owner_ranges
20753                .iter()
20754                .all(|owner| owner.range.start_byte > sibling_start
20755                    || owner.range.end_byte <= sibling_start),
20756            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
20757        );
20758    }
20759
20760    #[test]
20761    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
20762        let source = r#"#ifdef OUTER
20763namespace absl {
20764ABSL_NAMESPACE_BEGIN namespace container_internal {
20765template <typename T>
20766struct broken {
20767  using value_type = T;
20768};
20769}
20770}
20771
20772#ifdef OWNER_DEF
20773template <typename T>
20774typename broken<T>::value_type broken<T>::method() {
20775  value_type value{};
20776  return value;
20777}
20778#endif
20779#endif
20780"#;
20781        let mut parser = tree_sitter::Parser::new();
20782        parser
20783            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20784            .unwrap();
20785        let tree = parser.parse(source, None).unwrap();
20786        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20787        let broken = recovered
20788            .iter()
20789            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20790            .expect("the sentinel class must be recovered");
20791        let method_start = source
20792            .find("typename broken<T>::value_type broken<T>::method()")
20793            .expect("outer sibling owner");
20794        assert!(
20795            broken
20796                .owner_ranges
20797                .iter()
20798                .all(|owner| owner.range.start_byte > method_start
20799                    || owner.range.end_byte <= method_start),
20800            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
20801        );
20802    }
20803
20804    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
20805    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
20806        let mut signatures = parsed
20807            .declarations()
20808            .iter()
20809            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
20810            .filter_map(|unit| unit.signature().map(str::to_string))
20811            .collect::<Vec<_>>();
20812        signatures.sort();
20813        signatures.dedup();
20814        signatures
20815    }
20816
20817    #[test]
20818    fn callable_parameter_types_come_from_the_ast_parameter_list() {
20819        let source = r#"
20820template <typename T, ENABLE_BYTES(T)>
20821Vec256<T> DupOdd(Vec256<T> value) { return value; }
20822
20823struct Visitor {
20824  void fail(this auto const& self) {}
20825};
20826"#;
20827        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
20828        let dup_odd = parsed
20829            .declarations()
20830            .iter()
20831            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
20832            .expect("DupOdd declaration");
20833        assert_eq!(
20834            dup_odd.signature(),
20835            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
20836        );
20837        assert_eq!(
20838            parsed
20839                .signature_metadata
20840                .get(dup_odd)
20841                .and_then(|metadata| metadata.first())
20842                .and_then(SignatureMetadata::callable_parameter_types),
20843            Some(["Vec256<T>".to_string()].as_slice())
20844        );
20845
20846        let fail = parsed
20847            .declarations()
20848            .iter()
20849            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
20850            .expect("explicit-object member");
20851        assert_eq!(fail.signature(), Some("(const this auto &)"));
20852        let metadata = parsed
20853            .signature_metadata
20854            .get(fail)
20855            .and_then(|metadata| metadata.first())
20856            .expect("explicit-object signature metadata");
20857        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
20858        assert!(
20859            metadata
20860                .callable_arity()
20861                .is_some_and(|arity| arity.accepts(0))
20862        );
20863    }
20864
20865    #[test]
20866    fn trailing_qualifiers_survive_parameter_list_whitespace() {
20867        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
20868        // declarator's structure, so an out-of-line definition that spells its
20869        // parameter list with different whitespace than the declaration must
20870        // still carry it.
20871        let source = r#"
20872struct Widget {
20873  bool multiline(int settings, int supprs) const;
20874  bool doublespace(int settings, int supprs) const;
20875  bool noexcept_multiline(int settings, int supprs) noexcept;
20876  bool ref_multiline(int settings, int supprs) &&;
20877};
20878bool
20879Widget::multiline (int settings,
20880                   int supprs) const
20881{ return settings + supprs > 0; }
20882bool Widget::doublespace(int settings,  int supprs) const { return true; }
20883bool Widget::noexcept_multiline(int settings,
20884                                int supprs) noexcept { return true; }
20885bool Widget::ref_multiline(int settings,
20886                           int supprs) && { return true; }
20887"#;
20888        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
20889        assert_eq!(
20890            vec!["(int, int) const".to_string()],
20891            identity_signatures(&parsed, "Widget.multiline")
20892        );
20893        assert_eq!(
20894            vec!["(int, int) const".to_string()],
20895            identity_signatures(&parsed, "Widget.doublespace")
20896        );
20897        assert_eq!(
20898            vec!["(int, int) noexcept".to_string()],
20899            identity_signatures(&parsed, "Widget.noexcept_multiline")
20900        );
20901        assert_eq!(
20902            vec!["(int, int) &&".to_string()],
20903            identity_signatures(&parsed, "Widget.ref_multiline")
20904        );
20905    }
20906
20907    #[test]
20908    fn macro_fragmented_plain_class_keeps_following_member_signature() {
20909        let source = r#"
20910struct CString {};
20911class CMessage {
20912public:
20913  CString GetParams(unsigned int index, unsigned int length = -1) const
20914      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
20915    return GetParamsColon(index, length);
20916  }
20917  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
20918};
20919CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
20920  return {};
20921}
20922"#;
20923        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
20924        assert_eq!(
20925            vec!["(unsigned int, unsigned int) const".to_string()],
20926            identity_signatures(&parsed, "CMessage.GetParamsColon")
20927        );
20928    }
20929
20930    #[test]
20931    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
20932        let source = r#"
20933#pragma once
20934#define DEMO_DEPRECATED(message)
20935namespace demo {
20936struct Base {
20937    static int aligned(int value) { return value; }
20938    int legacy(int value) const
20939        DEMO_DEPRECATED("use replacement()") { return value; }
20940    int replacement() const;
20941    void run(int value);
20942};
20943struct OtherBase {
20944    void run(int value);
20945    static int aligned(int value) { return value; }
20946};
20947struct Derived : Base {};
20948struct Override : Base {
20949    void run(int value);
20950    static int aligned(int value) { return value; }
20951};
20952struct RecoveredOverride : Base {
20953    int legacy(int value) const
20954        DEMO_DEPRECATED("use replacement()") { return value; }
20955    void run(int value);
20956};
20957struct Hidden : Base {
20958    void run(int first, int second);
20959    static int aligned(int first, int second) { return first + second; }
20960};
20961struct Ambiguous : Base, OtherBase {};
20962}
20963struct Global {};
20964"#;
20965        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
20966        let declarations = parsed.declarations();
20967        let fq_names = declarations
20968            .iter()
20969            .map(|unit| unit.fq_name())
20970            .collect::<std::collections::BTreeSet<_>>();
20971
20972        for expected in [
20973            "demo.Base",
20974            "demo.Base.aligned",
20975            "demo.Base.legacy",
20976            "demo.Base.replacement",
20977            "demo.Base.run",
20978            "demo.Derived",
20979            "demo.OtherBase",
20980            "demo.Override",
20981            "demo.RecoveredOverride",
20982            "demo.Hidden",
20983            "demo.Ambiguous",
20984            "Global",
20985        ] {
20986            assert!(
20987                fq_names.contains(expected),
20988                "missing {expected} from namespaced macro fragment: {declarations:#?}"
20989            );
20990        }
20991        assert!(
20992            !fq_names.contains("Derived"),
20993            "following class escaped its namespace: {declarations:#?}"
20994        );
20995        assert!(
20996            !fq_names.contains("demo.Global"),
20997            "global class crossed the recovered namespace boundary: {declarations:#?}"
20998        );
20999    }
21000
21001    #[test]
21002    fn trailing_qualifiers_still_separate_genuine_overloads() {
21003        // The qualifier must keep distinguishing the real C++ overload sets it
21004        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
21005        let source = r#"
21006struct Widget {
21007  int* slot(int index);
21008  const int* slot(int index) const;
21009  int log(int severity) &;
21010  int log(int severity) &&;
21011};
21012"#;
21013        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
21014        assert_eq!(
21015            vec!["(int)".to_string(), "(int) const".to_string()],
21016            identity_signatures(&parsed, "Widget.slot")
21017        );
21018        assert_eq!(
21019            vec!["(int) &".to_string(), "(int) &&".to_string()],
21020            identity_signatures(&parsed, "Widget.log")
21021        );
21022    }
21023
21024    #[test]
21025    fn virtual_specifier_is_not_part_of_the_identity_signature() {
21026        // `override` never appears on the out-of-line definition, and C++ does
21027        // not make it part of the signature, so it must not split the identity.
21028        let source = r#"
21029struct Base {
21030  virtual void run(int value) const;
21031};
21032struct Widget : Base {
21033  void run(int value) const override;
21034};
21035void Widget::run(int value) const {}
21036"#;
21037        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
21038        assert_eq!(
21039            vec!["(int) const".to_string()],
21040            identity_signatures(&parsed, "Widget.run")
21041        );
21042    }
21043
21044    #[test]
21045    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
21046        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
21047        // the function type, so a declaration that spells `const int` and a
21048        // definition that spells `int` are one entity.
21049        let source = r#"
21050struct Widget {
21051  bool value_params(const int settings, const int supprs);
21052  void pointee_const(const int* p);
21053  void pointer_const(int* const p);
21054  void both_const(const int* const p);
21055  void reference_const(const int& p);
21056  void array_const(const int values[4]);
21057};
21058bool Widget::value_params(int settings, int supprs) { return true; }
21059void Widget::pointer_const(int* p) {}
21060void Widget::both_const(const int* p) {}
21061"#;
21062        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
21063        assert_eq!(
21064            vec!["(int, int)".to_string()],
21065            identity_signatures(&parsed, "Widget.value_params")
21066        );
21067        assert_eq!(
21068            vec!["(int *)".to_string()],
21069            identity_signatures(&parsed, "Widget.pointer_const")
21070        );
21071        assert_eq!(
21072            vec!["(const int *)".to_string()],
21073            identity_signatures(&parsed, "Widget.both_const")
21074        );
21075        // The const that is not top-level still distinguishes the type.
21076        assert_eq!(
21077            vec!["(const int *)".to_string()],
21078            identity_signatures(&parsed, "Widget.pointee_const")
21079        );
21080        assert_eq!(
21081            vec!["(const int &)".to_string()],
21082            identity_signatures(&parsed, "Widget.reference_const")
21083        );
21084        assert_eq!(
21085            vec!["(const int [4])".to_string()],
21086            identity_signatures(&parsed, "Widget.array_const")
21087        );
21088    }
21089
21090    #[test]
21091    fn top_level_parameter_const_still_separates_pointee_overloads() {
21092        let source = r#"
21093struct Widget {
21094  void take(const int* p);
21095  void take(int* p);
21096};
21097"#;
21098        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
21099        assert_eq!(
21100            vec!["(const int *)".to_string(), "(int *)".to_string()],
21101            identity_signatures(&parsed, "Widget.take")
21102        );
21103    }
21104
21105    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
21106        let mut parser = tree_sitter::Parser::new();
21107        parser
21108            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21109            .unwrap();
21110        let tree = parser.parse(source, None).unwrap();
21111        let start = source.find(callable_name).expect("callable declaration");
21112        let declarator =
21113            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
21114        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
21115    }
21116
21117    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
21118        let mut shapes = comparable_shapes(source, callable_name);
21119        assert_eq!(1, shapes.len(), "{shapes:?}");
21120        match shapes.remove(0) {
21121            CppComparableSlot::Shape(shape) => shape,
21122            other => panic!("expected a comparable shape, got {other:?}"),
21123        }
21124    }
21125
21126    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
21127        let mut current = shape.root();
21128        loop {
21129            match shape.node(current) {
21130                CppComparableNode::Named { .. } => return shape.node(current),
21131                CppComparableNode::Pointer { inner, .. }
21132                | CppComparableNode::Reference { inner }
21133                | CppComparableNode::Array { inner } => current = *inner,
21134                CppComparableNode::Generic { base, .. } => current = *base,
21135            }
21136        }
21137    }
21138
21139    #[test]
21140    fn comparable_shape_keeps_pointee_const() {
21141        assert_ne!(
21142            sole_comparable_shape("void f(const char* p);", "f("),
21143            sole_comparable_shape("void f(char* p);", "f(")
21144        );
21145    }
21146
21147    #[test]
21148    fn comparable_shape_keeps_inner_pointer_const() {
21149        assert_ne!(
21150            sole_comparable_shape("void f(int** p);", "f("),
21151            sole_comparable_shape("void f(int* const* p);", "f(")
21152        );
21153    }
21154
21155    #[test]
21156    fn comparable_shape_drops_top_level_pointer_const() {
21157        assert_eq!(
21158            sole_comparable_shape("void f(int* const p);", "f("),
21159            sole_comparable_shape("void f(int* p);", "f(")
21160        );
21161    }
21162
21163    #[test]
21164    fn comparable_shape_drops_top_level_base_const() {
21165        assert_eq!(
21166            sole_comparable_shape("void f(const int p);", "f("),
21167            sole_comparable_shape("void f(int p);", "f(")
21168        );
21169    }
21170
21171    #[test]
21172    fn comparable_shape_decays_top_level_array_to_pointer() {
21173        assert_eq!(
21174            sole_comparable_shape("void f(int a[3]);", "f("),
21175            sole_comparable_shape("void f(int* a);", "f(")
21176        );
21177        assert_eq!(
21178            sole_comparable_shape("void f(int* a[3]);", "f("),
21179            sole_comparable_shape("void f(int** a);", "f(")
21180        );
21181    }
21182
21183    #[test]
21184    fn comparable_shape_keeps_array_behind_pointer() {
21185        assert_ne!(
21186            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
21187            sole_comparable_shape("struct S { void f(int** a); };", "f(")
21188        );
21189    }
21190
21191    #[test]
21192    fn comparable_shape_records_written_name_and_lexical_scope() {
21193        let declared =
21194            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
21195        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
21196        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
21197            panic!("named leaf");
21198        };
21199        assert_eq!(["Msg".to_string()].as_slice(), name.path());
21200        assert_eq!(
21201            ["ns".to_string(), "S".to_string()].as_slice(),
21202            name.lexical_scope()
21203        );
21204        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
21205            panic!("named leaf");
21206        };
21207        assert_eq!(
21208            ["ns".to_string(), "Msg".to_string()].as_slice(),
21209            name.path()
21210        );
21211        assert!(name.lexical_scope().is_empty());
21212        assert_ne!(declared, defined);
21213    }
21214
21215    #[test]
21216    fn comparable_shape_marks_sized_primitive_leaf() {
21217        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
21218        let CppComparableNode::Named {
21219            name, primitive, ..
21220        } = comparable_named_leaf(&shape)
21221        else {
21222            panic!("named leaf");
21223        };
21224        assert!(primitive);
21225        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
21226        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
21227    }
21228
21229    #[test]
21230    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
21231        assert_eq!(
21232            vec![CppComparableSlot::Unstructured],
21233            comparable_shapes("void f(void (*cb)(int));", "f(")
21234        );
21235    }
21236
21237    #[test]
21238    fn comparable_shape_reports_ellipsis_slot() {
21239        let shapes = comparable_shapes("void f(int a, ...);", "f(");
21240        assert_eq!(2, shapes.len(), "{shapes:?}");
21241        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
21242    }
21243
21244    #[test]
21245    fn comparable_shape_keeps_template_argument_const() {
21246        assert_ne!(
21247            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
21248            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
21249        );
21250    }
21251
21252    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
21253    /// file-scope tag that a later `struct inner *` at file scope may name.
21254    #[test]
21255    fn c_file_mints_aggregate_member_tag_at_file_scope() {
21256        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21257        let parsed = parse_cpp_declarations(source, "x.c");
21258        let declarations = parsed.declarations();
21259
21260        assert!(
21261            declarations
21262                .iter()
21263                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21264            "expected a file-scope inner tag, got {declarations:?}"
21265        );
21266        assert!(
21267            declarations
21268                .iter()
21269                .all(|unit| unit.fq_name() != "outer$inner"),
21270            "expected no nested identity, got {declarations:?}"
21271        );
21272        assert!(
21273            declarations
21274                .iter()
21275                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
21276        );
21277        // Members still belong to their own aggregate.
21278        assert!(
21279            declarations
21280                .iter()
21281                .any(|unit| unit.fq_name() == "inner.value")
21282        );
21283        assert!(
21284            declarations
21285                .iter()
21286                .any(|unit| unit.fq_name() == "outer.item")
21287        );
21288
21289        let outer = declarations
21290            .iter()
21291            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
21292            .expect("outer");
21293        assert!(
21294            parsed
21295                .children
21296                .get(outer)
21297                .into_iter()
21298                .flatten()
21299                .all(|child| child.fq_name() != "inner"),
21300            "the tag must not hang off the aggregate it is written inside: {:?}",
21301            parsed.children
21302        );
21303    }
21304
21305    /// A header carries no compilation language of its own, and a `.cpp`
21306    /// translation unit really does declare a nested class. Both keep exactly
21307    /// the C++ extraction they had before the C dialect existed.
21308    #[test]
21309    fn header_and_cpp_files_keep_nested_tag_identity() {
21310        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21311        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
21312            let parsed = parse_cpp_declarations(source, name);
21313            let declarations = parsed.declarations();
21314            assert!(
21315                declarations
21316                    .iter()
21317                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21318                "{name} must keep the nested identity, got {declarations:?}"
21319            );
21320            assert!(
21321                declarations.iter().all(|unit| unit.fq_name() != "inner"),
21322                "{name} must not mint a file-scope tag, got {declarations:?}"
21323            );
21324            assert!(
21325                declarations
21326                    .iter()
21327                    .any(|unit| unit.fq_name() == "outer$inner.value")
21328            );
21329        }
21330    }
21331
21332    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
21333    #[test]
21334    fn uppercase_c_extension_keeps_cpp_tag_scope() {
21335        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21336        let parsed = parse_cpp_declarations(source, "x.C");
21337        assert!(
21338            parsed
21339                .declarations()
21340                .iter()
21341                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
21342        );
21343    }
21344
21345    /// There is no such thing as a partially nested tag in C: every level of a
21346    /// nested aggregate chain lands at the same enclosing scope.
21347    #[test]
21348    fn c_file_mints_every_nesting_level_at_file_scope() {
21349        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
21350        let parsed = parse_cpp_declarations(source, "z.c");
21351        let declarations = parsed.declarations();
21352
21353        for tag in ["a", "b", "c"] {
21354            assert!(
21355                declarations
21356                    .iter()
21357                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
21358                "expected a file-scope {tag}, got {declarations:?}"
21359            );
21360        }
21361        assert!(
21362            declarations
21363                .iter()
21364                .all(|unit| !unit.fq_name().contains('$')),
21365            "no level may keep a nested identity, got {declarations:?}"
21366        );
21367        // Each member still belongs to the aggregate that declares it.
21368        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
21369        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
21370        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
21371    }
21372
21373    /// An enum tag is a tag; its enumerators stay members of the enum, which is
21374    /// what makes them ordinary identifiers at the enum's own (file) scope.
21375    #[test]
21376    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
21377        let source = "struct outer { enum color { RED, GREEN } c; };\n";
21378        let parsed = parse_cpp_declarations(source, "e.c");
21379        let declarations = parsed.declarations();
21380
21381        let color = declarations
21382            .iter()
21383            .find(|unit| unit.is_class() && unit.fq_name() == "color")
21384            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
21385        assert!(
21386            declarations
21387                .iter()
21388                .all(|unit| unit.fq_name() != "outer$color")
21389        );
21390        for enumerator in ["color.RED", "color.GREEN"] {
21391            assert!(
21392                declarations.iter().any(|unit| unit.fq_name() == enumerator),
21393                "expected {enumerator}, got {declarations:?}"
21394            );
21395        }
21396        let children = parsed
21397            .children
21398            .get(color)
21399            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
21400        assert!(
21401            ["color.RED", "color.GREEN"]
21402                .iter()
21403                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
21404            "enumerators must hang off their enum: {children:?}"
21405        );
21406    }
21407
21408    #[test]
21409    fn c_file_mints_member_list_union_at_file_scope() {
21410        let source = "struct outer { union inner { int a; float b; } item; };\n";
21411        let parsed = parse_cpp_declarations(source, "u.c");
21412        let declarations = parsed.declarations();
21413        assert!(
21414            declarations
21415                .iter()
21416                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21417            "expected a file-scope inner union, got {declarations:?}"
21418        );
21419        assert!(
21420            declarations
21421                .iter()
21422                .all(|unit| unit.fq_name() != "outer$inner")
21423        );
21424        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
21425        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
21426    }
21427
21428    /// A tag declared in a namespace member list is not a file-scope tag: the
21429    /// nearest enclosing non-aggregate scope is the namespace.
21430    #[test]
21431    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
21432        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
21433        let parsed = parse_cpp_declarations(source, "n.c");
21434        let declarations = parsed.declarations();
21435        let inner = declarations
21436            .iter()
21437            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
21438            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
21439        assert_eq!(inner.package_name(), "ns");
21440        assert!(
21441            declarations
21442                .iter()
21443                .all(|unit| unit.fq_name() != "ns.outer$inner")
21444        );
21445    }
21446
21447    /// Pins today's treatment of a tag declared inside a function body: the
21448    /// declaration walk does not descend into statement bodies, so no unit is
21449    /// minted for it in either dialect. C block scope is out of scope for the
21450    /// dialect change, and this test proves the change did not disturb it.
21451    #[test]
21452    fn function_local_tags_are_unchanged_in_both_dialects() {
21453        let source =
21454            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
21455        for name in ["y.c", "y.cpp"] {
21456            let parsed = parse_cpp_declarations(source, name);
21457            let declarations = parsed.declarations();
21458            assert!(
21459                declarations
21460                    .iter()
21461                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
21462                "{name}: {declarations:?}"
21463            );
21464            for tag in ["localtag", "deeper", "localtag$deeper"] {
21465                assert!(
21466                    declarations.iter().all(|unit| unit.fq_name() != tag),
21467                    "{name} must not mint {tag}, got {declarations:?}"
21468                );
21469            }
21470        }
21471    }
21472
21473    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
21474    /// re-scope: the typedef name is identical in both dialects.
21475    #[test]
21476    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
21477        let source = "typedef struct { int v; } T;\n";
21478        for name in ["t.c", "t.cpp"] {
21479            let parsed = parse_cpp_declarations(source, name);
21480            let declarations = parsed.declarations();
21481            assert!(
21482                declarations
21483                    .iter()
21484                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
21485                "{name}: {declarations:?}"
21486            );
21487        }
21488    }
21489
21490    /// The same principle for every anonymous aggregate the walk indexes, not
21491    /// just the typedef one: a promoted anonymous union in a member list, an
21492    /// anonymous struct given a receiver type by its declarator, a
21493    /// function-local aggregate, and one inside an inline member function
21494    /// body. The C++ reading used to mint none of these, so a pure C++
21495    /// workspace indexed no members at all (#3301).
21496    #[test]
21497    fn anonymous_aggregate_members_are_identical_in_both_dialects() {
21498        let sources = [
21499            "struct outer { union { int a; float b; }; };\n",
21500            "struct outer { struct { int *ops; } sock; };\n",
21501            "void run(void) { struct { int v; } item; }\n",
21502            "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
21503        ];
21504        for source in sources {
21505            let names = |name: &str| {
21506                parse_cpp_declarations(source, name)
21507                    .declarations()
21508                    .iter()
21509                    .map(|unit| unit.fq_name())
21510                    .collect::<std::collections::BTreeSet<_>>()
21511            };
21512            let c_names = names("a.c");
21513            let cpp_names = names("a.cpp");
21514            assert_eq!(
21515                c_names, cpp_names,
21516                "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
21517            );
21518            assert!(
21519                c_names
21520                    .iter()
21521                    .any(|name| name.contains('.') && name != "outer"),
21522                "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
21523            );
21524        }
21525    }
21526
21527    #[test]
21528    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
21529        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
21530        let parsed = parse_cpp_declarations(source, "socket.c");
21531        let declarations = parsed.declarations();
21532        assert_eq!(
21533            declarations
21534                .iter()
21535                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
21536                .count(),
21537            1,
21538            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
21539        );
21540        for expected in [
21541            "PAL_HANDLE",
21542            "PAL_HANDLE.sock",
21543            "PAL_HANDLE$sock",
21544            "PAL_HANDLE$sock.ops",
21545        ] {
21546            assert!(
21547                declarations.iter().any(|unit| unit.fq_name() == expected),
21548                "expected {expected}, got {declarations:?}"
21549            );
21550        }
21551    }
21552
21553    /// `class` is not C. Source that spells one in a `.c` file is not C code,
21554    /// so it keeps the C++ reading rather than acquiring a half-C identity.
21555    #[test]
21556    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
21557        let source = "class outer { class inner { int v; }; };\n";
21558        let c_parsed = parse_cpp_declarations(source, "k.c");
21559        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
21560        let c_declarations = c_parsed.declarations();
21561        let cpp_declarations = cpp_parsed.declarations();
21562        assert!(
21563            c_declarations
21564                .iter()
21565                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21566            "{c_declarations:?}"
21567        );
21568        assert_eq!(
21569            c_declarations
21570                .iter()
21571                .map(|unit| unit.fq_name())
21572                .collect::<std::collections::BTreeSet<_>>(),
21573            cpp_declarations
21574                .iter()
21575                .map(|unit| unit.fq_name())
21576                .collect::<std::collections::BTreeSet<_>>()
21577        );
21578    }
21579
21580    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
21581    /// every (node, class-like name) pair a tree offers, and require the same
21582    /// answer from both.
21583    ///
21584    /// The release build has no `debug_assertions` agreement check, so this is
21585    /// what pins the two together there.  Both query orders are exercised:
21586    /// document order is what the walk does, and reverse order proves that a
21587    /// question about an earlier byte than one already answered is still
21588    /// filtered back to its own prefix rather than answered from the wider
21589    /// fold.
21590    ///
21591    /// Returns how many questions were answered with a namespace, so a fixture
21592    /// can assert it actually reached the path (#2754).
21593    fn namespace_forward_scan_agreement(source: &str) -> usize {
21594        let mut parser = tree_sitter::Parser::new();
21595        parser
21596            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21597            .unwrap();
21598        let tree = parser.parse(source, None).unwrap();
21599        let root = tree.root_node();
21600        let ancestry = ParentIndex::new(root);
21601
21602        let mut nodes = Vec::new();
21603        let mut names = std::collections::BTreeSet::new();
21604        let mut cursor = root.walk();
21605        let mut stack = vec![root];
21606        while let Some(node) = stack.pop() {
21607            if matches!(
21608                node.kind(),
21609                "class_specifier" | "struct_specifier" | "union_specifier"
21610            ) && let Some(name) = class_like_name(node, source, &ancestry)
21611            {
21612                names.insert(name);
21613            }
21614            nodes.push(node);
21615            stack.extend(node.named_children(&mut cursor));
21616        }
21617        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
21618        assert!(!names.is_empty(), "fixture declares no class-like name");
21619
21620        let mut answered = 0usize;
21621        for reversed in [false, true] {
21622            let mut scan = CppNamespaceForwardScan::default();
21623            let ordered: Vec<_> = if reversed {
21624                nodes.iter().rev().copied().collect()
21625            } else {
21626                nodes.clone()
21627            };
21628            answered = 0;
21629            for node in ordered {
21630                for name in &names {
21631                    scan.advance_to(root, node.start_byte(), source, &ancestry);
21632                    let carried = scan.unique_earlier_forward(name, node);
21633                    assert_eq!(
21634                        carried,
21635                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
21636                        "carried-forward scan and prefix scan disagree about {name} at \
21637                         {} node starting at byte {} (reversed order: {reversed})",
21638                        node.kind(),
21639                        node.start_byte()
21640                    );
21641                    answered += usize::from(carried.is_some());
21642                }
21643            }
21644        }
21645        answered
21646    }
21647
21648    /// A malformed namespace whose forward declarations are the only identity
21649    /// signal left for the class definitions tree-sitter pushed out to file
21650    /// scope.  Both recovered classes open the guard; only the first one is
21651    /// separated from the namespace by nothing but recovery trivia, so only the
21652    /// first one borrows.  The carried-forward scan has to reproduce both
21653    /// answers.
21654    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
21655namespace ns {
21656class Widget;
21657class Gadget;
21658int x = ;
21659}
21660class API Widget {
21661public:
21662    void first();
21663};
21664class API Gadget {
21665public:
21666    void second();
21667};
21668"#;
21669
21670    #[test]
21671    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
21672        assert!(
21673            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
21674            "the fixture must actually reach the namespace-borrow path"
21675        );
21676
21677        // Nothing here may be answered, and the two paths have to agree about
21678        // that too: a clean namespace is not an identity proof, two forwards of
21679        // one name are ambiguous rather than a guess, and a forward inside a
21680        // function body is not at namespace scope.
21681        for source in [
21682            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
21683            r#"#define API
21684namespace ns {
21685class Widget;
21686class Widget;
21687int x = ;
21688}
21689class API Widget {
21690public:
21691    void method();
21692};
21693"#,
21694            r#"#define API
21695namespace ns {
21696void host() {
21697    class Widget;
21698}
21699int x = ;
21700}
21701class API Widget {
21702public:
21703    void method();
21704};
21705"#,
21706        ] {
21707            assert_eq!(
21708                namespace_forward_scan_agreement(source),
21709                0,
21710                "no borrow is justified here: {source}"
21711            );
21712        }
21713    }
21714
21715    /// The fold is incremental, so a walk that asks about steadily later bytes
21716    /// must never re-fold a node an earlier question already folded, and must
21717    /// never skip one that lies between two questions.
21718    #[test]
21719    fn carried_forward_namespace_scan_folds_each_node_once() {
21720        let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
21721        let mut parser = tree_sitter::Parser::new();
21722        parser
21723            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21724            .unwrap();
21725        let tree = parser.parse(source, None).unwrap();
21726        let root = tree.root_node();
21727        let ancestry = ParentIndex::new(root);
21728
21729        let mut incremental = CppNamespaceForwardScan::default();
21730        for cutoff in 0..=source.len() {
21731            incremental.advance_to(root, cutoff, source, &ancestry);
21732        }
21733        let mut whole = CppNamespaceForwardScan::default();
21734        whole.advance_to(root, source.len(), source, &ancestry);
21735
21736        let mut incremental_shape: Vec<_> = incremental
21737            .forwards
21738            .iter()
21739            .map(|(name, forwards)| {
21740                (
21741                    name.clone(),
21742                    forwards
21743                        .iter()
21744                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
21745                        .collect::<Vec<_>>(),
21746                )
21747            })
21748            .collect();
21749        let mut whole_shape: Vec<_> = whole
21750            .forwards
21751            .iter()
21752            .map(|(name, forwards)| {
21753                (
21754                    name.clone(),
21755                    forwards
21756                        .iter()
21757                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
21758                        .collect::<Vec<_>>(),
21759                )
21760            })
21761            .collect();
21762        incremental_shape.sort();
21763        whole_shape.sort();
21764        for (_, forwards) in &mut incremental_shape {
21765            forwards.sort();
21766        }
21767        for (_, forwards) in &mut whole_shape {
21768            forwards.sort();
21769        }
21770
21771        assert!(!whole_shape.is_empty(), "fixture folds no forward");
21772        assert_eq!(
21773            incremental_shape, whole_shape,
21774            "one byte at a time must fold exactly what one whole pass folds"
21775        );
21776    }
21777
21778    /// Drive the region reparse and the whitespace-padded reparse it replaced
21779    /// over the same region and require identical trees.
21780    ///
21781    /// The release build has no `debug_assertions` agreement check, so this is
21782    /// what pins the two together there. Each body is reparsed at its own
21783    /// offset and again after a long prefix, because the prefix is the whole
21784    /// difference between the two techniques: the padded parse lexes it as
21785    /// whitespace, the included-range parse never sees it, and the tree has to
21786    /// come out the same either way (#2788).
21787    fn fragmented_class_reparse_agreement(body: &str) {
21788        for prefix in [
21789            String::new(),
21790            "// leading comment\n".to_string(),
21791            // A body starts just after its class head's `{`, which is normally
21792            // mid-line: the padded parse then has spaces before the region on
21793            // the region's own line, and the included-range parse has nothing
21794            // at all before it.
21795            "class Widget : public Base { ".to_string(),
21796            "namespace filler {\n".to_string()
21797                + &"struct Filler { int member; };\n".repeat(200)
21798                + "}\n",
21799            "namespace filler {\n".to_string()
21800                + &"struct Filler { int member; };\n".repeat(200)
21801                + "}\nclass Widget : public Base { ",
21802        ] {
21803            let source = format!("{prefix}{body}");
21804            let start = prefix.len();
21805            let end = source.len();
21806            let region = cpp_reparse_fragmented_class_body(&source, start, end)
21807                .expect("the region reparse must produce a tree");
21808            let padded = cpp_reparse_padded_class_body(&source, start, end)
21809                .expect("the padded reparse must produce a tree");
21810            assert_eq!(
21811                cpp_tree_shape(&region),
21812                cpp_tree_shape(&padded),
21813                "region and padded reparse disagree at offset {start} of {end} bytes"
21814            );
21815            assert_eq!(
21816                region.root_node().start_byte(),
21817                start,
21818                "the reparsed region keeps its original offsets"
21819            );
21820        }
21821    }
21822
21823    #[test]
21824    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
21825        // A conditional immediately after an access label: the shape the padded
21826        // technique was kept for, because the directive and its macro name
21827        // become an ERROR plus the following declaration's apparent type.
21828        fragmented_class_reparse_agreement(
21829            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
21830        );
21831        fragmented_class_reparse_agreement(
21832            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
21833        );
21834        // The merged inline constructor and the nested fragmented bodies the
21835        // #938 recovery reads out of a reparse.
21836        fragmented_class_reparse_agreement(
21837            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
21838        );
21839        fragmented_class_reparse_agreement(
21840            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
21841        );
21842        // A body that is not member-shaped at all still has to reparse the same
21843        // way, because the admission gate reads the tree to reject it.
21844        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
21845    }
21846
21847    /// A header shaped like the generated ones this walk is slow on: many
21848    /// enums, classes whose members share the enums' names, nested enums, an
21849    /// ownerless enumerator, and namespaced repeats of all of it.
21850    fn many_enums_and_mixed_declarations() -> String {
21851        let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
21852        for index in 0..40 {
21853            let _ = write!(
21854                source,
21855                "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
21856                 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
21857                 class Color{index}Like {{ public: int member{index}; }};\n"
21858            );
21859        }
21860        source.push_str("namespace outer {\n");
21861        for index in 0..20 {
21862            let _ = write!(
21863                source,
21864                "enum Shade{index} {{ DARK{index} }};\n\
21865                 struct Shade{index}Holder {{ int field{index}; }};\n"
21866            );
21867        }
21868        source.push_str("}\n");
21869        source
21870    }
21871
21872    /// Drive [`CppFieldOwnerIndex`] and the declaration scan it replaced over
21873    /// every question a fixture's declarations can ask, and require the same
21874    /// answer from both.
21875    ///
21876    /// The release build has no `debug_assertions` agreement check, so this is
21877    /// what pins the two together there. The index is fed one declaration at a
21878    /// time and every question is re-asked after each one, which is what proves
21879    /// the incremental record agrees -- a whole-set rebuild would pass a weaker
21880    /// test. Each of the fixture's own fields is also fed in restated as a
21881    /// declaration of another file: the scan ignores those because it asks
21882    /// about the asking unit's own source, and the index has to ignore them for
21883    /// the same reason.
21884    ///
21885    /// Returns how many questions the fixture answered `true`, so a caller can
21886    /// assert that it actually reached the path (#2786).
21887    fn field_owner_index_agreement(source: &str, name: &str) -> usize {
21888        let parsed = parse_cpp_declarations(source, name);
21889        let file = ProjectFile::new(std::env::temp_dir(), name);
21890        let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
21891
21892        let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
21893        declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
21894
21895        let foreign: Vec<CodeUnit> = declarations
21896            .iter()
21897            .filter(|unit| unit.kind() == CodeUnitType::Field)
21898            .map(|unit| {
21899                CodeUnit::new_fq(
21900                    elsewhere.clone(),
21901                    unit.kind(),
21902                    unit.package_name().to_string(),
21903                    unit.short_name().to_string(),
21904                    unit.fq().clone(),
21905                )
21906            })
21907            .collect();
21908
21909        // One owner chain deeper than any C++ short name reaches today
21910        // (`cpp_member_fq`: at most one `.`, separating the owner chain from
21911        // the member). The scan asks `starts_with("owner.")`, so a field like
21912        // this answers for every owner in its chain, and the index has to
21913        // record every one of them rather than only the innermost.
21914        let mut packages: Vec<String> = declarations
21915            .iter()
21916            .map(|unit| unit.package_name().to_string())
21917            .collect();
21918        packages.push(String::new());
21919        packages.sort();
21920        packages.dedup();
21921        let deeper: Vec<CodeUnit> = packages
21922            .iter()
21923            .map(|package_name| {
21924                CodeUnit::new_fq(
21925                    file.clone(),
21926                    CodeUnitType::Field,
21927                    package_name.clone(),
21928                    "SynthOwner.middle.leaf".to_string(),
21929                    cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
21930                )
21931            })
21932            .collect();
21933
21934        // Every (package, owner) pair anything could ask about: each unit's own
21935        // short name, each dotted prefix of it, and the empty owner an
21936        // anonymous enum asks with (#2140).
21937        let mut questions: Vec<(String, String)> = Vec::new();
21938        for unit in declarations.iter().chain(deeper.iter()) {
21939            let package_name = unit.package_name().to_string();
21940            let short_name = unit.short_name();
21941            questions.push((package_name.clone(), short_name.to_string()));
21942            questions.push((package_name.clone(), String::new()));
21943            for (offset, _) in short_name.match_indices('.') {
21944                questions.push((package_name.clone(), short_name[..offset].to_string()));
21945            }
21946        }
21947        questions.sort();
21948        questions.dedup();
21949
21950        let mut index = CppFieldOwnerIndex::default();
21951        let mut recorded: Vec<&CodeUnit> = Vec::new();
21952        let mut answered = 0usize;
21953        for unit in foreign
21954            .iter()
21955            .chain(declarations.iter())
21956            .chain(deeper.iter())
21957        {
21958            index.record(unit, &file);
21959            recorded.push(unit);
21960            for (package_name, owner_short_name) in &questions {
21961                let carried = index.owns_fields(package_name, owner_short_name);
21962                assert_eq!(
21963                    carried,
21964                    cpp_declarations_hold_owned_fields(
21965                        recorded.iter().copied(),
21966                        &file,
21967                        package_name,
21968                        owner_short_name
21969                    ),
21970                    "the carried field index and the declaration scan disagree about \
21971                     {package_name:?}/{owner_short_name:?} after recording {}",
21972                    unit.fq_name()
21973                );
21974                answered += usize::from(carried);
21975            }
21976        }
21977
21978        // The whole-set build the first question performs must land on the same
21979        // index the incremental record built.
21980        let rebuilt = CppFieldOwnerIndex::of(
21981            foreign
21982                .iter()
21983                .chain(declarations.iter())
21984                .chain(deeper.iter()),
21985            &file,
21986        );
21987        for (package_name, owner_short_name) in &questions {
21988            assert_eq!(
21989                rebuilt.owns_fields(package_name, owner_short_name),
21990                index.owns_fields(package_name, owner_short_name),
21991                "a rebuilt index must answer what the incremental one answers for \
21992                 {package_name:?}/{owner_short_name:?}"
21993            );
21994        }
21995        answered
21996    }
21997
21998    /// The one thing the field index cannot absorb by addition: a deferred
21999    /// replacement of a declaration that owns children removes those children.
22000    ///
22001    /// The first enum builds the index, the struct records `Color.RED` into it,
22002    /// the body-less second `Color` replaces the first and takes `Color.RED`
22003    /// with it, and the last enum then asks about owner `Color`. An index that
22004    /// survived that removal answers `true` where the declarations say `false`,
22005    /// which is exactly what the in-walk agreement assertion catches (#2786).
22006    #[test]
22007    fn a_replacement_that_removes_children_drops_the_field_index() {
22008        let source =
22009            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
22010        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
22011        let mut names: Vec<_> = parsed
22012            .declarations()
22013            .iter()
22014            .map(|unit| unit.fq_name())
22015            .collect();
22016        names.sort();
22017        assert_eq!(
22018            names,
22019            vec![
22020                "Color".to_string(),
22021                "First".to_string(),
22022                "First.A".to_string()
22023            ],
22024            "the replaced Color owns no field any more"
22025        );
22026    }
22027
22028    /// A recovery that re-declares what the file already declared mints
22029    /// nothing.
22030    ///
22031    /// The reparse walk replaces the outer `Widget`, which removes its method,
22032    /// and then re-creates that method from the region. Creation alone would
22033    /// call the method recovered; it was there before the recovery opened, so
22034    /// the recovered set is empty and only the region's own reparse window is
22035    /// recorded (#2787).
22036    #[test]
22037    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
22038        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
22039                      BEGIN_NS\n\
22040                      namespace demo { struct Widget { void doWork(); }; }\n\
22041                      END_NS\n";
22042        let parsed = parse_cpp_declarations(source, "restored.cpp");
22043        let recovered: Vec<String> = parsed
22044            .materialization_records
22045            .iter()
22046            .filter_map(|record| match record {
22047                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
22048                _ => None,
22049            })
22050            .collect();
22051        assert!(
22052            recovered.is_empty(),
22053            "the region declares nothing the file did not already declare: {recovered:?}"
22054        );
22055        let mut names: Vec<String> = parsed
22056            .declarations()
22057            .iter()
22058            .map(|unit| unit.fq_name())
22059            .collect();
22060        names.sort();
22061        assert_eq!(
22062            names,
22063            vec![
22064                "demo".to_string(),
22065                "demo.Widget".to_string(),
22066                "demo.Widget.doWork".to_string(),
22067            ]
22068        );
22069    }
22070
22071    /// Four macro-sentinel recoveries in one file (#941). Each one must record
22072    /// exactly the declarations it minted -- not the ones an earlier recovery
22073    /// minted, and not the file's other declarations -- in start-byte order,
22074    /// and each record must carry its own reparse window (#2787).
22075    #[test]
22076    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
22077        let mut source = String::new();
22078        for index in 0..4 {
22079            let _ = write!(
22080                source,
22081                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
22082            );
22083        }
22084        source.push_str("void outside() {}\n");
22085        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
22086
22087        let recovered: Vec<(String, (usize, usize))> = parsed
22088            .materialization_records
22089            .iter()
22090            .filter_map(|record| match record {
22091                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
22092                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
22093                }
22094                _ => None,
22095            })
22096            .collect();
22097
22098        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
22099        for index in 0..4 {
22100            // The window the reparse covers: everything after the opening
22101            // sentinel token up to the newline before the closing one.
22102            let region = format!("namespace demo{index}");
22103            let region_start = source.find(&region).expect("each region is in the source");
22104            let start = source[..region_start]
22105                .rfind("BEGIN_NS")
22106                .expect("each region opens with a sentinel")
22107                + "BEGIN_NS".len();
22108            let end = start
22109                + source[start..]
22110                    .find("END_NS")
22111                    .expect("each region closes with a sentinel")
22112                - 1;
22113            let window = (start, end);
22114            for name in [
22115                format!("demo{index}"),
22116                format!("demo{index}.Widget{index}"),
22117                format!("demo{index}.Widget{index}.doWork{index}"),
22118            ] {
22119                expected.push((name, window));
22120            }
22121        }
22122        assert_eq!(
22123            recovered, expected,
22124            "each recovery records its own minted declarations, in order"
22125        );
22126        assert!(
22127            parsed
22128                .declarations()
22129                .iter()
22130                .any(|unit| unit.fq_name() == "outside"),
22131            "the declaration outside every region stays parsed and unrecovered"
22132        );
22133    }
22134
22135    #[test]
22136    fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
22137        assert!(
22138            field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
22139            "the fixture must actually own fields"
22140        );
22141
22142        // The shapes that make the two implementations diverge if the index
22143        // records the wrong keys: a nested enum's owner is its whole dotted
22144        // chain, a class named like an enum owns fields under that same name, a
22145        // `$` in a short name is an owner separator the dotted prefix rule must
22146        // not split on, and the same enum name in two namespaces is two owners.
22147        for (source, name) in [
22148            ("struct S { enum E { V }; };\n", "nested.hpp"),
22149            (
22150                "enum Color { RED };\nstruct Color { int RED; };\n",
22151                "class-like.c",
22152            ),
22153            ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
22154            (
22155                "enum E { V };\nnamespace ns { enum E { V }; }\n",
22156                "repeated.hpp",
22157            ),
22158            ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
22159        ] {
22160            field_owner_index_agreement(source, name);
22161        }
22162    }
22163
22164    /// `blocks` copies of one plain class-with-methods template. The class the
22165    /// question is about is always the first one, so the only thing that
22166    /// changes between two of these sources is how much unrelated tree
22167    /// surrounds it.
22168    fn repeated_class_blocks(blocks: usize) -> String {
22169        let mut source = String::from("namespace demo {\n");
22170        for index in 0..blocks {
22171            let _ = write!(
22172                source,
22173                "\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"
22174            );
22175        }
22176        source.push_str("\n}\n");
22177        source
22178    }
22179
22180    /// #1496: asking whether a recovered class shape claims one range must cost
22181    /// the path to that range, not a pass over the whole translation unit.
22182    ///
22183    /// The C++ inverse scan asks this once per candidate type reference, so a
22184    /// whole-tree walk makes one file's scan quadratic in its own size. Both
22185    /// sources here answer `None` -- these are plain classes that no recovery
22186    /// shape claims -- which is exactly the case that used to pay full price.
22187    #[test]
22188    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
22189        let mut answers = Vec::new();
22190        let mut visits = Vec::new();
22191        let mut node_counts = Vec::new();
22192        for blocks in [200usize, 400] {
22193            let source = repeated_class_blocks(blocks);
22194            let mut parser = tree_sitter::Parser::new();
22195            parser
22196                .set_language(&tree_sitter_cpp::LANGUAGE.into())
22197                .unwrap();
22198            let tree = parser.parse(&source, None).unwrap();
22199            let start_byte = source.find("class Widget0 ").expect("first class");
22200            let end_byte = start_byte
22201                + source[start_byte..]
22202                    .find("};")
22203                    .expect("first class terminator")
22204                + "};".len();
22205            let range = Range {
22206                start_byte,
22207                end_byte,
22208                start_line: 0,
22209                end_line: 0,
22210            };
22211            reset_recovered_class_body_node_visits_for_test();
22212            let recovered_export_classes =
22213                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
22214            answers.push(recovered_class_body_at(
22215                &recovered_export_classes,
22216                tree.root_node(),
22217                &source,
22218                "Widget0",
22219                &range,
22220            ));
22221            visits.push(recovered_class_body_node_visits_for_test());
22222            let mut nodes = 0usize;
22223            let mut stack = vec![tree.root_node()];
22224            while let Some(node) = stack.pop() {
22225                nodes += 1;
22226                let mut cursor = node.walk();
22227                stack.extend(node.named_children(&mut cursor));
22228            }
22229            node_counts.push(nodes);
22230        }
22231
22232        assert_eq!(
22233            answers,
22234            vec![None, None],
22235            "no recovered shape claims a plain class"
22236        );
22237        assert_eq!(
22238            visits[0], visits[1],
22239            "the walk must follow the range's own path, so doubling the unrelated \
22240             classes must not change the node count: {visits:?} over trees of \
22241             {node_counts:?} nodes"
22242        );
22243        assert!(
22244            visits[1] * 20 < node_counts[1],
22245            "the walk must stay far below one pass over the tree: {visits:?} over \
22246             trees of {node_counts:?} nodes"
22247        );
22248    }
22249
22250    #[test]
22251    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
22252        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
22253        let mut parser = tree_sitter::Parser::new();
22254        parser
22255            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22256            .expect("C++ grammar");
22257        let tree = parser.parse(source, None).expect("fixture tree");
22258        let mut stack = vec![tree.root_node()];
22259        let mut recovered = None;
22260        while let Some(node) = stack.pop() {
22261            if node.kind() == "field_declaration"
22262                && let Some(field) = recovered_function_like_field_declarator(node, source)
22263            {
22264                recovered = Some((node, field.name));
22265                break;
22266            }
22267            let mut cursor = node.walk();
22268            stack.extend(node.named_children(&mut cursor));
22269        }
22270        let (declaration, name) = recovered.unwrap_or_else(|| {
22271            panic!(
22272                "pointer-wrapped macro field was not recovered: {}",
22273                tree.root_node().to_sexp()
22274            )
22275        });
22276        assert_eq!(node_text(name, source), "handshake");
22277        let recovered =
22278            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
22279        assert_eq!(recovered.pointer_depth(), 1);
22280        assert_eq!(
22281            render_cpp_field_signature(declaration, name, source),
22282            "struct handshake * handshake;"
22283        );
22284    }
22285
22286    #[test]
22287    fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
22288        let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
22289        let mut parser = tree_sitter::Parser::new();
22290        parser
22291            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22292            .expect("C++ grammar");
22293        let tree = parser.parse(source, None).expect("fixture tree");
22294        let mut stack = vec![tree.root_node()];
22295        let mut recovered = None;
22296        while let Some(node) = stack.pop() {
22297            if let Some(field) = recovered_pyobject_head_field(node, source) {
22298                recovered = Some((node, field));
22299                break;
22300            }
22301            let mut cursor = node.walk();
22302            stack.extend(node.named_children(&mut cursor));
22303        }
22304        let (declaration, recovered) = recovered.unwrap_or_else(|| {
22305            panic!(
22306                "pointer field after PyObject_HEAD was not recovered: {}",
22307                tree.root_node().to_sexp()
22308            )
22309        });
22310        assert_eq!(node_text(recovered.name, source), "image");
22311        assert_eq!(recovered.pointer_depth(), 1);
22312        assert_eq!(
22313            render_cpp_field_signature(declaration, recovered.declarator, source),
22314            "ImagingObject *image;"
22315        );
22316    }
22317}