Skip to main content

brokk_bifrost_cpp/
declarations.rs

1//! The C++ declaration walk, including the macro-sentinel error recovery.
2//!
3//! Every function here is a pure function of a parsed tree and its source text.
4//! `analyzer/cpp/adapter.rs` in `brokk-bifrost-analysis` drives
5//! [`CppVisitor`] out of `LanguageAdapter::parse_file`.
6
7use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use brokk_bifrost_core::analyzer::common::{
10    node_source_text, parse_source_ranges_with_cancellation, parse_source_region,
11};
12use brokk_bifrost_core::analyzer::fq_name::{
13    FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
14};
15use brokk_bifrost_core::analyzer::model::{
16    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
17    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
18    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
19    ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
20    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
21};
22use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
23use brokk_bifrost_core::analyzer::structural::materialization::{
24    GenerationKind, MaterializationRecord,
25};
26use brokk_bifrost_core::analyzer::tree_walk::{
27    NodeKindIds, ParentIndex, WalkControl, children_iter, named_children_iter,
28    push_children_reversed, push_named_children_reversed, walk_named_tree_preorder,
29};
30use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
31use brokk_bifrost_core::hash::{HashMap, HashSet};
32use regex::Regex;
33use tree_sitter::{Node, Parser, Tree};
34
35/// Intern one qualified-name segment in the process-global interner.
36fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
37    segment_interner().intern(text, kind)
38}
39
40/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
41/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
42/// `::` head is exactly the mixed-separator store issue #1163 is about; the
43/// structured form records each namespace component, and the equivalence check
44/// renders it natively (with `::` between adjacent Package segments) so it
45/// round-trips to the legacy string. Splitting the already-joined string here is
46/// the M1 bridge — the legacy strings stay authoritative until M3.
47fn cpp_push_package(fq: &mut FqName, package_name: &str) {
48    for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
49        fq.push(cpp_segment(component, SegmentKind::Package));
50    }
51}
52
53/// C++ namespace paths are stored `::`-joined in `package_name` (issue #1163).
54const CPP_PACKAGE_SEPARATOR: &str = "::";
55
56/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
57/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
58/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
59/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
60/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
61/// cpp-specific native rendering rule is needed for this chain.
62fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
63    let mut first = true;
64    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
65    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
66    // entry point, not re-inference of an already-structured name.
67    for component in chain.split('$').filter(|c| !c.is_empty()) {
68        let kind = if first {
69            SegmentKind::Type
70        } else {
71            SegmentKind::Nested
72        };
73        fq.push(cpp_segment(component, kind));
74        first = false;
75    }
76}
77
78/// Structured name for a C++ namespace module: every `::`-separated component is
79/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
80/// `short_name` with an empty `package_name`).
81fn cpp_namespace_fq(full_name: &str) -> FqName {
82    let mut fq = FqName::new();
83    cpp_push_package(&mut fq, full_name);
84    fq
85}
86
87/// The per-level namespace components a `namespace_definition`'s `name` field
88/// declares.
89///
90/// A C++17 nested definition (`namespace a::b::c`) parses as a
91/// `nested_namespace_specifier` whose named children are the per-level
92/// `namespace_identifier`s plus, for three or more levels, a further
93/// `nested_namespace_specifier`; the `::` separators, the optional per-level
94/// `inline`, and the leading global `::` are all anonymous tokens the walk
95/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
96/// segment path as the expanded `namespace a { namespace b { } }` form.
97///
98/// A shape outside that grammar is the deliberately ill-formed source the
99/// diagnostic corpora carry. Those keep their historical single-component
100/// reading of the raw name text, which the caller still joins to the lexical
101/// namespace exactly as before.
102fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
103    let mut components = Vec::new();
104    let mut stack = vec![node];
105    while let Some(current) = stack.pop() {
106        match current.kind() {
107            "namespace_identifier" | "identifier" => {
108                components.push(normalize_cpp_whitespace(node_text(current, source)));
109            }
110            "nested_namespace_specifier" => {
111                for index in (0..current.named_child_count()).rev() {
112                    stack.push(
113                        current
114                            .named_child(index)
115                            .expect("index below the node's own named child count"),
116                    );
117                }
118            }
119            _ => return cpp_raw_namespace_name_components(node, source),
120        }
121    }
122    if components.iter().any(String::is_empty) {
123        return cpp_raw_namespace_name_components(node, source);
124    }
125    components
126}
127
128/// The historical reading of a namespace name node: its whole source text as
129/// one component, with a leading global `::` marker dropped so the caller's
130/// global-scope handling stays the AST boundary rather than a text prefix.
131///
132/// This is the recovery path for source outside the C++ grammar, so the text it
133/// returns can carry a separator that names nothing. react-native-windows
134/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
135/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
136/// trailing separator with no tail. The caller stores this component in
137/// `short_name` and derives the fq by splitting it back apart, so an empty tail
138/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
139/// here, at the one place the malformed text enters, rather than leaving each
140/// consumer to guard (#2353, a variant of #1878).
141fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
142    let start = node
143        .child(0)
144        .filter(|child| !child.is_named() && child.kind() == "::")
145        .map_or(node.start_byte(), |marker| marker.end_byte());
146    let text = normalize_cpp_whitespace(
147        source
148            .get(start..node.end_byte())
149            .expect("namespace name node covers one source range"),
150    );
151    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
152    if text.is_empty() {
153        return Vec::new();
154    }
155    vec![text]
156}
157
158/// Return the named namespace path that structurally encloses `node`.
159///
160/// This intentionally follows namespace AST ancestors rather than inspecting
161/// source text. Anonymous namespaces are not representable in the legacy C++
162/// package field, so a path containing one fails closed.
163fn cpp_lexical_namespace_name<'tree>(
164    node: Node<'tree>,
165    source: &str,
166    ancestry: &ParentIndex<'tree>,
167) -> Option<String> {
168    let mut components = Vec::new();
169    let mut ancestor = ancestry.parent(node);
170    while let Some(current) = ancestor {
171        if current.kind() == "namespace_definition" {
172            let name_node = current.child_by_field_name("name")?;
173            let name = normalize_cpp_whitespace(node_text(name_node, source));
174            if name.is_empty() {
175                return None;
176            }
177            components.push(name);
178        }
179        ancestor = ancestry.parent(current);
180    }
181    if components.is_empty() {
182        return None;
183    }
184    components.reverse();
185    // Same shared empty-component decision as `cpp_push_package`, which splits
186    // this string back apart: an enclosing namespace whose own name node is
187    // malformed contributes a component carrying its own separator (#2353).
188    Some(
189        normalize_joined(
190            &components.join(CPP_PACKAGE_SEPARATOR),
191            CPP_PACKAGE_SEPARATOR,
192        )
193        .into_owned(),
194    )
195}
196
197/// Nested-class `$` join for short names. An anonymous parent class (empty
198/// short_name) contributes no segment: the FqName bridge drops empty
199/// components, so a bare `parent$child` join would desync `short_name` from
200/// the fq and trip the package/short boundary assert in
201/// `CodeUnit::with_signature_and_fq` (#2140).
202fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
203    if parent_short.is_empty() {
204        name.to_string()
205    } else {
206        format!("{parent_short}${name}")
207    }
208}
209
210/// Member `.` join for short names; same anonymous-parent guard as
211/// [`cpp_join_nested_short`] (#2140).
212fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
213    if parent_short.is_empty() {
214        name.to_string()
215    } else {
216        format!("{parent_short}.{name}")
217    }
218}
219
220/// Structural fq for a leaf declaration: the parent unit's fq plus this
221/// declaration's own name as one segment (or the package segments plus the
222/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
223/// chain, so a literal `$` inside a source identifier (Cython template
224/// substitution points, gcc `$`-identifiers) survives instead of corrupting
225/// the chain and tripping the package/short boundary assert (#2140).
226fn cpp_leaf_fq(
227    package_name: &str,
228    parent: Option<&CodeUnit>,
229    name: &str,
230    kind_if_nested: SegmentKind,
231    kind_if_top: SegmentKind,
232) -> FqName {
233    if let Some(parent) = parent {
234        parent
235            .fq()
236            .clone()
237            .with_pushed(cpp_segment(name, kind_if_nested))
238    } else {
239        let mut fq = FqName::new();
240        cpp_push_package(&mut fq, package_name);
241        fq.push(cpp_segment(name, kind_if_top));
242        fq
243    }
244}
245
246/// Structured name for a member unit (function, field, enumerator). The
247/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
248/// the member has an owner, by `.member`; free functions and globals have no
249/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
250/// C++ member names never contain a literal `.`, so the single `.` (if any)
251/// separates the owner chain from the member.
252pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
253    let mut fq = FqName::new();
254    cpp_push_package(&mut fq, package_name);
255    match short_name.rsplit_once('.') {
256        Some((owner_chain, member)) => {
257            cpp_push_type_chain(&mut fq, owner_chain);
258            fq.push(cpp_segment(member, SegmentKind::Member));
259        }
260        None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
261    }
262    fq
263}
264
265#[derive(Clone)]
266pub struct ScopeInfo {
267    package_name: String,
268    module: Option<CodeUnit>,
269    class_unit: Option<CodeUnit>,
270    template_signature: Option<String>,
271    template_metadata: Option<CppTemplateMetadata>,
272    declarations_are_fields: bool,
273    recovered_specialization_member_scope: bool,
274    /// Namespace targets of every `using namespace X;` directive lexically
275    /// visible at this point in the file (declaration order), threaded
276    /// forward sibling-by-sibling by the sequential container walk (see
277    /// `CppWork::Siblings`). An out-of-line member definition written as a
278    /// bare `Class::method` at file/namespace scope with no enclosing
279    /// `namespace {}` block (issue #1093, e.g. log4cxx's
280    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
281    /// const { ... }`) has no other structural signal for which namespace
282    /// actually owns `Class`; this is the best-effort candidate list used to
283    /// recover it so the definition's indexed identity matches its header
284    /// declaration's.
285    visible_using_namespaces: Vec<String>,
286}
287
288struct CppContainer<'tree> {
289    node: Node<'tree>,
290    scope: ScopeInfo,
291}
292
293struct CppNodeWork<'tree> {
294    node: Node<'tree>,
295    scope: ScopeInfo,
296}
297
298/// Cursor over one container's remaining named children, processed one at a
299/// time (rather than all at once) so a `using namespace X;` sibling can
300/// update `scope.visible_using_namespaces` for the siblings that follow it,
301/// matching real C++ using-directive semantics. Nested container work is
302/// still pushed and fully drained before the cursor resumes (stack LIFO
303/// order), preserving the original left-to-right visitation order.
304struct CppSiblingsWork<'tree> {
305    children: std::vec::IntoIter<Node<'tree>>,
306    scope: ScopeInfo,
307}
308
309enum CppWork<'tree> {
310    Container(CppContainer<'tree>),
311    Node(CppNodeWork<'tree>),
312    Siblings(CppSiblingsWork<'tree>),
313}
314
315fn class_like_name<'tree>(
316    node: Node<'tree>,
317    source: &str,
318    ancestry: &ParentIndex<'tree>,
319) -> Option<String> {
320    let best = class_like_name_from_children(node, source);
321    if let Some(parent) = ancestry.parent(node)
322        && matches!(
323            parent.kind(),
324            "declaration" | "field_declaration" | "function_definition"
325        )
326        // A class_specifier carrying its own body proves the grammar name is
327        // the real class name: a sibling declarator then declares an object
328        // (`class X {} x;`), never a displaced class name. The gate matters
329        // when the class name is itself an all-caps token (`X`, `API`) --
330        // without it the export-macro re-read below steals the object
331        // declarator's name for the class (#2283). The genuine export-macro
332        // shapes leave the class_specifier bodyless, the same invariant
333        // recover_malformed_exported_multiple_base_class already gates on.
334        && cpp_body_node(node).is_none()
335        && node
336            .child_by_field_name("name")
337            .map(|name_node| {
338                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
339            })
340            .unwrap_or(false)
341        && let Some(recovered) = exported_class_name_from_node(parent, source)
342        && best.as_deref() != Some(recovered.as_str())
343    {
344        return Some(recovered);
345    }
346    best.or_else(|| {
347        node.child_by_field_name("name")
348            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
349            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
350    })
351}
352
353fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
354    let mut grammar_name = None;
355    if let Some(name_node) = node.child_by_field_name("name") {
356        let name = normalize_cpp_whitespace(node_text(name_node, source));
357        if name.is_empty() {
358            return None;
359        }
360        if !cpp_export_macro_token(&name) {
361            return Some(name);
362        }
363        grammar_name = Some(name);
364    }
365
366    let mut best = None;
367    let mut cursor = node.walk();
368    let mut stack = Vec::new();
369    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
370        if matches!(
371            child.kind(),
372            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
373        ) {
374            break;
375        }
376        stack.push(child);
377    }
378
379    while let Some(current) = stack.pop() {
380        if matches!(current.kind(), "type_identifier" | "identifier") {
381            let name = normalize_cpp_whitespace(node_text(current, source));
382            if !name.is_empty() && !cpp_export_macro_token(&name) {
383                best = Some(name);
384            }
385            continue;
386        }
387
388        push_named_children_reversed(current, &mut stack);
389    }
390    best.or(grammar_name)
391}
392
393pub fn cpp_export_macro_token(token: &str) -> bool {
394    token
395        .chars()
396        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
397}
398
399struct RecoveredExportedClass<'tree> {
400    declaration_node: Node<'tree>,
401    name: String,
402    body: Option<Node<'tree>>,
403    raw_supertypes: Option<Vec<String>>,
404    uses_initializer_body: bool,
405    /// Present only for the fragmented multiple-base export shape (issue #938).
406    /// Carries the true class-body byte region -- the members tree-sitter scattered
407    /// out of the recovered node -- so they can be reparsed and re-owned as members
408    /// rather than lost inside the truncated `initializer_list` stand-in.
409    fragmented_body: Option<FragmentedExportBody>,
410}
411
412struct RecoveredFunctionLikeExportClassPair {
413    name: String,
414    range: Range,
415    raw_supertypes: Option<Vec<String>>,
416    fragmented_body: FragmentedExportBody,
417}
418
419struct RecoveredEmbeddedFunctionLikeExportClass {
420    name: String,
421    range: Range,
422    raw_supertypes: Vec<String>,
423    fragmented_body: FragmentedExportBody,
424}
425
426/// The recovered class-body geometry for a fragmented multiple-base export class.
427/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
428/// verbatim for a region reparse (issue #941 machinery) so every recovered member
429/// keeps its exact original byte/line position. `class_range` is the full class
430/// navigation range spanning to the displaced closing brace.
431struct FragmentedExportBody {
432    reparse_start: usize,
433    reparse_end: usize,
434    class_range: Range,
435}
436
437fn recovered_fragmented_export_body(
438    body: Node<'_>,
439    class_range: Range,
440) -> Option<FragmentedExportBody> {
441    let open = body.child(0).filter(|child| child.kind() == "{")?;
442    let close = body
443        .child(body.child_count().saturating_sub(1))
444        .filter(|child| child.kind() == "}" && !child.is_missing());
445    Some(FragmentedExportBody {
446        reparse_start: open.end_byte(),
447        // A zero-width missing `}` contributes no source byte. Keep the whole
448        // body range in that case; subtracting one byte would discard the last
449        // member's semicolon and make the otherwise valid region unsafe to
450        // index. A real close token is excluded by its structured start.
451        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
452        class_range,
453    })
454}
455
456struct DisplacedFragmentNamespaceBoundary<'tree> {
457    class_close: Node<'tree>,
458    class_semicolon: Node<'tree>,
459    namespace_items: Vec<Node<'tree>>,
460}
461
462/// Result of validating a reparsed fragmented class body.  A complete tree can
463/// safely consume the whole region.  A partial tree may contain only the exact
464/// class-named constructor that tree-sitter merged into an access label; its
465/// remaining siblings must stay on the ordinary outer walk.
466enum FragmentedExportMembers {
467    Complete(Tree),
468    ConditionalConstructor(Tree),
469}
470
471#[derive(Clone, Copy)]
472struct DisplacedMacroClassTail {
473    split_index: usize,
474    class_range: Range,
475}
476
477fn recover_exported_class_declaration<'tree>(
478    node: Node<'tree>,
479    source: &str,
480) -> Option<RecoveredExportedClass<'tree>> {
481    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
482        return Some(recovered);
483    }
484
485    let class_node = first_class_like_child(node)?;
486    if let Some(name_node) = class_node.child_by_field_name("name") {
487        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
488        if cpp_export_macro_token(&class_name) {
489            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
490            // Name declarator. Only a bare declarator can be the displaced class name;
491            // wrappers describe an object whose type merely happens to look macro-like.
492            let mut cursor = node.walk();
493            if node
494                .children_by_field_name("declarator", &mut cursor)
495                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
496            {
497                return None;
498            }
499        } else if has_direct_cpp_declarator(node) {
500            return None;
501        }
502    }
503    let name = exported_class_name_from_node(class_node, source)?;
504    Some(RecoveredExportedClass {
505        declaration_node: class_node,
506        name,
507        body: cpp_body_node(class_node),
508        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
509            .then(|| extract_cpp_supertypes(class_node, source)),
510        uses_initializer_body: false,
511        fragmented_body: None,
512    })
513}
514
515fn recover_malformed_exported_base_class<'tree>(
516    node: Node<'tree>,
517    source: &str,
518) -> Option<RecoveredExportedClass<'tree>> {
519    if node.kind() != "declaration" {
520        return None;
521    }
522    let class_node = node.child_by_field_name("type")?;
523    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
524        return None;
525    }
526    let macro_name = class_node
527        .child_by_field_name("name")
528        .and_then(|name| direct_identifier_name(name, source))?;
529    if !cpp_export_macro_token(&macro_name) {
530        return None;
531    }
532
533    let mut named_cursor = node.walk();
534    let mut named = node.named_children(&mut named_cursor);
535    if named
536        .next()
537        .is_none_or(|child| !same_node(child, class_node))
538    {
539        return None;
540    }
541    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
542    if displaced.kind() != "ERROR" {
543        return None;
544    }
545    let name = displaced_exported_class_name(displaced, source)?;
546
547    let remaining = named.collect::<Vec<_>>();
548    let init = *remaining.last()?;
549    if init.kind() != "init_declarator" {
550        return None;
551    }
552    let final_base = init
553        .child_by_field_name("declarator")
554        .and_then(|base| recovered_malformed_base_name(base, source))?;
555    let body = init.child_by_field_name("value")?;
556    // A complete reduction has a real closing brace here. In Chromium's Widget
557    // declaration, tree-sitter instead emits the same direct `}` slot as a
558    // zero-width missing node where the first body macro truncates the prefix.
559    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
560        return None;
561    }
562
563    if remaining[..remaining.len() - 1]
564        .iter()
565        .any(|child| match child.kind() {
566            "qualified_identifier"
567            | "scoped_type_identifier"
568            | "type_identifier"
569            | "identifier" => false,
570            "ERROR" => !is_malformed_inheritance_access(*child, source),
571            _ => true,
572        })
573    {
574        return None;
575    }
576
577    let mut raw_supertypes = Vec::new();
578    for base in &remaining[..remaining.len() - 1] {
579        if base.kind() == "ERROR" {
580            continue;
581        }
582        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
583    }
584    raw_supertypes.push(final_base);
585
586    Some(RecoveredExportedClass {
587        declaration_node: node,
588        name,
589        body: Some(body),
590        raw_supertypes: Some(raw_supertypes),
591        uses_initializer_body: true,
592        fragmented_body: fragmented_export_body_region(node, body, source),
593    })
594}
595
596/// Locate the true class-body region for a fragmented multiple-base export class.
597///
598/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
599/// emits in place of the real class body. Tree-sitter reduces that body in one of
600/// two shapes, both of which lose the members from the recovered node:
601///
602/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
603///   a real closing brace and holds the whole body text inline. The interior between
604///   the braces reparses to the members directly.
605/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
606///   first member with a zero-width MISSING `}`; every later member -- and the real
607///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
608///   siblings. The interior runs from the opening brace to that displaced `}`.
609///
610/// Returns the interior byte range to reparse plus the full class navigation range.
611fn fragmented_export_body_region(
612    node: Node<'_>,
613    body: Node<'_>,
614    source: &str,
615) -> Option<FragmentedExportBody> {
616    let reparse_start = body.start_byte() + 1;
617    let close = direct_close_brace(body)?;
618    if close.end_byte() > close.start_byte() {
619        return Some(FragmentedExportBody {
620            reparse_start,
621            reparse_end: close.start_byte(),
622            class_range: cpp_declaration_range(node),
623        });
624    }
625    // The closing brace was displaced past the recovered node. A balanced nested
626    // class keeps its own braces, so the first lone-`}` sibling is this class's.
627    let mut sibling = node.next_named_sibling();
628    let displaced_close = loop {
629        let Some(current) = sibling else {
630            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
631        };
632        if cpp_is_stray_close_brace(current, source) {
633            break current;
634        }
635        sibling = current.next_named_sibling();
636    };
637    Some(FragmentedExportBody {
638        reparse_start,
639        reparse_end: displaced_close.start_byte(),
640        class_range: Range {
641            start_byte: node.start_byte(),
642            end_byte: displaced_close.end_byte(),
643            start_line: node.start_position().row + 1,
644            end_line: displaced_close.end_position().row + 1,
645        },
646    })
647}
648
649/// Locate the true class-body region for the export-macro class shape that
650/// tree-sitter promotes to a `function_definition`.
651///
652/// In this shape the synthetic function body closes at the first inline
653/// method, while the class's real members continue as root-level siblings until
654/// a stray `}` followed by the displaced class `;`. Reparse the complete
655/// interior so those siblings are visited with the recovered class scope.
656fn fragmented_export_function_body_region(
657    node: Node<'_>,
658    body: Node<'_>,
659    source: &str,
660    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
661) -> Option<FragmentedExportBody> {
662    let reparse_start = body.start_byte().checked_add(1)?;
663    if let Some(boundary) = displaced_namespace {
664        return Some(FragmentedExportBody {
665            reparse_start,
666            reparse_end: boundary.class_close.start_byte(),
667            class_range: Range {
668                start_byte: node.start_byte(),
669                end_byte: boundary.class_semicolon.end_byte(),
670                start_line: node.start_position().row + 1,
671                end_line: boundary.class_semicolon.end_position().row + 1,
672            },
673        });
674    }
675    let siblings = cpp_following_named_siblings(node, source);
676    let boundary = fragmented_export_sibling_class_boundary(node, source);
677    let boundary_index = boundary.and_then(|boundary| {
678        siblings
679            .iter()
680            .position(|candidate| same_node(*candidate, boundary))
681    });
682    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
683    let mut sibling_index = 0;
684    // A complete recovered class's synthetic wrapper is immediately followed
685    // by its displaced semicolon (comments and a trailing attribute macro --
686    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
687    // may sit between the body and that semicolon). Only scan for a later
688    // stray close when real member siblings intervene; otherwise every earlier
689    // complete class would borrow the next malformed class's close and claim
690    // its members. The trailing-attribute case is the gtest shape: the scan
691    // borrowed a close ~1900 lines later and re-owned a following
692    // `namespace testing { namespace internal {` block as class members,
693    // doubling the package path ("testing::internal::testing::internal") and
694    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
695    // boundary assert (#2297).
696    while let Some(current) = siblings.get(sibling_index).copied() {
697        if current.kind() == "comment" {
698            sibling_index += 1;
699            continue;
700        }
701        if is_trailing_attribute_macro_sibling(current) {
702            sibling_index += 1;
703            continue;
704        }
705        if cpp_is_stray_semicolon(current, source) {
706            return None;
707        }
708        break;
709    }
710    while let Some(current) = siblings.get(sibling_index).copied() {
711        let next = siblings.get(sibling_index + 1).copied();
712        if cpp_is_stray_close_brace(current, source)
713            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
714        {
715            let semicolon = next.expect("checked above");
716            return Some(FragmentedExportBody {
717                reparse_start,
718                reparse_end: current.start_byte(),
719                class_range: Range {
720                    start_byte: node.start_byte(),
721                    end_byte: semicolon.end_byte(),
722                    start_line: node.start_position().row + 1,
723                    end_line: semicolon.end_position().row + 1,
724                },
725            });
726        }
727        // When the final access label keeps the class close in its malformed
728        // declaration body, tree-sitter nests the lone `}` ERROR below the
729        // label instead of exposing it as a direct sibling. Search only the
730        // scattered siblings after the synthetic wrapper. The first such
731        // close is the class terminator because nested class bodies retain
732        // their own balanced class_specifier nodes.
733        if current.start_byte() >= body.end_byte()
734            && let Some(close) = cpp_nested_stray_close_brace(current, source)
735        {
736            return Some(FragmentedExportBody {
737                reparse_start,
738                reparse_end: close.start_byte(),
739                class_range: Range {
740                    start_byte: node.start_byte(),
741                    end_byte: current.end_byte(),
742                    start_line: node.start_position().row + 1,
743                    end_line: current.end_position().row + 1,
744                },
745            });
746        }
747        sibling_index += 1;
748    }
749    boundary.map(|boundary| FragmentedExportBody {
750        reparse_start,
751        reparse_end: boundary.start_byte(),
752        class_range: Range {
753            start_byte: node.start_byte(),
754            end_byte: boundary.start_byte(),
755            start_line: node.start_position().row + 1,
756            end_line: boundary.start_position().row + 1,
757        },
758    })
759}
760
761/// Find a later macro-export class that tree-sitter lifted through an enclosing
762/// preprocessor container. A class that is still a direct sibling can be a
763/// nested member of the current fragmented class, so only a changed parent is
764/// a proven boundary between the two recovered class envelopes.
765fn fragmented_export_sibling_class_boundary<'tree>(
766    node: Node<'tree>,
767    source: &str,
768) -> Option<Node<'tree>> {
769    let node_parent = node.parent()?;
770    cpp_following_named_siblings(node, source)
771        .into_iter()
772        .find(|candidate| {
773            recover_exported_class_function_definition(*candidate, source).is_some()
774                && candidate
775                    .parent()
776                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
777        })
778}
779
780/// A trailing attribute macro after a recovered class's closing brace, spelled
781/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
782/// bare identifier is never a class member (members need a type), so this
783/// sibling can only be the class's own tail (#2297).
784fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
785    if node.kind() != "expression_statement" {
786        return false;
787    }
788    let mut cursor = node.walk();
789    let mut children = node.named_children(&mut cursor);
790    children
791        .next()
792        .is_some_and(|child| child.kind() == "identifier")
793        && children.next().is_none()
794}
795
796/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
797/// export-class wrapper can place the class close inside an access-label node,
798/// so direct-sibling checks alone miss the boundary.  Walk named CST children
799/// only; the helper does not inspect source text beyond the existing structured
800/// stray-brace predicate.
801fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
802    let mut stack = vec![node];
803    while let Some(current) = stack.pop() {
804        if cpp_is_stray_close_brace(current, source) {
805            return Some(current);
806        }
807        let mut cursor = current.walk();
808        stack.extend(current.named_children(&mut cursor));
809    }
810    None
811}
812
813/// Return named siblings that follow `node`, including siblings that tree-sitter
814/// attached to an enclosing container after malformed recovery split the local
815/// declaration list. Stop at the first structurally visible class close so a
816/// later namespace or exported class cannot supply the recovery boundary.
817fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
818    let mut siblings = Vec::new();
819    let mut anchor = node;
820    while let Some(parent) = anchor.parent() {
821        let at_translation_unit = parent.kind() == "translation_unit";
822        let mut sibling = anchor.next_named_sibling();
823        while let Some(current) = sibling {
824            if at_translation_unit
825                && (current.kind() == "namespace_definition"
826                    || (current.kind() == "function_definition"
827                        && first_class_like_child(current).is_some()))
828            {
829                return siblings;
830            }
831            siblings.push(current);
832            if cpp_is_stray_close_brace(current, source) {
833                if let Some(semicolon) = current
834                    .next_named_sibling()
835                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
836                {
837                    siblings.push(semicolon);
838                }
839                return siblings;
840            }
841            if current.start_byte() >= node.end_byte()
842                && matches!(current.kind(), "ERROR" | "labeled_statement")
843                && cpp_nested_stray_close_brace(current, source).is_some()
844            {
845                return siblings;
846            }
847            sibling = current.next_named_sibling();
848        }
849        anchor = parent;
850    }
851    siblings
852}
853
854fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
855    if node.start_byte() >= class_end {
856        return false;
857    }
858    node.end_byte() <= class_end
859        || cpp_nested_stray_close_brace(node, source)
860            .is_some_and(|close| close.start_byte() == class_end)
861}
862
863/// A class whose head tree-sitter kept in one malformed container while the
864/// members after the first and the closing `};` scattered across that
865/// container's following siblings.
866struct FragmentedClassRecovery<'tree> {
867    declaration_node: Node<'tree>,
868    name: String,
869    raw_supertypes: Vec<String>,
870    body: FragmentedExportBody,
871}
872
873/// Recover a class whose opening prefix is retained in one ERROR node while one
874/// or more nested class closes and the outer close are displaced to sibling
875/// `}`/`;` nodes. All boundaries come from tree-sitter nodes: the direct class
876/// tokens establish nesting depth and the displaced close nodes terminate it.
877///
878/// Two head spellings reach the ERROR arm. A plain `class Name ... {` keeps the
879/// `class` keyword as the container's first token. A function-like export macro
880/// (`class BOTAN_PUBLIC_API(2, 0) Name : public virtual Base {`) is reduced to a
881/// body-less `class_specifier` named after the macro followed by the
882/// invocation's `(`, arguments and `)`, with the real class name and the base
883/// clause left as bare tokens after it (#2924).
884fn fragmented_class_body<'tree>(
885    node: Node<'tree>,
886    source: &str,
887) -> Option<FragmentedClassRecovery<'tree>> {
888    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
889        return Some(recovered);
890    }
891    let supported_container = node.kind() == "ERROR"
892        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
893    if !supported_container {
894        return None;
895    }
896    let mut cursor = node.walk();
897    let children = node.children(&mut cursor).collect::<Vec<_>>();
898    if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
899        return Some(recovered);
900    }
901    let keyword = children.first()?;
902    if !matches!(keyword.kind(), "class" | "struct" | "union") {
903        return None;
904    }
905    let name_node = children
906        .iter()
907        .copied()
908        .skip(1)
909        .find(|child| child.is_named())?;
910    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
911        return None;
912    }
913    let name = normalize_cpp_whitespace(node_text(name_node, source));
914    if name.is_empty() || cpp_export_macro_token(&name) {
915        return None;
916    }
917    let open_index = children.iter().position(|child| child.kind() == "{")?;
918    Some(FragmentedClassRecovery {
919        declaration_node: node,
920        name,
921        raw_supertypes: extract_cpp_supertypes(node, source),
922        body: fragmented_displaced_class_body(node, &children, open_index, source)?,
923    })
924}
925
926/// The fragmented body of a function-like export-macro class head that
927/// tree-sitter kept in one declaration-scope `ERROR` together with the body's
928/// opening `{` (Botan's `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey :
929/// public virtual EC_PublicKey {`, #2924).
930///
931/// The class name is read positionally by [`recovered_export_head_name`] -- the
932/// last identifier before the head ends at `final`, at the base clause `:`, or
933/// at the body `{` -- so no macro spelling is interpreted, and a base clause
934/// spelled with `virtual`, with `final`, or with qualified base names needs no
935/// arm of its own.
936fn fragmented_export_macro_class_body<'tree>(
937    node: Node<'tree>,
938    children: &[Node<'tree>],
939    source: &str,
940) -> Option<FragmentedClassRecovery<'tree>> {
941    let class_node = *children.first()?;
942    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
943        return None;
944    }
945    // The identifier tree-sitter took for the class name is the export macro, a
946    // function-like invocation when the `(` of its argument list follows it
947    // directly. Spelling plays no part (#2557).
948    class_node
949        .child_by_field_name("name")
950        .and_then(|name| direct_identifier_name(name, source))?;
951    let invocation = *children.get(1)?;
952    if invocation.is_named() || invocation.kind() != "(" {
953        return None;
954    }
955    let open_index = children
956        .iter()
957        .position(|child| !child.is_named() && child.kind() == "{")?;
958    let open = children[open_index];
959    let name_node = recovered_export_head_name(node, open, source)?;
960    let name = normalize_cpp_whitespace(node_text(name_node, source));
961    if name.is_empty() || cpp_export_macro_token(&name) {
962        return None;
963    }
964    Some(FragmentedClassRecovery {
965        declaration_node: node,
966        name,
967        raw_supertypes: recovered_export_head_bases(
968            node,
969            name_node.end_byte(),
970            open.start_byte(),
971            source,
972        ),
973        body: fragmented_displaced_class_body(node, children, open_index, source)
974            .or_else(|| fragmented_container_close_class_body(node, open, source))?,
975    })
976}
977
978/// The class body a fragmented head opened at `children[open_index]` but could
979/// not close: tree-sitter scattered the remaining members and the closing `};`
980/// across the container's following siblings. The direct class tokens after the
981/// opening brace say how many closes the nested classes consume before this
982/// class's own, and the displaced `}`/`;` sibling pair terminates it.
983fn fragmented_displaced_class_body(
984    node: Node<'_>,
985    children: &[Node<'_>],
986    open_index: usize,
987    source: &str,
988) -> Option<FragmentedExportBody> {
989    let open = children[open_index];
990    let nested_class_opens = children[open_index + 1..]
991        .iter()
992        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
993        .count();
994    let mut closes_remaining = 1 + nested_class_opens;
995    let mut sibling = node.next_named_sibling();
996    while let Some(candidate) = sibling {
997        let next = candidate.next_named_sibling();
998        if cpp_is_stray_close_brace(candidate, source) {
999            closes_remaining -= 1;
1000            if closes_remaining == 0 {
1001                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
1002                if open.end_byte() >= candidate.start_byte() {
1003                    return None;
1004                }
1005                return Some(FragmentedExportBody {
1006                    reparse_start: open.end_byte(),
1007                    reparse_end: candidate.start_byte(),
1008                    class_range: Range {
1009                        start_byte: node.start_byte(),
1010                        end_byte: semicolon.end_byte(),
1011                        start_line: node.start_position().row + 1,
1012                        end_line: semicolon.end_position().row + 1,
1013                    },
1014                });
1015            }
1016        }
1017        sibling = next;
1018    }
1019    None
1020}
1021
1022/// The body of a fragmented export-macro class head whose close tree-sitter
1023/// spent on the enclosing container instead: Botan's
1024/// `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey : public virtual
1025/// EC_PublicKey {` leaves every member as a `declaration_list` sibling and ends
1026/// `namespace Botan` on the class's own brace, so nothing is displaced to a
1027/// sibling `}` and [`fragmented_displaced_class_body`] finds no terminator
1028/// (#2924).
1029fn fragmented_container_close_class_body(
1030    node: Node<'_>,
1031    open: Node<'_>,
1032    source: &str,
1033) -> Option<FragmentedExportBody> {
1034    let parent = node.parent()?;
1035    if !matches!(
1036        parent.kind(),
1037        "declaration_list" | "field_declaration_list" | "compound_statement"
1038    ) {
1039        return None;
1040    }
1041    let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1042    // The container's close is this class's close only when the body's own
1043    // brace balance says so. Without that cross-check -- the one issue #1524
1044    // already uses on a mis-closed wrapper body -- a class that really does end
1045    // earlier would claim every later container-level declaration as a member.
1046    if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1047        || open.end_byte() >= close.start_byte()
1048    {
1049        return None;
1050    }
1051    Some(FragmentedExportBody {
1052        reparse_start: open.end_byte(),
1053        reparse_end: close.start_byte(),
1054        class_range: Range {
1055            start_byte: node.start_byte(),
1056            end_byte: close.end_byte(),
1057            start_line: node.start_position().row + 1,
1058            end_line: close.end_position().row + 1,
1059        },
1060    })
1061}
1062
1063pub(crate) fn recovered_fragmented_class_has_body(
1064    node: Node<'_>,
1065    source: &str,
1066    expected_name: &str,
1067    expected_range: &Range,
1068) -> bool {
1069    fragmented_class_body(node, source).is_some_and(|recovered| {
1070        recovered.name == expected_name
1071            && recovered.body.class_range.start_byte == expected_range.start_byte
1072            && recovered.body.class_range.end_byte == expected_range.end_byte
1073    })
1074}
1075
1076/// Recover a plain class whose parser-visible body ends inside a malformed
1077/// inline member. Tree-sitter then attaches either the next real member
1078/// declarator or the unfinished `else` branch directly to the outer function
1079/// definition and leaves the class's actual `};` among later siblings. Those
1080/// structured continuations and the close/semicolon siblings establish the
1081/// complete body envelope without interpreting source text.
1082fn fragmented_plain_class_declaration_body<'tree>(
1083    node: Node<'tree>,
1084    source: &str,
1085) -> Option<FragmentedClassRecovery<'tree>> {
1086    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1087        return None;
1088    }
1089    let class_node = node.child_by_field_name("type")?;
1090    if !matches!(
1091        class_node.kind(),
1092        "class_specifier" | "struct_specifier" | "union_specifier"
1093    ) {
1094        return None;
1095    }
1096    let name_node = class_node.child_by_field_name("name")?;
1097    let name = normalize_cpp_whitespace(node_text(name_node, source));
1098    if name.is_empty() || cpp_export_macro_token(&name) {
1099        return None;
1100    }
1101    let body = cpp_body_node(class_node)?;
1102    if body.kind() != "field_declaration_list" {
1103        return None;
1104    }
1105    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1106        if declarator.start_byte() < class_node.end_byte() {
1107            return None;
1108        }
1109        let mut cursor = node.walk();
1110        node.named_children(&mut cursor).any(|child| {
1111            if child.kind() != "ERROR"
1112                || child.start_byte() < class_node.end_byte()
1113                || child.end_byte() > declarator.start_byte()
1114            {
1115                return false;
1116            }
1117            let mut cursor = child.walk();
1118            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1119            let Some((return_type, attributes)) = components.split_last() else {
1120                return false;
1121            };
1122            matches!(
1123                return_type.kind(),
1124                "identifier"
1125                    | "type_identifier"
1126                    | "primitive_type"
1127                    | "decltype"
1128                    | "placeholder_type_specifier"
1129            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1130                && attributes.iter().all(|attribute| {
1131                    matches!(attribute.kind(), "identifier" | "type_identifier")
1132                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1133                            *attribute, source,
1134                        )))
1135                })
1136        })
1137    } else {
1138        let mut cursor = node.walk();
1139        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1140        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1141            if same_node(*candidate_class, class_node)
1142                && continuation.kind() == "identifier"
1143                && node_text(*continuation, source) == "else"
1144                && continuation_body.kind() == "compound_statement"
1145                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1146                && continuation_body
1147                    .child(continuation_body.child_count().saturating_sub(1))
1148                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1149    };
1150    if !displaced_member {
1151        return None;
1152    }
1153    let open = body
1154        .children(&mut body.walk())
1155        .find(|child| child.kind() == "{")?;
1156    let siblings = cpp_following_named_siblings(node, source);
1157    let ordinary_boundary =
1158        siblings
1159            .iter()
1160            .copied()
1161            .enumerate()
1162            .find_map(|(close_index, close)| {
1163                cpp_is_stray_close_brace(close, source)
1164                    .then(|| {
1165                        siblings
1166                            .get(close_index + 1)
1167                            .copied()
1168                            .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1169                            .map(|semicolon| (close, semicolon))
1170                    })
1171                    .flatten()
1172            });
1173    let (close, semicolon) =
1174        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1175            (boundary.class_close, boundary.class_semicolon)
1176        } else {
1177            ordinary_boundary?
1178        };
1179    if open.end_byte() >= close.start_byte() {
1180        return None;
1181    }
1182    Some(FragmentedClassRecovery {
1183        declaration_node: class_node,
1184        name,
1185        raw_supertypes: extract_cpp_supertypes(class_node, source),
1186        body: FragmentedExportBody {
1187            reparse_start: open.end_byte(),
1188            reparse_end: close.start_byte(),
1189            class_range: Range {
1190                start_byte: class_node.start_byte(),
1191                end_byte: semicolon.end_byte(),
1192                start_line: class_node.start_position().row + 1,
1193                end_line: semicolon.end_position().row + 1,
1194            },
1195        },
1196    })
1197}
1198
1199fn displaced_export_function_namespace_shape<'tree>(
1200    declaration: Node<'tree>,
1201    source: &str,
1202) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1203    let mut nested = Vec::new();
1204    for index in (0..declaration.named_child_count()).rev() {
1205        nested.push(declaration.named_child(index)?);
1206    }
1207    while let Some(current) = nested.pop() {
1208        // A recovered export class nested in this class can consume the first
1209        // parser-visible namespace close itself. In that shape the existing
1210        // later-class boundary logic already distinguishes the nested and
1211        // namespace-sibling owners; do not mistake the nested close for this
1212        // class's terminator.
1213        if recover_exported_class_function_definition(current, source).is_some() {
1214            return None;
1215        }
1216        for index in (0..current.named_child_count()).rev() {
1217            nested.push(current.named_child(index)?);
1218        }
1219    }
1220    let mut same_envelope_sibling = declaration.next_named_sibling();
1221    while let Some(current) = same_envelope_sibling {
1222        if recover_exported_class_function_definition(current, source).is_some() {
1223            return None;
1224        }
1225        same_envelope_sibling = current.next_named_sibling();
1226    }
1227    let declaration_list = declaration.parent()?;
1228    if declaration_list.kind() != "declaration_list" {
1229        return None;
1230    }
1231    let namespace = declaration_list.parent()?;
1232    if namespace.kind() != "namespace_definition"
1233        || namespace.child_by_field_name("body") != Some(declaration_list)
1234    {
1235        return None;
1236    }
1237    let class_close = direct_close_brace(declaration_list)?;
1238    let trailing_semicolon = namespace.next_named_sibling()?;
1239    if trailing_semicolon.kind() != "expression_statement"
1240        || trailing_semicolon.named_child_count() != 0
1241    {
1242        return None;
1243    }
1244    // A chain of malformed export classes can consume one parser-visible
1245    // namespace close per class. Walk through the enclosing sibling levels so
1246    // the later real namespace close remains the structural boundary; a
1247    // direct next-sibling walk stops at the first collapsed namespace and
1248    // incorrectly makes its intervening items members of this class.
1249    let siblings = cpp_following_named_siblings(namespace, source);
1250    let trailing_index = siblings
1251        .iter()
1252        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1253    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1254        recover_exported_class_function_definition(*candidate, source).is_some()
1255    }) {
1256        // Consecutive recovered classes already have an exact sibling-class
1257        // boundary. Preserve that established path, including nested export
1258        // classes, instead of interpreting the first class close as a
1259        // collapsed namespace boundary.
1260        return None;
1261    }
1262    let mut namespace_items = Vec::new();
1263    let mut nested_fragment_end = 0;
1264    for current in siblings.into_iter().skip(trailing_index + 1) {
1265        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1266        {
1267            return Some(DisplacedFragmentNamespaceBoundary {
1268                class_close,
1269                class_semicolon: trailing_semicolon,
1270                namespace_items,
1271            });
1272        }
1273        if current.start_byte() >= nested_fragment_end
1274            && let Some(recovered) = fragmented_class_body(current, source)
1275        {
1276            nested_fragment_end = recovered.body.class_range.end_byte;
1277        } else if current.start_byte() >= nested_fragment_end
1278            && recover_exported_class_function_definition(current, source).is_some()
1279            && let Some(body) = cpp_body_node(current)
1280            && let Some(fragmented) =
1281                fragmented_export_function_body_region(current, body, source, None)
1282        {
1283            nested_fragment_end = fragmented.class_range.end_byte;
1284        }
1285        namespace_items.push(current);
1286    }
1287    None
1288}
1289
1290fn displaced_fragment_namespace_boundary<'tree>(
1291    declaration: Node<'tree>,
1292    body: Node<'tree>,
1293    source: &str,
1294) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1295    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1296    let reparse_start = body.start_byte() + 1;
1297    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1298    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1299}
1300
1301/// Recover the class/namespace brace geometry for a declaration whose class
1302/// close tree-sitter consumed as the enclosing namespace close. This proof is
1303/// independent of whether every member in the class body can be reparsed: the
1304/// ordinary-tree fallback can still re-own bounded sibling declarations when
1305/// an unknown macro makes the complete body reparse unsafe.
1306fn displaced_fragment_namespace_geometry<'tree>(
1307    declaration: Node<'tree>,
1308    source: &str,
1309) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1310    // A templated class's malformed function wrapper remains beneath the
1311    // template node even though its later members have escaped to the
1312    // enclosing declaration list. Lift only that exact declaration child.
1313    let envelope = declaration
1314        .parent()
1315        .filter(|parent| {
1316            parent.kind() == "template_declaration"
1317                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1318        })
1319        .unwrap_or(declaration);
1320    let declaration_list = envelope.parent()?;
1321    if declaration_list.kind() != "declaration_list" {
1322        return None;
1323    }
1324    let namespace = declaration_list.parent()?;
1325    if namespace.kind() != "namespace_definition"
1326        || namespace.child_by_field_name("body") != Some(declaration_list)
1327    {
1328        return None;
1329    }
1330    let class_close = direct_close_brace(declaration_list)?;
1331    let trailing_semicolon = namespace.next_named_sibling()?;
1332    if trailing_semicolon.kind() != "expression_statement"
1333        || trailing_semicolon.named_child_count() != 0
1334    {
1335        return None;
1336    }
1337    let mut namespace_items = Vec::new();
1338    let mut sibling = trailing_semicolon.next_named_sibling();
1339    let mut nested_fragment_end = 0;
1340    loop {
1341        let current = sibling?;
1342        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1343        {
1344            break;
1345        }
1346        if current.start_byte() >= nested_fragment_end
1347            && let Some(recovered) = fragmented_class_body(current, source)
1348        {
1349            nested_fragment_end = recovered.body.class_range.end_byte;
1350        }
1351        namespace_items.push(current);
1352        sibling = current.next_named_sibling();
1353    }
1354    Some(DisplacedFragmentNamespaceBoundary {
1355        class_close,
1356        class_semicolon: trailing_semicolon,
1357        namespace_items,
1358    })
1359}
1360
1361/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1362fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1363    (0..node.child_count())
1364        .filter_map(|index| node.child(index))
1365        .find(|child| !child.is_named() && child.kind() == "}")
1366}
1367
1368/// A displaced lone closing brace: the class close that the fragmented multiple-base
1369/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1370fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1371    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1372}
1373
1374/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1375/// text while skipping line/block comments and string/char literals. The
1376/// exported-class recovery needs this when tree-sitter's bogus
1377/// `function_definition` body runs past the class's true closing brace and
1378/// swallows following siblings (issue #1524): the grammar tree carries no
1379/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1380/// close is located textually. Returns `None` when the text is unbalanced or
1381/// contains a construct the scanner deliberately does not interpret (raw
1382/// strings) -- callers treat that as "cannot partition" and keep the
1383/// un-split recovery.
1384fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1385    let bytes = source.as_bytes();
1386    if bytes.get(open_byte) != Some(&b'{') {
1387        return None;
1388    }
1389    let mut depth = 0usize;
1390    let mut i = open_byte;
1391    while i < bytes.len() {
1392        match bytes[i] {
1393            b'{' => depth += 1,
1394            b'}' => {
1395                depth = depth.checked_sub(1)?;
1396                if depth == 0 {
1397                    return Some(i);
1398                }
1399            }
1400            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1401                while i < bytes.len() && bytes[i] != b'\n' {
1402                    i += 1;
1403                }
1404                continue;
1405            }
1406            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1407                i += 2;
1408                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1409                    i += 1;
1410                }
1411                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1412                continue;
1413            }
1414            quote @ (b'"' | b'\'') => {
1415                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1416                // bail out rather than mis-count.
1417                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1418                    return None;
1419                }
1420                i += 1;
1421                while i < bytes.len() && bytes[i] != quote {
1422                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1423                }
1424                if i >= bytes.len() {
1425                    return None;
1426                }
1427            }
1428            _ => {}
1429        }
1430        i += 1;
1431    }
1432    None
1433}
1434
1435fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1436    let mut name = None;
1437    let mut colon_count = 0;
1438    let mut access_count = 0;
1439    for index in 0..node.child_count() {
1440        let child = node.child(index)?;
1441        match child.kind() {
1442            "identifier" | "type_identifier" if child.is_named() => {
1443                if name.is_some() {
1444                    return None;
1445                }
1446                let candidate = normalize_cpp_whitespace(node_text(child, source));
1447                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1448                    return None;
1449                }
1450                name = Some(candidate);
1451            }
1452            "template_function" | "template_type" if child.is_named() => {
1453                if name.is_some() {
1454                    return None;
1455                }
1456                let candidate = child
1457                    .child_by_field_name("name")
1458                    .and_then(|name| direct_identifier_name(name, source))?;
1459                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1460                    return None;
1461                }
1462                name = Some(candidate);
1463            }
1464            ":" if !child.is_named() => colon_count += 1,
1465            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1466            _ => return None,
1467        }
1468    }
1469    (colon_count == 1 && access_count == 1)
1470        .then_some(name)
1471        .flatten()
1472}
1473
1474fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1475    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1476        return false;
1477    }
1478    node.named_child(0)
1479        .and_then(|child| direct_identifier_name(child, source))
1480        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1481}
1482
1483fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1484    (0..node.child_count()).any(|index| {
1485        node.child(index)
1486            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1487    })
1488}
1489
1490fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1491    match node.kind() {
1492        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1493            recovered_base_atom(node, source)
1494        }
1495        "template_type" | "template_function" => node
1496            .child_by_field_name("name")
1497            .and_then(|name| recovered_malformed_base_name(name, source)),
1498        "ERROR" => None,
1499        "qualified_identifier" | "scoped_type_identifier" => {
1500            let suffix = node
1501                .child_by_field_name("name")
1502                .and_then(|name| recovered_malformed_base_name(name, source))?;
1503            let scope = node
1504                .child_by_field_name("scope")
1505                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1506            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1507                malformed_qualified_prefix(node, source)?
1508            } else {
1509                if malformed_qualified_prefix(node, source).is_some() {
1510                    return None;
1511                }
1512                scope
1513            };
1514            Some(format!("{prefix}::{suffix}"))
1515        }
1516        _ => None,
1517    }
1518}
1519
1520fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1521    if !matches!(
1522        node.kind(),
1523        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1524    ) {
1525        return None;
1526    }
1527    let name = normalize_cpp_whitespace(node_text(node, source));
1528    (!name.is_empty()).then_some(name)
1529}
1530
1531fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1532    let mut prefix = None;
1533    let mut cursor = node.walk();
1534    for error in node
1535        .named_children(&mut cursor)
1536        .filter(|child| child.kind() == "ERROR")
1537    {
1538        if prefix.is_some() {
1539            return None;
1540        }
1541        // `public virtual Botan::EC_PublicKey` reduces the access specifier to
1542        // the qualified name's scope and leaves `virtual` beside the real
1543        // qualifier inside the `ERROR`. `virtual` is a base specifier, never a
1544        // component of the base's name (#2924).
1545        let mut error_cursor = error.walk();
1546        let atoms = error
1547            .named_children(&mut error_cursor)
1548            .map(|child| recovered_base_atom(child, source))
1549            .collect::<Option<Vec<_>>>()?;
1550        let [atom] = atoms
1551            .iter()
1552            .filter(|atom| atom.as_str() != "virtual")
1553            .collect::<Vec<_>>()[..]
1554        else {
1555            return None;
1556        };
1557        prefix = Some(atom.clone());
1558    }
1559    prefix
1560}
1561
1562/// One declaration an attribute-like macro invocation swallowed into a
1563/// declaration-scope `ERROR`, with the byte range that spells it.
1564/// What [`stranded_declaration_run`] read out of one node.
1565struct StrandedRun<'tree> {
1566    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1567    /// Whether every part of the node read as part of a declaration. False when
1568    /// a part the reader does not understand ended it early, or when the last
1569    /// parts were types with no declarator after them. Callers that index what
1570    /// was found keep the declarations either way; a caller deciding whether a
1571    /// whole region is safe to index requires this.
1572    complete: bool,
1573}
1574
1575struct MacroWrappedDeclaration<'tree> {
1576    declarator: Node<'tree>,
1577    range: Range,
1578    /// Whether the recovered declaration spells `static`. The envelope hides
1579    /// that keyword from the ordinary linkage reader, which looks for it among
1580    /// a declaration node's own children, and internal linkage is what decides
1581    /// whether a header declaration and a body in another file are one symbol.
1582    is_static: bool,
1583}
1584
1585/// Whether `node` stands where declarations live: directly in the translation
1586/// unit, in a `namespace` or `extern "C"` body, or in an `ERROR` that itself
1587/// stands in one of those.
1588///
1589/// An `ERROR` is a grouping failure, never a scope, so what it holds stands
1590/// where it stands. The case that matters is a whole translation unit the
1591/// parser could not close: whisper.cpp's `include/whisper.h` opens
1592/// `extern "C" {` inside `#ifdef __cplusplus` and closes it inside another one,
1593/// so the braces never balance in the token stream tree-sitter sees, the file's
1594/// own root node is an `ERROR`, and every top-level declaration in the file is
1595/// a child of it (#3094). Without this the macro recoveries below read nothing
1596/// in such a file, because none of their envelopes is in a declaration scope
1597/// the parser managed to build.
1598fn is_declaration_scope_position(node: Node<'_>) -> bool {
1599    declaration_scope_container(node).is_some()
1600}
1601
1602/// The container `node` declares in, for [`is_declaration_scope_position`]. Its
1603/// end is where a parse failure that starts at `node` can still be doing
1604/// damage: a declaration scope has no synchronization point of its own, so the
1605/// parser carries such a failure to the scope's close.
1606fn declaration_scope_container(node: Node<'_>) -> Option<Node<'_>> {
1607    let mut parent = node.parent()?;
1608    loop {
1609        match parent.kind() {
1610            "translation_unit" => return Some(parent),
1611            "declaration_list" => {
1612                return parent
1613                    .parent()
1614                    .is_some_and(|grandparent| {
1615                        matches!(
1616                            grandparent.kind(),
1617                            "namespace_definition" | "linkage_specification"
1618                        )
1619                    })
1620                    .then_some(parent);
1621            }
1622            "ERROR" => match parent.parent() {
1623                Some(grandparent) => parent = grandparent,
1624                // A root `ERROR` is the translation unit the parser could not
1625                // build, so its children stand at file scope.
1626                None => return Some(parent),
1627            },
1628            _ => return None,
1629        }
1630    }
1631}
1632
1633/// Whether `node` is an `ERROR` the parser produced where declarations live.
1634fn is_declaration_scope_error(node: Node<'_>) -> bool {
1635    node.kind() == "ERROR" && is_declaration_scope_position(node)
1636}
1637
1638/// Whether `node` can only be part of what precedes a declarator -- a type, a
1639/// specifier, or a word of the attribute macro's own text that the lexer left
1640/// as a bare identifier -- so a run of these followed by a declarator is one
1641/// declaration the parser failed to group.
1642fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1643    matches!(
1644        node.kind(),
1645        "identifier"
1646            | "type_identifier"
1647            | "primitive_type"
1648            | "sized_type_specifier"
1649            | "struct_specifier"
1650            | "union_specifier"
1651            | "enum_specifier"
1652            | "type_qualifier"
1653            | "storage_class_specifier"
1654            | "explicit_function_specifier"
1655            | "virtual_function_specifier"
1656            | "qualified_identifier"
1657            | "template_type"
1658            | "dependent_type"
1659            | "placeholder_type_specifier"
1660    )
1661}
1662
1663/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
1664/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
1665/// words the lexer reports as bare identifiers. Anything else in such an
1666/// `ERROR` stops the recovery, so a macro argument that carries structure this
1667/// recovery does not understand is never guessed at.
1668fn is_macro_argument_error(node: Node<'_>) -> bool {
1669    if node.kind() != "ERROR" {
1670        return false;
1671    }
1672    let mut cursor = node.walk();
1673    node.named_children(&mut cursor).all(|child| {
1674        matches!(
1675            child.kind(),
1676            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1677        )
1678    })
1679}
1680
1681/// The end of a recovered declaration: the declarator's end, extended across
1682/// the `;` the grammar left beside it inside the envelope.
1683///
1684/// The envelope's own end is the hard boundary. The parser hands the last
1685/// declaration's `;` to the sibling statement it recovered with, outside the
1686/// envelope, and a range that reached it would no longer lie inside one node --
1687/// which is how every reader (including the resolver's climb from a range to
1688/// the node that declares it) finds a recovered declaration again.
1689fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1690    declarator
1691        .next_sibling()
1692        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1693        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1694}
1695
1696/// The declarations `node` holds after an attribute-like macro cost the parser
1697/// their grouping, in source order.
1698///
1699/// The parser packs the parts into whichever slots it has left. In whisper's
1700/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
1701/// field takes the export macro, the real return type and the *next*
1702/// declaration's declarator both end up inside one sibling `ERROR`, and the
1703/// `declarator` field takes whatever declaration the recovery reached last. In
1704/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
1705/// arrive as bare identifiers and the attributed member and the member after it
1706/// share one declarator node.
1707///
1708/// Both are the same failure, so both get the same reading: flatten the node's
1709/// parts -- splicing each nested `ERROR`'s own children in place, since an
1710/// `ERROR` here is only a grouping failure -- and read the flat run as what it
1711/// spells, a run of type-and-specifier tokens followed by a declarator, over
1712/// and over.
1713///
1714/// Fails closed. A part that is neither a declarator nor something that can
1715/// only precede one ends the recovery there, and the declarations before it are
1716/// kept.
1717fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1718    let mut parts = Vec::new();
1719    let mut cursor = node.walk();
1720    for child in node.named_children(&mut cursor) {
1721        if child.kind() == "ERROR" {
1722            let mut error_cursor = child.walk();
1723            parts.extend(child.named_children(&mut error_cursor));
1724        } else {
1725            parts.push(child);
1726        }
1727    }
1728
1729    let mut declarations = Vec::new();
1730    let mut start = None;
1731    let mut is_static = false;
1732    let mut complete = true;
1733    for part in parts {
1734        if part.kind() == "comment" {
1735            continue;
1736        }
1737        if let Some(declarator) = extract_function_declarator(part) {
1738            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1739            declarations.push(MacroWrappedDeclaration {
1740                declarator,
1741                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1742                is_static,
1743            });
1744            is_static = false;
1745            continue;
1746        }
1747        if !is_recovered_declaration_type_part(part) {
1748            complete = false;
1749            break;
1750        }
1751        is_static |= part.kind() == "storage_class_specifier"
1752            && normalize_cpp_whitespace(node_text(part, source)) == "static";
1753        start.get_or_insert(part.start_byte());
1754    }
1755    StrandedRun {
1756        declarations,
1757        complete: complete && start.is_none(),
1758    }
1759}
1760
1761/// The declarations an attribute-like macro invocation swallowed into a
1762/// declaration-scope `ERROR`, in source order.
1763///
1764/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
1765/// declaration in a macro call:
1766///
1767/// ```text
1768/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
1769///                  struct llama_model * model,
1770///           struct llama_context_params   params),
1771///         "use llama_init_from_model instead");
1772/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
1773/// ```
1774///
1775/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
1776/// holding the macro name, the wrapped declaration as a
1777/// `parameter_declaration`, the hint string as another `ERROR`, and then every
1778/// following declaration as a further `parameter_declaration` until it
1779/// recovers. That is what removed `llama_tokenize` from the index and left the
1780/// seven-argument call in `talk-llama.cpp` with only that file's own
1781/// three-parameter `static` overload to choose from (#2552, and the
1782/// `LLAMA_API` half of #2551).
1783///
1784/// Every part of every swallowed declaration is still a real node; only their
1785/// grouping is lost. This rebuilds the grouping and reads the nodes.
1786fn macro_wrapped_declarations<'tree>(
1787    envelope: Node<'tree>,
1788    source: &str,
1789) -> Vec<MacroWrappedDeclaration<'tree>> {
1790    let mut declarations = Vec::new();
1791    if !is_declaration_scope_error(envelope) {
1792        return declarations;
1793    }
1794    let mut cursor = envelope.walk();
1795    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1796    let [macro_name, arguments @ ..] = children.as_slice() else {
1797        return declarations;
1798    };
1799    if macro_name.kind() != "identifier" {
1800        return declarations;
1801    }
1802    let mut wrapped_declaration_seen = false;
1803    for argument in arguments {
1804        match argument.kind() {
1805            "comment" => {}
1806            "parameter_declaration" => {
1807                let recovered = stranded_declaration_run(*argument, source).declarations;
1808                if recovered.is_empty() {
1809                    break;
1810                }
1811                wrapped_declaration_seen = true;
1812                declarations.extend(recovered);
1813            }
1814            // The hint string, and only that: an argument the recovery cannot
1815            // read as a declaration is admitted before the wrapped declaration
1816            // is found, so a macro whose first argument is not a declaration
1817            // recovers nothing.
1818            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1819            _ => break,
1820        }
1821    }
1822    declarations
1823}
1824
1825/// What one macro invocation swallowed when it collapsed a whole run of
1826/// declarations.
1827struct CollapsedMacroDeclarationRun {
1828    /// The byte just past the `;` that closes the invocation, which is where
1829    /// the declarations it swallowed begin.
1830    invocation_end: usize,
1831    /// The byte this recovery owns to: the close of the enclosing declaration
1832    /// scope when the invocation swallowed the declarations written after it,
1833    /// and the invocation's own end when the parser instead spread the
1834    /// invocation itself across the container's children and swallowed nothing.
1835    region_end: usize,
1836}
1837
1838/// The tokens one collapsed macro invocation spans, in source order: the leaves
1839/// of `node`, then the leaves of the siblings written after it. Comments and
1840/// the parser's own MISSING tokens are skipped; neither is in the source the
1841/// invocation is written in.
1842///
1843/// The parser puts the parts of a failed invocation wherever it has room. It
1844/// keeps them in one `ERROR`; it splits them across an `ERROR` and the
1845/// `expression_statement` it recovered with; it spreads them flat over the
1846/// container's children; it packs the tail of the file into one of the
1847/// arguments. The grouping therefore says nothing, but the token order still
1848/// spells the invocation, which is what this reads.
1849struct MacroInvocationTokens<'tree> {
1850    stack: Vec<Node<'tree>>,
1851    next_sibling: Option<Node<'tree>>,
1852}
1853
1854impl<'tree> MacroInvocationTokens<'tree> {
1855    fn new(node: Node<'tree>) -> Self {
1856        Self {
1857            stack: vec![node],
1858            next_sibling: node.next_sibling(),
1859        }
1860    }
1861}
1862
1863impl<'tree> Iterator for MacroInvocationTokens<'tree> {
1864    type Item = Node<'tree>;
1865
1866    fn next(&mut self) -> Option<Node<'tree>> {
1867        loop {
1868            let Some(node) = self.stack.pop() else {
1869                let sibling = self.next_sibling?;
1870                self.next_sibling = sibling.next_sibling();
1871                self.stack.push(sibling);
1872                continue;
1873            };
1874            if node.child_count() == 0 {
1875                if node.kind() == "comment" || node.is_missing() {
1876                    continue;
1877                }
1878                return Some(node);
1879            }
1880            let mut cursor = node.walk();
1881            let children = node.children(&mut cursor).collect::<Vec<_>>();
1882            self.stack.extend(children.into_iter().rev());
1883        }
1884    }
1885}
1886
1887/// The macro invocation that begins at `node` when the parser could not group
1888/// it, or `None` when `node` does not begin one.
1889///
1890/// whisper.cpp's bundled `llama.h` writes
1891///
1892/// ```text
1893/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
1894///                          const char * path_model,
1895///           struct llama_model_params   params),
1896///         "use llama_model_load_from_file instead");
1897/// ```
1898///
1899/// and its own `include/whisper.h` writes
1900///
1901/// ```text
1902/// WHISPER_DEPRECATED(
1903///     WHISPER_API struct whisper_context * whisper_init_from_file(const char * path_model),
1904///     "use whisper_init_from_file_with_params instead"
1905/// );
1906/// ```
1907///
1908/// tree-sitter cannot know either name is a macro, so it reads the name and its
1909/// `(` as a function declarator whose close it never finds, and carries that
1910/// failure forward over the declarations written after it. In `llama.h` that
1911/// puts 57 KB, from line 481 to line 1535, into one `function_definition`
1912/// (#2551). In `whisper.h` it instead flattens the invocations over the
1913/// container's children -- a bare `identifier`, `(`, `parameter_declaration`,
1914/// `,` and an `ERROR` holding the hint, over and over -- and then packs lines
1915/// 232 to 456 into one `parameter_declaration` (#3094). The envelope is a
1916/// `function_definition` when a brace block falls in a swallowed tail, because
1917/// the parser borrows it for the body the bogus definition needs, and an
1918/// `ERROR` or a bare token when none does. None of that says anything about the
1919/// construct, so the node's kind is not the criterion; the token order is.
1920///
1921/// The invocation ends at the `)` that closes its own `(`, immediately followed
1922/// by `;`. That is unambiguous: a `)` the lexer left inside the hint text is
1923/// balanced by the `(` beside it, and a macro argument list carries no `;` of
1924/// its own, so a `;` reached before the close means this is some other
1925/// construct -- a macro-defined function body, say -- and the ordinary readers
1926/// keep the node.
1927///
1928/// Fails closed everywhere else too. The head must be an export-macro-shaped
1929/// token followed by `(`, the node must stand where declarations live, and an
1930/// invocation the ordinary [`macro_wrapped_declarations`] reader already has --
1931/// one the parser left whole in a declaration-scope `ERROR` and that swallowed
1932/// nothing past it -- is left to that reader.
1933fn collapsed_macro_declaration_run(
1934    node: Node<'_>,
1935    source: &str,
1936) -> Option<CollapsedMacroDeclarationRun> {
1937    let container_end = declaration_scope_container(node)?.end_byte();
1938    let mut tokens = MacroInvocationTokens::new(node);
1939    let name = tokens.next()?;
1940    if !matches!(name.kind(), "identifier" | "type_identifier")
1941        || !cpp_export_macro_token(node_text(name, source))
1942    {
1943        return None;
1944    }
1945    if tokens.next()?.kind() != "(" {
1946        return None;
1947    }
1948    let mut depth = 1usize;
1949    let invocation_end = loop {
1950        let token = tokens.next()?;
1951        match token.kind() {
1952            "(" => depth += 1,
1953            ";" => return None,
1954            ")" => {
1955                depth -= 1;
1956                if depth == 0 {
1957                    let semicolon = tokens.next()?;
1958                    if semicolon.kind() != ";" {
1959                        return None;
1960                    }
1961                    break semicolon.end_byte();
1962                }
1963            }
1964            _ => {}
1965        }
1966    };
1967    // An invocation that reaches the end of its own declaration scope swallowed
1968    // nothing: there is nothing after it left to recover, and the ordinary
1969    // readers have the invocation itself. This is also what stops the scan
1970    // below from re-entering itself, since the region it reparses for one
1971    // invocation ends exactly at that invocation's `;`.
1972    if invocation_end >= container_end {
1973        return None;
1974    }
1975    // Where the damage ends. A node that reaches past its own invocation packed
1976    // real declarations into the invocation's argument list, so the parser was
1977    // still failing when it got there and everything to the close of the scope
1978    // is suspect -- in `whisper.h` the collapse takes the `{` of the enum on
1979    // line 455 with it, and every declaration from there to the end of the file
1980    // is shredded. A node that stops at or before the invocation's `;` holds
1981    // nothing but the invocation, whose own parts the parser spread over the
1982    // siblings after it.
1983    let region_end = if node.end_byte() > invocation_end {
1984        container_end
1985    } else {
1986        invocation_end
1987    };
1988    Some(CollapsedMacroDeclarationRun {
1989        invocation_end,
1990        region_end,
1991    })
1992}
1993
1994/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
1995/// ordinary class body, and the members it swallowed.
1996///
1997/// Botan deprecates members that way:
1998///
1999/// ```text
2000/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
2001/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
2002/// ```
2003///
2004/// The parser reads the macro name as the member's type and its argument list
2005/// as a parenthesized declarator, which then swallows the attributed member
2006/// *and* the member written after it: a `field_declaration` whose `type` is a
2007/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
2008/// opening with an `ERROR` whose first part is a bare identifier -- the first
2009/// word of the string, which the lexer could not keep together.
2010///
2011/// The macro's spelling is not the criterion; that structural shape is. The
2012/// returned declarations are in source order, the first being the attributed
2013/// member and the rest the members the declarator swallowed after it.
2014fn string_attribute_macro_member_declarators<'tree>(
2015    field: Node<'tree>,
2016    source: &str,
2017) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2018    if field.kind() != "field_declaration"
2019        || field
2020            .child_by_field_name("type")
2021            .is_none_or(|type_node| type_node.kind() != "type_identifier")
2022    {
2023        return None;
2024    }
2025    let declarator = field.child_by_field_name("declarator")?;
2026    if declarator.kind() != "parenthesized_declarator" {
2027        return None;
2028    }
2029    let opening = declarator.named_child(0)?;
2030    if opening.kind() != "ERROR"
2031        || opening
2032            .named_child(0)
2033            .is_none_or(|word| word.kind() != "identifier")
2034    {
2035        return None;
2036    }
2037    let declarations = stranded_declaration_run(declarator, source).declarations;
2038    (!declarations.is_empty()).then_some(declarations)
2039}
2040
2041/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
2042/// export-macro class body leaves in front of the members that macro decorated.
2043///
2044/// The reparse reads the class body as statements, so the attribute becomes a
2045/// call statement of its own -- with the `;` the grammar had to invent -- and
2046/// the members after it are stranded in the `ERROR` that follows.
2047fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2048    let Some(call) = (node.kind() == "expression_statement")
2049        .then(|| node.named_child(0))
2050        .flatten()
2051        .filter(|child| child.kind() == "call_expression")
2052    else {
2053        return false;
2054    };
2055    call.child_by_field_name("function")
2056        .is_some_and(|function| function.kind() == "identifier")
2057        && call
2058            .child_by_field_name("arguments")
2059            .is_some_and(|arguments| {
2060                let mut cursor = arguments.walk();
2061                arguments.named_child_count() > 0
2062                    && arguments
2063                        .named_children(&mut cursor)
2064                        .all(|argument| argument.kind() == "string_literal")
2065            })
2066}
2067
2068/// The start byte of the call the region reparse left where an access-labeled
2069/// constructor was written.
2070///
2071/// A constructor is a member only inside a class body. The export-macro class
2072/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
2073/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
2074/// comma-expression under `private:`, with no declarator left in the tree. It
2075/// is still spelled in the source, though, starting at the one call under the
2076/// label whose callee is the class's own name. Botan's private six-parameter
2077/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
2078/// call is an ambiguity this declines, and so is a reparse from that byte that
2079/// does not yield exactly one constructor declarator there.
2080fn cpp_access_label_constructor_call_start(
2081    node: Node<'_>,
2082    class_name: &str,
2083    source: &str,
2084) -> Option<usize> {
2085    if node.kind() != "labeled_statement" {
2086        return None;
2087    }
2088    let label = node.named_child(0)?;
2089    if label.kind() != "statement_identifier"
2090        || !matches!(
2091            node_text(label, source).trim(),
2092            "public" | "private" | "protected"
2093        )
2094    {
2095        return None;
2096    }
2097    let mut starts = Vec::new();
2098    let mut stack = vec![node];
2099    while let Some(current) = stack.pop() {
2100        if current.kind() == "call_expression"
2101            && current
2102                .child_by_field_name("function")
2103                .is_some_and(|function| {
2104                    function.kind() == "identifier"
2105                        && node_text(function, source).trim() == class_name
2106                })
2107        {
2108            starts.push(current.start_byte());
2109        }
2110        let mut cursor = current.walk();
2111        stack.extend(current.named_children(&mut cursor));
2112    }
2113    let [start] = starts.as_slice() else {
2114        return None;
2115    };
2116    Some(*start)
2117}
2118
2119/// The `function_definition` that gives `declarator` a body, following only the
2120/// declarator chain, so a recovered callable knows whether it is a declaration
2121/// or a definition without anyone having to say.
2122fn cpp_declarator_function_definition<'tree>(
2123    declarator: Node<'tree>,
2124    ancestry: &ParentIndex<'tree>,
2125) -> Option<Node<'tree>> {
2126    let mut current = declarator;
2127    while let Some(parent) = ancestry.parent(current) {
2128        match parent.kind() {
2129            "function_definition" if parent.child_by_field_name("body").is_some() => {
2130                return Some(parent);
2131            }
2132            "pointer_declarator"
2133            | "reference_declarator"
2134            | "parenthesized_declarator"
2135            | "array_declarator" => current = parent,
2136            _ => return None,
2137        }
2138    }
2139    None
2140}
2141
2142/// Whether `node` lies in the body of a `namespace_definition` the ordinary
2143/// declaration walk still reaches, so a recovery that runs ahead of that walk
2144/// would name what it finds without the namespace.
2145fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2146    let mut current = node;
2147    while let Some(parent) = ancestry.parent(current) {
2148        if parent.kind() == "namespace_definition"
2149            && parent.child_by_field_name("body") == Some(current)
2150        {
2151            return true;
2152        }
2153        current = parent;
2154    }
2155    false
2156}
2157
2158/// Whether the source a recovered callable's byte range spells is a definition
2159/// (a declarator with a body) rather than a declaration.
2160///
2161/// A callable recovered from a mangled region owns no node of its own in the
2162/// file's tree, so the occurrence-role scan that climbs from the range to the
2163/// node containing it lands on whatever container the parser left and answers
2164/// for that instead -- which called Botan's inline `XMSS_Parameters` private
2165/// constructor a declaration and left its call site with nothing to navigate to
2166/// (#2552). Reparse the range on its own, the same offset-preserving reparse
2167/// extraction used, and read the answer from the one declaration it spells.
2168///
2169/// `None` when the range is not one recovered declaration on its own, which is
2170/// the case for every ordinary declaration, so callers keep their own answer.
2171pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2172    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2173    let root = tree.root_node();
2174    let mut cursor = root.walk();
2175    let items = root
2176        .named_children(&mut cursor)
2177        .filter(|child| child.kind() != "comment")
2178        .collect::<Vec<_>>();
2179    let [item] = items.as_slice() else {
2180        return None;
2181    };
2182    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2183        return None;
2184    }
2185    match item.kind() {
2186        "function_definition" => Some(item.child_by_field_name("body").is_some()),
2187        "declaration" | "field_declaration" => Some(false),
2188        _ => None,
2189    }
2190}
2191
2192/// Whether `node` is an envelope an attribute-like macro invocation left where
2193/// declarations were written: the declaration-scope `ERROR`
2194/// [`macro_wrapped_declarations`] reads, or the collapsed run
2195/// [`collapsed_macro_declaration_run`] reads.
2196///
2197/// Extraction and resolution must read one definition of this shape. The
2198/// resolver climbs from a declaration's recorded byte range to the node that
2199/// declares it, and a recovered declaration's range lies inside one of these
2200/// envelopes rather than inside a `declaration` node, so the climb stops here
2201/// (the same role `is_recovered_exported_class_container` plays for a recovered
2202/// class).
2203pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2204    !macro_wrapped_declarations(node, source).is_empty()
2205        || collapsed_macro_declaration_run(node, source).is_some()
2206}
2207
2208fn recover_exported_class_function_definition<'tree>(
2209    node: Node<'tree>,
2210    source: &str,
2211) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
2212    if node.kind() != "function_definition" {
2213        return None;
2214    }
2215    if let Some(prefix) = node.prev_named_sibling()
2216        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2217        && recovered.range.end_byte == node.end_byte()
2218    {
2219        return Some((node, recovered.name, recovered.raw_supertypes));
2220    }
2221    let type_node = node.child_by_field_name("type")?;
2222    let declarator = node.child_by_field_name("declarator")?;
2223
2224    if matches!(
2225        type_node.kind(),
2226        "class_specifier" | "struct_specifier" | "union_specifier"
2227    ) {
2228        let type_name = type_node
2229            .child_by_field_name("name")
2230            .and_then(|name| direct_identifier_name(name, source));
2231        let exported_macro_type = type_name
2232            .as_ref()
2233            .is_some_and(|name| cpp_export_macro_token(name));
2234        if exported_macro_type {
2235            let mut cursor = node.walk();
2236            let errors_before_declarator = node
2237                .named_children(&mut cursor)
2238                .filter(|child| {
2239                    child.kind() == "ERROR"
2240                        && child.start_byte() >= type_node.end_byte()
2241                        && child.end_byte() <= declarator.start_byte()
2242                })
2243                .collect::<Vec<_>>();
2244            if let Some(name) = errors_before_declarator
2245                .iter()
2246                .find_map(|error| displaced_exported_class_name(*error, source))
2247            {
2248                let raw_supertypes = errors_before_declarator
2249                    .iter()
2250                    .any(|error| malformed_inheritance_syntax(*error))
2251                    .then(|| recovered_malformed_base_name(declarator, source))
2252                    .flatten()
2253                    .map(|base| vec![base]);
2254                return Some((node, name, raw_supertypes));
2255            }
2256            if errors_before_declarator
2257                .iter()
2258                .any(|error| malformed_inheritance_syntax(*error))
2259            {
2260                return None;
2261            }
2262        }
2263        if !exported_macro_type
2264            && let Some(name) = type_name
2265            && !cpp_export_macro_token(&name)
2266            && let Some(base) =
2267                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2268        {
2269            return Some((node, name, Some(vec![base])));
2270        }
2271        if let Some(name) = direct_identifier_name(declarator, source)
2272            && exported_macro_type
2273            && !cpp_export_macro_token(&name)
2274        {
2275            let raw_supertypes = exported_macro_type
2276                .then(|| recovered_single_base_after_declarator(node, declarator, source))
2277                .flatten()
2278                .map(|base| vec![base]);
2279            return Some((node, name, raw_supertypes));
2280        }
2281        if declarator.kind() == "parenthesized_declarator"
2282            && type_node
2283                .child_by_field_name("name")
2284                .and_then(|name| direct_identifier_name(name, source))
2285                .is_some_and(|name| cpp_export_macro_token(&name))
2286        {
2287            if let Some((name, base)) =
2288                recovered_function_like_export_class_owner(declarator, source)
2289            {
2290                return Some((node, name, Some(vec![base])));
2291            }
2292            let body_start = node
2293                .child_by_field_name("body")
2294                .map(|body| body.start_byte())
2295                .unwrap_or(node.end_byte());
2296            let mut cursor = node.walk();
2297            if let Some(name) = node
2298                .named_children(&mut cursor)
2299                .filter(|child| {
2300                    child.kind() == "ERROR"
2301                        && child.start_byte() >= declarator.end_byte()
2302                        && child.end_byte() <= body_start
2303                })
2304                .find_map(|error| declarator_name_from_node(error, source))
2305            {
2306                return Some((node, name, None));
2307            }
2308        }
2309    }
2310
2311    let declarator_text = direct_identifier_name(declarator, source)?;
2312    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2313        return None;
2314    }
2315    class_identifier_before_body(node, source).map(|name| (node, name, None))
2316}
2317
2318fn recovered_function_like_export_class_owner(
2319    declarator: Node<'_>,
2320    source: &str,
2321) -> Option<(String, String)> {
2322    if declarator.kind() != "parenthesized_declarator" {
2323        return None;
2324    }
2325    let mut cursor = declarator.walk();
2326    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2327    let [prefix, base] = children.as_slice() else {
2328        return None;
2329    };
2330    if prefix.kind() != "ERROR"
2331        || !matches!(
2332            base.kind(),
2333            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2334        )
2335    {
2336        return None;
2337    }
2338    let mut identifiers = Vec::new();
2339    let mut prefix_cursor = prefix.walk();
2340    for child in prefix.named_children(&mut prefix_cursor) {
2341        match child.kind() {
2342            "number_literal" | "string_literal" | "char_literal" => {}
2343            "identifier" | "type_identifier" => {
2344                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2345            }
2346            _ => return None,
2347        }
2348    }
2349    let name = match identifiers.as_slice() {
2350        [name] => name.clone(),
2351        [name, final_token] if final_token == "final" => name.clone(),
2352        _ => return None,
2353    };
2354    if name.is_empty() || cpp_export_macro_token(&name) {
2355        return None;
2356    }
2357    let base = recovered_malformed_base_name(*base, source)?;
2358    Some((name, base))
2359}
2360
2361/// Collect the base names tree-sitter scattered across the recovered head of a
2362/// function-like export-macro class. `skip` names the structural children that
2363/// are not bases, such as the class name and the body. A base arrives either as
2364/// a direct sibling identifier or inside the `ERROR` node the grammar produced
2365/// for a `: public Base` fragment. The grammar leaves the `final` specifier and
2366/// the access specifiers in the same position as the bases, so drop them.
2367/// The bases a recovered function-like export-macro class head names between
2368/// `after` (the end of the class name, or of the access specifier that stands
2369/// in for it) and `before` (the start of the body, or of the `init_declarator`
2370/// that carries it). Bases arrive as bare declarator fields and inside `ERROR`
2371/// fragments, and one fragment can also hold the class name and `final`
2372/// (`M1 M2 Name final : public A`), so each part is bounded by position rather
2373/// than by the fragment that holds it.
2374fn recovered_export_head_bases(
2375    node: Node<'_>,
2376    after: usize,
2377    before: usize,
2378    source: &str,
2379) -> Vec<String> {
2380    let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2381    let mut bases = Vec::new();
2382    let mut cursor = node.walk();
2383    for child in node.named_children(&mut cursor) {
2384        if child.kind() == "ERROR" {
2385            let mut error_cursor = child.walk();
2386            bases.extend(
2387                child
2388                    .named_children(&mut error_cursor)
2389                    .filter(within)
2390                    .filter_map(|part| recovered_malformed_base_name(part, source)),
2391            );
2392        } else if within(&child)
2393            && let Some(base) = recovered_malformed_base_name(child, source)
2394        {
2395            bases.push(base);
2396        }
2397    }
2398    bases.retain(|base| {
2399        !matches!(
2400            base.as_str(),
2401            "final" | "public" | "protected" | "private" | "virtual"
2402        )
2403    });
2404    bases
2405}
2406
2407/// Whether `token` is the `final` that closes a recovered class head. The
2408/// grammar keeps it as the `final` keyword when it recognised the head
2409/// (`M1 M2 Name final : public A`) and as an `identifier` spelled `final` when
2410/// it did not (`Name final {`, `OTHER_MACRO Name final : public A`).
2411fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2412    if token.is_named() {
2413        token.kind() == "identifier" && node_text(token, source) == "final"
2414    } else {
2415        token.kind() == "final"
2416    }
2417}
2418
2419/// The class name of a recovered function-like export-macro class head, read by
2420/// position rather than spelling (#2557). `node` is the sibling tree-sitter
2421/// built from the head after `class MACRO(2, 0)`, and `tail` the child that
2422/// carries the body. The head's tokens, in source order with `ERROR` fragments
2423/// flattened, read `macros... name [final] [: bases...]`: the name is the last
2424/// identifier before the head ends, and the head ends at `final`, at the base
2425/// clause `:`, or at `tail`. Every identifier before the name is decoration
2426/// (`class MACRO(2, 0) OTHER_MACRO Name`), and a class named in capitals
2427/// (`X509_CA`) is a class name like any other.
2428///
2429/// A `MISSING` identifier is a zero-width node tree-sitter invented to close a
2430/// rule, not a token of the source, so it is never the name.
2431fn recovered_export_head_name<'tree>(
2432    node: Node<'tree>,
2433    tail: Node<'tree>,
2434    source: &str,
2435) -> Option<Node<'tree>> {
2436    export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2437}
2438
2439/// The class name of a head whose tokens straddle the recovered pair: the
2440/// grammar can keep `class MACRO(3, 6) Name final :` in the prefix fragment and
2441/// only the base list and the body in the sibling (Botan's TPM2 keys, #2924).
2442/// The positional rule is the same, read over the two token runs in source
2443/// order, so a name in either node wins by position rather than by which node
2444/// holds it.
2445fn recovered_export_pair_head_name<'tree>(
2446    prefix: Node<'tree>,
2447    sibling: Node<'tree>,
2448    tail: Node<'tree>,
2449    source: &str,
2450) -> Option<Node<'tree>> {
2451    let mut tokens = export_head_tokens(prefix, None);
2452    tokens.extend(export_head_tokens(sibling, Some(tail)));
2453    export_head_name_from_tokens(&tokens, source)
2454}
2455
2456/// One node's head tokens in source order, with `ERROR` fragments spliced in
2457/// place (an `ERROR` here is a grouping failure, not a construct) and stopping
2458/// before `tail` when the caller names one.
2459fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2460    let mut tokens = Vec::new();
2461    let mut cursor = node.walk();
2462    for child in node.children(&mut cursor) {
2463        if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2464            break;
2465        }
2466        if child.kind() == "ERROR" {
2467            let mut fragment_cursor = child.walk();
2468            tokens.extend(child.children(&mut fragment_cursor));
2469        } else {
2470            tokens.push(child);
2471        }
2472    }
2473    tokens
2474}
2475
2476/// The last identifier before the head ends, per the positional rule.
2477fn export_head_name_from_tokens<'tree>(
2478    tokens: &[Node<'tree>],
2479    source: &str,
2480) -> Option<Node<'tree>> {
2481    let mut name = None;
2482    for token in tokens.iter().copied() {
2483        if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2484        {
2485            break;
2486        }
2487        if token.is_named()
2488            && !token.is_missing()
2489            && matches!(
2490                token.kind(),
2491                "identifier" | "type_identifier" | "field_identifier"
2492            )
2493        {
2494            name = Some(token);
2495        }
2496    }
2497    name
2498}
2499
2500/// The `init_declarator` whose `value` carries the class body when the grammar
2501/// keeps a function-like export-macro class head as one `declaration`.
2502fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2503    let mut cursor = declaration.walk();
2504    declaration
2505        .named_children(&mut cursor)
2506        .find(|child| child.kind() == "init_declarator")
2507}
2508
2509/// Read the bases and the initializer-list body of a `declaration`-shaped tail
2510/// of a function-like export-macro class head. The grammar splits a base list
2511/// across bare declarator fields, `ERROR` fragments, and one trailing
2512/// `init_declarator` whose `value` is the class body. `head_end` is where the
2513/// class identity ends and the bases can begin: the end of the access specifier
2514/// when the class name went to a statement label, and the end of the class name
2515/// itself otherwise.
2516fn recovered_export_declaration_tail<'tree>(
2517    declaration: Node<'tree>,
2518    head_end: usize,
2519    source: &str,
2520) -> Option<(Vec<String>, Node<'tree>)> {
2521    let init = recovered_export_init_declarator(declaration)?;
2522    let body = init.child_by_field_name("value")?;
2523    if body.kind() != "initializer_list" {
2524        return None;
2525    }
2526    let mut bases = recovered_export_head_bases(declaration, head_end, init.start_byte(), source);
2527    bases.extend(recovered_export_head_bases(
2528        init,
2529        init.start_byte(),
2530        body.start_byte(),
2531        source,
2532    ));
2533    Some((bases, body))
2534}
2535
2536/// Whether `node` is the head fragment tree-sitter leaves where
2537/// `class MACRO(args)` was written: the class keyword and the export macro's
2538/// function-like invocation in that order, with nothing else between them.
2539/// Spelling plays no part -- the invocation's `(` following the macro
2540/// identifier directly is what makes it function-like (#2557).
2541///
2542/// Two reductions produce the head. On its own the grammar keeps a body-less
2543/// `class_specifier` named after the macro inside a declaration-scope `ERROR`,
2544/// with the invocation's `(`, arguments and `)` beside it. After object-like
2545/// macro lines -- Botan's `BOTAN_DIAGNOSTIC_PUSH` /
2546/// `BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE` -- it instead keeps a
2547/// `declaration` whose class keyword is demoted to a bare `identifier` and
2548/// whose macro invocation becomes the trailing `init_declarator` (#2924).
2549fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2550    match node.kind() {
2551        "ERROR" => {
2552            let Some(class_node) = first_class_like_child(node) else {
2553                return false;
2554            };
2555            if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2556                return false;
2557            }
2558            if class_node
2559                .child_by_field_name("name")
2560                .and_then(|name| direct_identifier_name(name, source))
2561                .is_none()
2562            {
2563                return false;
2564            }
2565            class_node
2566                .next_sibling()
2567                .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2568        }
2569        "declaration" => {
2570            let mut cursor = node.walk();
2571            let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2572            // The class keyword lost its own node: it survives as a bare
2573            // identifier spelled `class`, `struct` or `union`, which is never a
2574            // declarator or a type in well-formed code. It can sit inside an
2575            // `ERROR` beside the object-like macro names, and an `ERROR` here is
2576            // a grouping failure rather than a construct, so read through it.
2577            let Some(keyword) = children
2578                .iter()
2579                .copied()
2580                .flat_map(|child| {
2581                    let mut cursor = child.walk();
2582                    if child.kind() == "ERROR" {
2583                        child.named_children(&mut cursor).collect::<Vec<_>>()
2584                    } else {
2585                        vec![child]
2586                    }
2587                })
2588                .find(|child| {
2589                    child.kind() == "identifier"
2590                        && matches!(node_text(*child, source), "class" | "struct" | "union")
2591                })
2592            else {
2593                return false;
2594            };
2595            children.last().is_some_and(|init| {
2596                init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2597                    let mut cursor = init.walk();
2598                    let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2599                    matches!(parts.as_slice(), [macro_name, arguments]
2600                        if matches!(macro_name.kind(), "identifier" | "type_identifier")
2601                            && arguments.kind() == "argument_list")
2602                }
2603            })
2604        }
2605        _ => false,
2606    }
2607}
2608
2609fn recover_function_like_export_class_pair(
2610    node: Node<'_>,
2611    source: &str,
2612) -> Option<RecoveredFunctionLikeExportClassPair> {
2613    if !is_function_like_export_class_head(node, source) {
2614        return None;
2615    }
2616    let sibling = node.next_named_sibling()?;
2617    let (name, raw_supertypes, body) = match sibling.kind() {
2618        // At translation-unit scope tree-sitter can leave the malformed class
2619        // head in one ERROR node and parse its body as the adjacent compound
2620        // statement. The head still carries the export invocation, displaced
2621        // class name, and optional `final` token in source order, so recover
2622        // the name by the same positional rule as the other shapes.
2623        "compound_statement" => (
2624            recovered_export_pair_head_name(node, sibling, sibling, source)
2625                .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2626            None,
2627            sibling,
2628        ),
2629        "expression_statement" => {
2630            let compound = sibling.named_child(0)?;
2631            if compound.kind() != "compound_literal_expression" {
2632                return None;
2633            }
2634            let body = compound.child_by_field_name("value")?;
2635            if body.kind() != "initializer_list" {
2636                return None;
2637            }
2638            (
2639                compound
2640                    .child_by_field_name("type")
2641                    .and_then(|name| direct_identifier_name(name, source))?,
2642                None,
2643                body,
2644            )
2645        }
2646        "labeled_statement" => {
2647            let label = sibling.child_by_field_name("label")?;
2648            if label.kind() != "statement_identifier" {
2649                return None;
2650            }
2651            let name = normalize_cpp_whitespace(node_text(label, source));
2652            let declaration = sibling
2653                .named_children(&mut sibling.walk())
2654                .find(|child| child.kind() == "declaration")?;
2655            let access = declaration.child_by_field_name("type")?;
2656            if !matches!(
2657                node_text(access, source),
2658                "public" | "protected" | "private"
2659            ) {
2660                return None;
2661            }
2662            let (bases, body) =
2663                recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
2664            (name, (!bases.is_empty()).then_some(bases), body)
2665        }
2666        // `class MACRO(2, 0) Name final { ... };`,
2667        // `class MACRO(2, 0) Name final : public Base { ... };`, and
2668        // `class MACRO(2, 0) OTHER_MACRO Name { ... };`. The head's identifiers
2669        // spread over the `type` field, the declarator field, and `ERROR`
2670        // fragments beside them; the name is the last one before the head ends.
2671        "function_definition" => {
2672            let body = sibling.child_by_field_name("body")?;
2673            if body.kind() != "compound_statement" {
2674                return None;
2675            }
2676            let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
2677            let bases = recovered_export_head_bases(
2678                sibling,
2679                name_node.end_byte(),
2680                body.start_byte(),
2681                source,
2682            );
2683            (
2684                normalize_cpp_whitespace(node_text(name_node, source)),
2685                (!bases.is_empty()).then_some(bases),
2686                body,
2687            )
2688        }
2689        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
2690        // comma-separated base list makes the grammar keep the whole tail as one
2691        // declaration whose trailing `init_declarator` carries the body.
2692        "declaration" => {
2693            let init = recovered_export_init_declarator(sibling)?;
2694            let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
2695            let (bases, body) =
2696                recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
2697            (
2698                normalize_cpp_whitespace(node_text(name_node, source)),
2699                (!bases.is_empty()).then_some(bases),
2700                body,
2701            )
2702        }
2703        _ => return None,
2704    };
2705    debug_assert!(
2706        !name.is_empty(),
2707        "a recovered class head names its class by an identifier token"
2708    );
2709    let range = Range {
2710        start_byte: node.start_byte(),
2711        end_byte: sibling.end_byte(),
2712        start_line: node.start_position().row + 1,
2713        end_line: sibling.end_position().row + 1,
2714    };
2715    Some(RecoveredFunctionLikeExportClassPair {
2716        name,
2717        raw_supertypes,
2718        range,
2719        fragmented_body: recovered_fragmented_export_body(body, range)?,
2720    })
2721}
2722
2723/// Recover a function-like export-macro class that tree-sitter embedded in a
2724/// larger error after an earlier malformed class body. The grammar still
2725/// preserves every part of the class head: the `class` token, export macro
2726/// identifier and argument list, displaced class identifier, access specifier,
2727/// base field, and initializer-list-shaped body. Match only that complete
2728/// structured sequence and keep each recovered class's exact byte envelope.
2729fn recover_embedded_function_like_export_classes(
2730    node: Node<'_>,
2731    source: &str,
2732) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2733    if !node.is_error() {
2734        return Vec::new();
2735    }
2736
2737    let mut nodes = Vec::new();
2738    let mut stack = vec![node];
2739    while let Some(current) = stack.pop() {
2740        nodes.push(current);
2741        push_children_reversed(current, &mut stack);
2742    }
2743    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2744
2745    // The scans below ask the same handful of kind questions once per node of
2746    // the error subtree, so the kinds are resolved to symbol ids first (#3097).
2747    let language = node.language();
2748    let class_kind = NodeKindIds::new(&language, "class");
2749    let identifier_kinds = [
2750        NodeKindIds::new(&language, "identifier"),
2751        NodeKindIds::new(&language, "type_identifier"),
2752        NodeKindIds::new(&language, "field_identifier"),
2753    ];
2754    let argument_list_kind = NodeKindIds::new(&language, "argument_list");
2755    let colon_kind = NodeKindIds::new(&language, ":");
2756    let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
2757
2758    let mut recovered = Vec::new();
2759    for class_token in nodes
2760        .iter()
2761        .copied()
2762        .filter(|child| !child.is_named() && class_kind.matches(*child))
2763    {
2764        let row = class_token.start_position().row;
2765        // The head after the `class` token reads `MACRO(args) macros... name
2766        // [final] : bases`. The macro is the first identifier, a function-like
2767        // invocation when its argument list is the next thing after it, and the
2768        // name is the last identifier before the head ends at `final` or at the
2769        // base clause `:`. Spelling plays no part (#2557).
2770        let is_identifier = |candidate: &Node<'_>| {
2771            !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
2772        };
2773        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2774            candidate.start_byte() >= class_token.end_byte()
2775                && candidate.start_position().row == row
2776                && is_identifier(candidate)
2777        }) else {
2778            continue;
2779        };
2780        let Some(arguments) = nodes.iter().copied().find(|candidate| {
2781            argument_list_kind.matches(*candidate)
2782                && candidate.start_byte() >= macro_name.end_byte()
2783                && candidate.start_position().row == row
2784        }) else {
2785            continue;
2786        };
2787        if nodes.iter().any(|candidate| {
2788            is_identifier(candidate)
2789                && candidate.start_byte() >= macro_name.end_byte()
2790                && candidate.end_byte() <= arguments.start_byte()
2791        }) {
2792            continue;
2793        }
2794        let Some(head_end) = nodes.iter().copied().find(|candidate| {
2795            candidate.start_byte() >= arguments.end_byte()
2796                && (recovered_export_head_final(*candidate, source)
2797                    || (!candidate.is_named() && colon_kind.matches(*candidate)))
2798        }) else {
2799            continue;
2800        };
2801        let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
2802            is_identifier(candidate)
2803                && candidate.start_byte() >= arguments.end_byte()
2804                && candidate.end_byte() <= head_end.start_byte()
2805                && candidate.start_position().row == row
2806        }) else {
2807            continue;
2808        };
2809        let name = normalize_cpp_whitespace(node_text(name_node, source));
2810        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2811            field_initializer_kind.matches(*candidate)
2812                && candidate.start_byte() >= name_node.end_byte()
2813                && candidate
2814                    .child_by_field_name("field")
2815                    .or_else(|| candidate.named_child(0))
2816                    .is_some()
2817                && candidate
2818                    .child_by_field_name("value")
2819                    .or_else(|| {
2820                        let mut cursor = candidate.walk();
2821                        candidate
2822                            .named_children(&mut cursor)
2823                            .find(|child| child.kind() == "initializer_list")
2824                    })
2825                    .is_some_and(|value| value.kind() == "initializer_list")
2826        }) else {
2827            continue;
2828        };
2829        let has_access = nodes.iter().copied().any(|candidate| {
2830            candidate.start_byte() >= name_node.end_byte()
2831                && candidate.end_byte() <= base_initializer.start_byte()
2832                && matches!(
2833                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2834                    "public" | "protected" | "private"
2835                )
2836        });
2837        if !has_access {
2838            continue;
2839        }
2840        let Some(base_node) = base_initializer
2841            .child_by_field_name("field")
2842            .or_else(|| base_initializer.named_child(0))
2843        else {
2844            continue;
2845        };
2846        let Some(base) = recovered_malformed_base_name(base_node, source) else {
2847            continue;
2848        };
2849        let body = base_initializer
2850            .child_by_field_name("value")
2851            .or_else(|| {
2852                let mut cursor = base_initializer.walk();
2853                base_initializer
2854                    .named_children(&mut cursor)
2855                    .find(|child| child.kind() == "initializer_list")
2856            })
2857            .expect("initializer-list value checked above");
2858        let range = Range {
2859            start_byte: class_token.start_byte(),
2860            end_byte: body.end_byte(),
2861            start_line: class_token.start_position().row + 1,
2862            end_line: body.end_position().row + 1,
2863        };
2864        if recovered
2865            .iter()
2866            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
2867                existing.name == name && existing.range == range
2868            })
2869        {
2870            continue;
2871        }
2872        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
2873            name,
2874            range,
2875            raw_supertypes: vec![base],
2876            fragmented_body: match recovered_fragmented_export_body(body, range) {
2877                Some(fragmented) => fragmented,
2878                None => continue,
2879            },
2880        });
2881    }
2882    recovered
2883}
2884
2885fn lifted_function_like_export_class_namespace<'tree>(
2886    node: Node<'tree>,
2887    source: &str,
2888    ancestry: &ParentIndex<'tree>,
2889) -> Option<String> {
2890    // A long malformed body can embed the next exported class several levels
2891    // below a bogus top-level function_definition. Compare namespace evidence
2892    // against that top-level envelope, not only the recovered ERROR's direct
2893    // parent. The source tree still proves the same boundary: one earlier
2894    // malformed namespace and one later standalone closing brace.
2895    let mut anchor = node;
2896    let parent = loop {
2897        let parent = ancestry.parent(anchor)?;
2898        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
2899            break parent;
2900        }
2901        anchor = parent;
2902    };
2903    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
2904        sibling.start_byte() > anchor.end_byte()
2905            && sibling.kind() == "ERROR"
2906            && sibling.named_child_count() == 0
2907            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
2908    });
2909    if !has_later_close {
2910        return None;
2911    }
2912    let candidates = parent
2913        .named_children(&mut parent.walk())
2914        .filter(|sibling| {
2915            sibling.kind() == "namespace_definition"
2916                && sibling.has_error()
2917                && sibling.end_byte() < anchor.start_byte()
2918        })
2919        .filter_map(|namespace| {
2920            namespace
2921                .child_by_field_name("name")
2922                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
2923                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
2924        })
2925        .collect::<Vec<_>>();
2926    let [namespace] = candidates.as_slice() else {
2927        return None;
2928    };
2929    Some(namespace.clone())
2930}
2931
2932pub(crate) fn recovered_function_like_export_class_pair_has_body(
2933    node: Node<'_>,
2934    source: &str,
2935    identifier: &str,
2936    range: &Range,
2937) -> bool {
2938    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
2939        recovered.name == identifier
2940            && recovered.range.start_byte == range.start_byte
2941            && recovered.range.end_byte == range.end_byte
2942    })
2943}
2944
2945/// One file's embedded export-macro class recovery, resolved once and keyed by
2946/// the `ERROR` node that mints each set.
2947///
2948/// [`recover_embedded_function_like_export_classes`] collects and sorts an
2949/// `ERROR` node's whole subtree, and the declaration-strength question asks it
2950/// once per class-like unit in the file. On a translation unit the parser could
2951/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
2952/// node spans most of the file -- that is one full subtree pass per class,
2953/// quadratic in the file's size, and it was 78% of that file's inverse scan
2954/// (#1496).
2955///
2956/// Keyed by byte span rather than node identity, so one analyzer generation's
2957/// re-parses of the same content share the index. A nested `ERROR` that shares
2958/// its parent's span recovers the same classes: the only node the parent adds
2959/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
2960#[derive(Default)]
2961pub struct CppRecoveredExportClassIndex {
2962    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
2963}
2964
2965impl CppRecoveredExportClassIndex {
2966    pub fn build(root: Node<'_>, source: &str) -> Self {
2967        let mut by_error_node: HashMap<
2968            (usize, usize),
2969            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
2970        > = HashMap::default();
2971        // One cursor for the whole-file walk, and the error question asked by
2972        // symbol rather than by `kind()` string (#3097).
2973        let mut cursor = root.walk();
2974        let mut stack = vec![root];
2975        while let Some(node) = stack.pop() {
2976            if node.is_error() {
2977                let recovered = recover_embedded_function_like_export_classes(node, source);
2978                if !recovered.is_empty() {
2979                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
2980                }
2981            }
2982            stack.extend(node.named_children(&mut cursor));
2983        }
2984        Self { by_error_node }
2985    }
2986
2987    /// The bytes this index holds, for the analyzer cache's weight.
2988    pub fn approximate_size(&self) -> usize {
2989        self.by_error_node
2990            .values()
2991            .fold(0usize, |total, recovered| {
2992                recovered.iter().fold(
2993                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
2994                    |acc, class| {
2995                        acc.saturating_add(std::mem::size_of::<
2996                            RecoveredEmbeddedFunctionLikeExportClass,
2997                        >())
2998                        .saturating_add(class.name.len())
2999                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3000                    },
3001                )
3002            })
3003    }
3004
3005    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3006        self.by_error_node
3007            .get(&(node.start_byte(), node.end_byte()))
3008            .is_some_and(|recovered| {
3009                recovered.iter().any(|class| {
3010                    class.name == identifier
3011                        && class.range.start_byte == range.start_byte
3012                        && class.range.end_byte == range.end_byte
3013                })
3014            })
3015    }
3016}
3017
3018// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
3019// `recovered_class_body_at` pops while deciding whether a recovered class shape
3020// claims one declaration range. The count is deterministic for a given source,
3021// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
3022// pins it directly instead of timing the walk, the way #2358 pinned the
3023// `remove_code_unit` scan.
3024#[cfg(any(test, feature = "test-support"))]
3025thread_local! {
3026    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3027        const { std::cell::Cell::new(0) };
3028}
3029
3030/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
3031/// the calling thread since the last
3032/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
3033#[cfg(any(test, feature = "test-support"))]
3034#[doc(hidden)]
3035pub fn recovered_class_body_node_visits_for_test() -> usize {
3036    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3037}
3038
3039/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
3040#[cfg(any(test, feature = "test-support"))]
3041#[doc(hidden)]
3042pub fn reset_recovered_class_body_node_visits_for_test() {
3043    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3044}
3045
3046#[cfg(any(test, feature = "test-support"))]
3047fn record_recovered_class_body_visit() {
3048    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3049}
3050
3051#[cfg(not(any(test, feature = "test-support")))]
3052fn record_recovered_class_body_visit() {}
3053
3054/// Whether a recovered class shape named `identifier` owns `range`, and if so
3055/// whether that shape has a body.
3056///
3057/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
3058/// forward declaration, and `None` means no recovered shape claims the range,
3059/// so the caller reads the plain `class_specifier` family instead. This is the
3060/// single definition of "does a recovered class have a body": the resolver's
3061/// declaration-strength answer and the navigation occurrence role both read it,
3062/// so an export-macro class is a definition on both paths.
3063///
3064/// The walk follows only the nodes whose span covers `range.start_byte`, which
3065/// is every node that can answer. Each recovered shape reports a range that
3066/// starts at the node's own start byte (the function-like export pair, whose
3067/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
3068/// class, which the caller gates on an equal start) or at a token inside the
3069/// node (the embedded export class, keyed on its `class` token, and the
3070/// exported class wrapper, gated on containment here), and every one of them is
3071/// accepted only on an exact match with `range`. Descending everywhere instead
3072/// made one declaration-strength question cost a full pass over the file, so
3073/// asking it once per reference was quadratic in file size: 80% of the 385 s
3074/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
3075/// (#1496).
3076pub(crate) fn recovered_class_body_at(
3077    recovered_export_classes: &CppRecoveredExportClassIndex,
3078    root: Node<'_>,
3079    source: &str,
3080    identifier: &str,
3081    range: &Range,
3082) -> Option<bool> {
3083    let covers_range_start = |node: &Node<'_>| {
3084        node.start_byte() <= range.start_byte
3085            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3086    };
3087    let mut stack = vec![root];
3088    let mut saw_forward = false;
3089    while let Some(node) = stack.pop() {
3090        record_recovered_class_body_visit();
3091        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
3092        // so an unequal start settles it before the recovery reads the node's
3093        // children at all.
3094        if (node.start_byte() == range.start_byte
3095            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3096            || recovered_export_classes.claims(node, identifier, range)
3097            || (node.start_byte() == range.start_byte
3098                && recovered_fragmented_class_has_body(node, source, identifier, range))
3099        {
3100            return Some(true);
3101        }
3102        if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3103            return Some(true);
3104        }
3105        // Macro-decorated exported classes are recovered from a malformed
3106        // function_definition/declaration wrapper. Their indexed class range starts at
3107        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
3108        // may also extend the indexed range beyond the wrapper through trailing class
3109        // fragments. Match the structured container that owns the range start by its
3110        // recovered name instead of requiring identical boundaries.
3111        if node.start_byte() <= range.start_byte
3112            && range.start_byte < node.end_byte()
3113            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3114        {
3115            if has_body {
3116                return Some(true);
3117            }
3118            saw_forward = true;
3119            continue;
3120        }
3121        let mut cursor = node.walk();
3122        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3123    }
3124    saw_forward.then_some(false)
3125}
3126
3127/// Whether the collapsed-aggregate recovery reads `node`'s children as the
3128/// head of `identifier`'s member list at `range`.
3129///
3130/// An aggregate whose member list opens with a field-list macro invocation
3131/// never forms a specifier, so the ordinary strength read finds no specifier
3132/// node at the indexed range and answers `Unknown`. A complete definition
3133/// recovered this way then loses the defining-type choice to a forward
3134/// declaration of the same tag in another header, and every member the
3135/// definition declares becomes unreachable through it: libuv's
3136/// `struct uv_loop_s` in `include/uv.h` lost to `struct uv_loop_s;` in
3137/// `include/uv/unix.h`, hiding `uv_loop_s.wq` (#3098). The recovery already
3138/// proves the body, so it answers for its own ranges.
3139fn recovered_collapsed_aggregate_has_body(
3140    node: Node<'_>,
3141    source: &str,
3142    identifier: &str,
3143    range: &Range,
3144) -> bool {
3145    let claims = |head: &CppCollapsedAggregateHead<'_>| {
3146        head.key.start_byte() == range.start_byte
3147            && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3148    };
3149    if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3150        return true;
3151    }
3152    if !node.is_error() {
3153        return false;
3154    }
3155    let mut cursor = node.walk();
3156    let children = node.children(&mut cursor).collect::<Vec<_>>();
3157    children
3158        .iter()
3159        .enumerate()
3160        .filter(|(_, child)| {
3161            child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3162        })
3163        .any(|(index, _)| {
3164            cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3165        })
3166}
3167
3168/// Whether `node` is the base type displaced into the declarator field of an
3169/// export-macro class that tree-sitter represented as a declaration or
3170/// function definition.
3171///
3172/// Declaration extraction already recovers this exact malformed envelope as a
3173/// class and records the declarator as its base. Reference extraction must use
3174/// the same structural fact instead of treating the node as a function name.
3175pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3176    if !matches!(
3177        node.kind(),
3178        "qualified_identifier" | "scoped_type_identifier" | "template_type"
3179    ) {
3180        return false;
3181    }
3182    if let Some(function) = node.parent().filter(|parent| {
3183        parent.kind() == "function_definition"
3184            && parent
3185                .child_by_field_name("declarator")
3186                .is_some_and(|declarator| same_node(declarator, node))
3187    }) {
3188        return recover_exported_class_function_definition(function, source)
3189            .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
3190    }
3191    let Some(initializer) = node.parent().filter(|parent| {
3192        parent.kind() == "init_declarator"
3193            && parent
3194                .child_by_field_name("declarator")
3195                .is_some_and(|declarator| same_node(declarator, node))
3196    }) else {
3197        return false;
3198    };
3199    initializer
3200        .parent()
3201        .filter(|parent| parent.kind() == "declaration")
3202        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3203        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3204}
3205
3206/// Recover the class item from a region reparse that still carries the
3207/// sentinel's synthetic function envelope.  An unknown class attribute can
3208/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
3209/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
3210/// then nested below that function, so direct class-child lookup is not enough.
3211struct CppSentinelReparsedClass<'tree> {
3212    declaration_node: Node<'tree>,
3213    name: String,
3214    body: Node<'tree>,
3215    raw_supertypes: Option<Vec<String>>,
3216}
3217
3218fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3219    let mut cursor = root.walk();
3220    root.named_children(&mut cursor)
3221        .find(|child| child.kind() != "comment")
3222        .filter(|child| child.kind() == "template_declaration")
3223}
3224
3225fn cpp_sentinel_reparsed_class<'tree>(
3226    root: Node<'tree>,
3227    template_node: Option<Node<'tree>>,
3228    source: &str,
3229    ancestry: &ParentIndex<'tree>,
3230) -> Option<CppSentinelReparsedClass<'tree>> {
3231    let container = template_node.unwrap_or(root);
3232    let mut cursor = container.walk();
3233    for child in container.named_children(&mut cursor) {
3234        if matches!(
3235            child.kind(),
3236            "class_specifier" | "struct_specifier" | "union_specifier"
3237        ) {
3238            let name = class_like_name(child, source, ancestry)?;
3239            let body = cpp_body_node(child)?;
3240            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3241                .then(|| extract_cpp_supertypes(child, source));
3242            return Some(CppSentinelReparsedClass {
3243                declaration_node: child,
3244                name,
3245                body,
3246                raw_supertypes,
3247            });
3248        }
3249        if child.kind() == "declaration"
3250            && let Some(class_node) = first_class_like_child(child)
3251        {
3252            let name = class_like_name(class_node, source, ancestry)?;
3253            let body = cpp_body_node(class_node)?;
3254            let raw_supertypes =
3255                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3256                    .then(|| extract_cpp_supertypes(class_node, source));
3257            return Some(CppSentinelReparsedClass {
3258                declaration_node: class_node,
3259                name,
3260                body,
3261                raw_supertypes,
3262            });
3263        }
3264        // Only when the nested class item carries its own body. A bodyless
3265        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
3266        // a function definition -- is the export-macro shape recovered by the
3267        // next arm, and must fall through to it rather than abort the search.
3268        if child.kind() == "function_definition"
3269            && let Some(class_node) = first_class_like_child(child)
3270            && let Some(body) = cpp_body_node(class_node)
3271            && let Some(name) = class_like_name(class_node, source, ancestry)
3272        {
3273            let raw_supertypes =
3274                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3275                    .then(|| extract_cpp_supertypes(class_node, source));
3276            return Some(CppSentinelReparsedClass {
3277                declaration_node: class_node,
3278                name,
3279                body,
3280                raw_supertypes,
3281            });
3282        }
3283        if child.kind() == "function_definition"
3284            && let Some((_, name, raw_supertypes)) =
3285                recover_exported_class_function_definition(child, source)
3286        {
3287            let body = cpp_body_node(child)?;
3288            return Some(CppSentinelReparsedClass {
3289                declaration_node: child,
3290                name,
3291                body,
3292                raw_supertypes,
3293            });
3294        }
3295    }
3296    None
3297}
3298
3299fn recovered_postfix_export_macro_base(
3300    node: Node<'_>,
3301    type_node: Node<'_>,
3302    declarator: Node<'_>,
3303    source: &str,
3304) -> Option<String> {
3305    let mut cursor = node.walk();
3306    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3307        child.kind() == "ERROR"
3308            && child.start_byte() >= type_node.end_byte()
3309            && child.end_byte() <= declarator.start_byte()
3310            && postfix_export_macro_inheritance(*child, source)
3311    });
3312    malformed_clauses.next()?;
3313    if malformed_clauses.next().is_some() {
3314        return None;
3315    }
3316    recovered_malformed_base_name(declarator, source)
3317}
3318
3319fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3320    let mut macro_count = 0;
3321    let mut colon_count = 0;
3322    let mut access_count = 0;
3323    for index in 0..node.child_count() {
3324        let Some(child) = node.child(index) else {
3325            return false;
3326        };
3327        match child.kind() {
3328            "identifier" | "type_identifier" if child.is_named() => {
3329                let candidate = normalize_cpp_whitespace(node_text(child, source));
3330                if !cpp_export_macro_token(&candidate) {
3331                    return false;
3332                }
3333                macro_count += 1;
3334            }
3335            ":" if !child.is_named() => colon_count += 1,
3336            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3337            _ => return false,
3338        }
3339    }
3340    macro_count == 1 && colon_count == 1 && access_count == 1
3341}
3342
3343fn recovered_single_base_after_declarator(
3344    node: Node<'_>,
3345    declarator: Node<'_>,
3346    source: &str,
3347) -> Option<String> {
3348    let body_start = node
3349        .child_by_field_name("body")
3350        .map(|body| body.start_byte())
3351        .unwrap_or(node.end_byte());
3352    let mut cursor = node.walk();
3353    let mut bases = node
3354        .named_children(&mut cursor)
3355        .filter(|child| {
3356            child.kind() == "ERROR"
3357                && child.start_byte() >= declarator.end_byte()
3358                && child.end_byte() <= body_start
3359        })
3360        .filter_map(|error| displaced_exported_class_name(error, source));
3361    let base = bases.next()?;
3362    bases.next().is_none().then_some(base)
3363}
3364
3365fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3366    (0..node.child_count()).any(|index| {
3367        node.child(index)
3368            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
3369    })
3370}
3371
3372pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3373    recover_exported_class_function_definition(node, source).is_some()
3374}
3375
3376fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3377    matches!(
3378        kind,
3379        "ERROR"
3380            | "preproc_if"
3381            | "preproc_ifdef"
3382            | "preproc_ifndef"
3383            | "preproc_else"
3384            | "preproc_elif"
3385    ) || (kind == "labeled_statement" && in_class_scope)
3386}
3387
3388pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3389    if node.kind() != "declaration" {
3390        return false;
3391    }
3392    let mut ancestor = node.parent();
3393    while let Some(container) = ancestor {
3394        match container.kind() {
3395            "compound_statement" => {
3396                return container.parent().is_some_and(|class_container| {
3397                    is_recovered_exported_class_container(class_container, source)
3398                });
3399            }
3400            // These containers preserve ScopeInfo in visit_node. declaration_list is
3401            // the body container selected for a linkage specification.
3402            "template_declaration" | "linkage_specification" | "declaration_list" => {}
3403            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3404            _ => return false,
3405        }
3406        ancestor = container.parent();
3407    }
3408    false
3409}
3410
3411pub fn recovered_exported_class_has_body(
3412    node: Node<'_>,
3413    source: &str,
3414    expected_name: &str,
3415) -> Option<bool> {
3416    match node.kind() {
3417        "function_definition" => {
3418            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
3419            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3420        }
3421        "declaration" | "field_declaration" => {
3422            let recovered = recover_exported_class_declaration(node, source)?;
3423            (recovered.name == expected_name).then(|| recovered.body.is_some())
3424        }
3425        _ => None,
3426    }
3427}
3428
3429fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3430    let body_start = node
3431        .child_by_field_name("body")
3432        .map(|body| body.start_byte())
3433        .unwrap_or(node.end_byte());
3434    let mut stack = Vec::new();
3435    for index in (0..node.named_child_count()).rev() {
3436        let Some(child) = node.named_child(index) else {
3437            continue;
3438        };
3439        if child.start_byte() >= body_start {
3440            continue;
3441        }
3442        stack.push(child);
3443    }
3444
3445    let mut best = None;
3446    while let Some(current) = stack.pop() {
3447        if matches!(current.kind(), "identifier" | "type_identifier") {
3448            let name = normalize_cpp_whitespace(node_text(current, source));
3449            if !name.is_empty()
3450                && !cpp_export_macro_token(&name)
3451                && !matches!(name.as_str(), "class" | "struct" | "union")
3452            {
3453                best = Some(name);
3454            }
3455            continue;
3456        }
3457
3458        for index in (0..current.named_child_count()).rev() {
3459            if let Some(child) = current.named_child(index)
3460                && child.start_byte() < body_start
3461            {
3462                stack.push(child);
3463            }
3464        }
3465    }
3466    best
3467}
3468
3469fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3470    if node.kind() == "declaration"
3471        && node
3472            .child_by_field_name("type")
3473            .or_else(|| first_class_like_child(node))
3474            .is_some_and(|type_node| {
3475                matches!(
3476                    type_node.kind(),
3477                    "class_specifier" | "struct_specifier" | "union_specifier"
3478                )
3479            })
3480        && let Some(name) = node
3481            .child_by_field_name("declarator")
3482            .and_then(|declarator| declarator_name_from_node(declarator, source))
3483        && !cpp_export_macro_token(&name)
3484    {
3485        return Some(name);
3486    }
3487
3488    if node.kind() == "function_definition"
3489        && node.child_by_field_name("type").is_some_and(|type_node| {
3490            matches!(
3491                type_node.kind(),
3492                "class_specifier" | "struct_specifier" | "union_specifier"
3493            )
3494        })
3495        && let Some(name) = node
3496            .child_by_field_name("declarator")
3497            .and_then(|declarator| direct_identifier_name(declarator, source))
3498        && !cpp_export_macro_token(&name)
3499    {
3500        return Some(name);
3501    }
3502
3503    let class_node = if matches!(
3504        node.kind(),
3505        "class_specifier" | "struct_specifier" | "union_specifier"
3506    ) {
3507        node
3508    } else {
3509        first_class_like_child(node)?
3510    };
3511    class_like_name_from_children(class_node, source)
3512}
3513
3514fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3515    if !matches!(
3516        node.kind(),
3517        "identifier" | "field_identifier" | "type_identifier"
3518    ) {
3519        return None;
3520    }
3521    let name = normalize_cpp_whitespace(node_text(node, source));
3522    (!name.is_empty()).then_some(name)
3523}
3524
3525fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3526    match node.kind() {
3527        "identifier" | "field_identifier" | "type_identifier" => {
3528            let name = normalize_cpp_whitespace(node_text(node, source));
3529            (!name.is_empty()).then_some(name)
3530        }
3531        _ => {
3532            let mut cursor = node.walk();
3533            node.named_children(&mut cursor)
3534                .find_map(|child| declarator_name_from_node(child, source))
3535        }
3536    }
3537}
3538
3539fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3540    let mut cursor = node.walk();
3541    node.named_children(&mut cursor).find(|child| {
3542        matches!(
3543            child.kind(),
3544            "class_specifier" | "struct_specifier" | "union_specifier"
3545        )
3546    })
3547}
3548
3549/// Push a container's children as a `Siblings` cursor rather than snapshotting
3550/// them all with one shared scope: children are visited one at a time so a
3551/// `using namespace X;` sibling can affect the scope threaded to the siblings
3552/// that textually follow it (issue #1093).
3553fn push_cpp_container_work<'tree>(
3554    node: Node<'tree>,
3555    scope: ScopeInfo,
3556    stack: &mut Vec<CppWork<'tree>>,
3557) {
3558    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3559}
3560
3561/// Materialize one selected named-child range with a tree-sitter cursor. The
3562/// cursor advances linearly across the parent's concrete children; repeatedly
3563/// asking for `named_child(index)` is quadratic on very wide generated nodes.
3564fn push_cpp_sibling_range<'tree>(
3565    parent: Node<'tree>,
3566    start_index: usize,
3567    end_index: usize,
3568    scope: ScopeInfo,
3569    stack: &mut Vec<CppWork<'tree>>,
3570) {
3571    let mut cursor = parent.walk();
3572    let children = parent
3573        .named_children(&mut cursor)
3574        .skip(start_index)
3575        .take(end_index.saturating_sub(start_index))
3576        .collect::<Vec<_>>()
3577        .into_iter();
3578    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3579}
3580
3581/// Advance a `Siblings` cursor by one child: dispatch the current child under
3582/// the scope accumulated from its *earlier* siblings, then push a
3583/// continuation for the remaining siblings carrying the scope updated for
3584/// *this* child (only `using namespace X;` directives change it). Pushing the
3585/// continuation before the current child's own node work means the current
3586/// child's subtree fully drains (LIFO) before the next sibling is visited,
3587/// preserving left-to-right order.
3588fn advance_cpp_siblings<'tree>(
3589    mut siblings: CppSiblingsWork<'tree>,
3590    source: &str,
3591    stack: &mut Vec<CppWork<'tree>>,
3592) {
3593    let Some(child) = siblings.children.next() else {
3594        return;
3595    };
3596    let current_scope = siblings.scope.clone();
3597    if let Some(namespace) = cpp_using_namespace_target(child, source) {
3598        siblings.scope.visible_using_namespaces.push(namespace);
3599    }
3600    if !siblings.children.as_slice().is_empty() {
3601        stack.push(CppWork::Siblings(siblings));
3602    }
3603    stack.push(CppWork::Node(CppNodeWork {
3604        node: child,
3605        scope: current_scope,
3606    }));
3607}
3608
3609/// The namespace target of a `using namespace X;` directive, or `None` for
3610/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
3611/// kind. Distinguished structurally by the presence of the grammar's literal
3612/// `namespace` keyword token among the node's children -- not by inspecting
3613/// source text -- so it never misreads a member-importing using-declaration
3614/// as a namespace directive.
3615fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3616    if node.kind() != "using_declaration" {
3617        return None;
3618    }
3619    let mut cursor = node.walk();
3620    let is_namespace_directive = node
3621        .children(&mut cursor)
3622        .any(|child| child.kind() == "namespace");
3623    if !is_namespace_directive {
3624        return None;
3625    }
3626    let target = node.named_child(0)?;
3627    // A leading `::` is the explicit-global marker, not part of the namespace
3628    // path (`using namespace ::std::chrono;`). Drop that AST token before
3629    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
3630    // keeps: storing the marker verbatim desynced the legacy package string from
3631    // the FqName bridge, which splits on `::` and drops the empty leading
3632    // component, tripping the package/short boundary assert when a bare-owner
3633    // out-of-line definition borrowed the directive's namespace (#1093 path).
3634    let start = target
3635        .child(0)
3636        .filter(|child| !child.is_named() && child.kind() == "::")
3637        .map_or(target.start_byte(), |marker| marker.end_byte());
3638    let text = normalize_cpp_whitespace(
3639        source
3640            .get(start..target.end_byte())
3641            .expect("using-directive target covers one source range"),
3642    );
3643    (!text.is_empty()).then_some(text)
3644}
3645
3646/// Every `using namespace X;` directive target in a file, in source order, for
3647/// resolution-time consumers that need the file's using-directives without the
3648/// per-position scope threading extraction does. Parses `source` fresh and
3649/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
3650/// keys on the grammar's `namespace` keyword token, not source text), so it
3651/// never misreads a member-importing `using X::Y;` as a namespace directive.
3652///
3653/// This is a whole-file over-approximation of what is in scope at any one point
3654/// (a directive nested inside a `namespace {}` block or a function body is still
3655/// reported), which is exactly what the #1134 identity reconciler wants: extra
3656/// candidate namespaces that no visible class confirms are harmless, and two
3657/// that both confirm are treated as a genuine ambiguity by the reconciler.
3658pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3659    let mut parser = Parser::new();
3660    if parser
3661        .set_language(&tree_sitter_cpp::LANGUAGE.into())
3662        .is_err()
3663    {
3664        return Vec::new();
3665    }
3666    let Some(tree) = parser.parse(source, None) else {
3667        return Vec::new();
3668    };
3669    let mut namespaces = Vec::new();
3670    let mut seen = std::collections::HashSet::new();
3671    let mut stack = vec![tree.root_node()];
3672    while let Some(node) = stack.pop() {
3673        if let Some(namespace) = cpp_using_namespace_target(node, source)
3674            && seen.insert(namespace.clone())
3675        {
3676            namespaces.push(namespace);
3677        }
3678        let mut cursor = node.walk();
3679        stack.extend(node.named_children(&mut cursor));
3680    }
3681    namespaces
3682}
3683
3684pub struct CppVisitor<'a> {
3685    pub file: &'a ProjectFile,
3686    pub source: &'a str,
3687    pub parsed: &'a mut ParsedFile,
3688    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
3689    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
3690    ///
3691    /// C has no nested tag scope: a struct/union/enum tag declared inside
3692    /// another aggregate's member list has the scope of the outer declaration
3693    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
3694    /// therefore declares a file-scope `inner` that a later file-scope
3695    /// `struct inner *p;` legitimately references, where C++ would make the
3696    /// same shape a nested class `outer::inner`. Headers carry no compilation
3697    /// language of their own and keep the conservative C++ interpretation.
3698    pub c_tag_semantics: bool,
3699    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3700    /// Byte regions whose contents were re-owned by a fragmented export-class
3701    /// recovery (#938): the scattered members between the fragmented
3702    /// declaration and its displaced closing brace are indexed as members of
3703    /// the recovered class by the region reparse, so the ordinary sibling walk
3704    /// must not ALSO index them as top-level declarations (that double-indexing
3705    /// made a scattered nested class ambiguous between `Inner` and
3706    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
3707    /// linear scan at visit time is fine.
3708    pub consumed_fragment_regions: Vec<(usize, usize)>,
3709    /// Lexical namespace corrections and brace boundaries for declarations
3710    /// displaced by parse recovery (issues #1537 and #3087). The parsed
3711    /// ancestors can stop short or extend past their true closes; see
3712    /// [`CppVisitor::recovered_namespace_scope`].
3713    pub orphaned_namespaces: OrphanedNamespaceScopeIndex,
3714    /// Owned reparses waiting for the outer work loop. Partitioning another
3715    /// swallowed class in a tail must not grow the Rust call stack.
3716    pub partitioned_regions: Vec<(Tree, std::ops::Range<usize>, ScopeInfo)>,
3717    /// The namespace forward declarations already folded out of each tree this
3718    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
3719    /// Empty until the first question, which the overwhelming majority of files
3720    /// never ask.
3721    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3722    /// Which owners already have field declarations in the parse product, as
3723    /// [`CppVisitor::has_enum_enumerator_units`] needs to know. `None` until
3724    /// the first enum asks, which most files never do (#2786).
3725    pub field_owners: Option<CppFieldOwnerIndex>,
3726    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
3727    /// happen to the declaration set, innermost last. Empty outside a recovery
3728    /// reparse, which is almost always (#2787).
3729    pub recovery_captures: Vec<CppRecoveryCapture>,
3730    /// Object-like field-list macros defined earlier in this source. Their
3731    /// replacements are parsed structurally and materialized under each
3732    /// invoking aggregate; no source-text expansion is used.
3733    pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
3734    /// Names whose active replacement is not a unique structured field list.
3735    /// A later `#undef` resets the ambiguity; another `#define` does not.
3736    pub ambiguous_object_macro_fields: HashSet<String>,
3737}
3738
3739/// One source-order event from an object-like field-list macro environment.
3740///
3741/// The event stream lets an include-closure consumer preserve preprocessor
3742/// invalidation without guessing which parsed header happened to be visited
3743/// first. A conservative consumer may permanently block a name after an
3744/// `undef` when conditional include order is not provable.
3745#[derive(Clone, Debug, PartialEq, Eq)]
3746pub enum ObjectMacroFieldEvent {
3747    Define {
3748        name: String,
3749        replacement: ObjectMacroReplacement,
3750        conditional: bool,
3751    },
3752    Undef {
3753        name: String,
3754        conditional: bool,
3755    },
3756}
3757
3758/// Collect object-like field-list macros from a parsed source in source order.
3759/// Macros with conflicting active replacements are omitted so an include
3760/// closure cannot silently choose one guarded definition over another.
3761pub fn collect_cpp_object_macro_fields<'tree>(
3762    root: Node<'tree>,
3763    source: &str,
3764) -> HashMap<String, ObjectMacroReplacement> {
3765    let mut fields = HashMap::default();
3766    let mut ambiguous = HashSet::default();
3767    for event in collect_cpp_object_macro_field_events(root, source) {
3768        match event {
3769            ObjectMacroFieldEvent::Define {
3770                name,
3771                replacement: value,
3772                conditional,
3773            } => {
3774                if value.is_empty() {
3775                    fields.remove(&name);
3776                    if conditional {
3777                        ambiguous.insert(name);
3778                    } else {
3779                        ambiguous.remove(&name);
3780                    }
3781                } else if ambiguous.contains(&name) {
3782                    // Keep the name blocked until an explicit undef resets it.
3783                } else if let Some(previous) = fields.get(&name) {
3784                    if previous != &value {
3785                        fields.remove(&name);
3786                        ambiguous.insert(name);
3787                    }
3788                } else {
3789                    fields.insert(name, value);
3790                }
3791            }
3792            ObjectMacroFieldEvent::Undef { name, conditional } => {
3793                fields.remove(&name);
3794                if conditional {
3795                    ambiguous.insert(name);
3796                } else {
3797                    ambiguous.remove(&name);
3798                }
3799            }
3800        }
3801    }
3802    fields
3803}
3804
3805/// Collect object-like field-list macro events in source order. Conditional
3806/// branches remain visible in the event stream: callers that cannot prove
3807/// branch selection can invalidate names conservatively instead of inventing
3808/// a replacement from one branch.
3809pub fn collect_cpp_object_macro_field_events<'tree>(
3810    root: Node<'tree>,
3811    source: &str,
3812) -> Vec<ObjectMacroFieldEvent> {
3813    let mut events = Vec::new();
3814    let mut stack = vec![root];
3815    while let Some(node) = stack.pop() {
3816        if node.kind() == "preproc_def"
3817            && let Some(name) = extract_macro_name(node, source)
3818        {
3819            let replacement = object_macro_replacement_of(node, source);
3820            events.push(ObjectMacroFieldEvent::Define {
3821                name,
3822                replacement,
3823                conditional: inside_preprocessor_conditional(node),
3824            });
3825        } else if is_cpp_undef_directive(node, source)
3826            && let Some(argument) = node.child_by_field_name("argument")
3827        {
3828            events.push(ObjectMacroFieldEvent::Undef {
3829                name: node_text(argument, source).trim().to_string(),
3830                conditional: inside_preprocessor_conditional(node),
3831            });
3832        }
3833        let mut cursor = node.walk();
3834        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3835        stack.extend(children.into_iter().rev());
3836    }
3837    events
3838}
3839
3840fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
3841    let mut current = node.parent();
3842    while let Some(parent) = current {
3843        if matches!(
3844            parent.kind(),
3845            "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
3846        ) {
3847            return true;
3848        }
3849        current = parent.parent();
3850    }
3851    false
3852}
3853
3854fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
3855    node.kind() == "preproc_call"
3856        && node
3857            .child_by_field_name("directive")
3858            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
3859}
3860
3861impl<'a> CppVisitor<'a> {
3862    /// Records `code_unit` with the answers the walk carries forward, then adds
3863    /// it to the parse product.
3864    ///
3865    /// Every declaration this walk publishes goes through this family, so the
3866    /// carried-forward answers see each one exactly once: the field ownership
3867    /// index behind [`Self::has_enum_enumerator_units`] (#2786) and the minted
3868    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
3869    fn add_declaration(
3870        &mut self,
3871        code_unit: CodeUnit,
3872        node: Node<'_>,
3873        parent: Option<CodeUnit>,
3874        top_level: Option<CodeUnit>,
3875    ) {
3876        self.note_declaration(&code_unit);
3877        let source = self.source;
3878        self.parsed
3879            .add_code_unit(code_unit, node, source, parent, top_level);
3880    }
3881
3882    /// Range-based form of [`Self::add_declaration`].
3883    fn add_declaration_with_range(
3884        &mut self,
3885        code_unit: CodeUnit,
3886        range: Range,
3887        parent: Option<CodeUnit>,
3888        top_level: Option<CodeUnit>,
3889    ) {
3890        self.note_declaration(&code_unit);
3891        self.parsed
3892            .add_code_unit_with_range(code_unit, range, parent, top_level);
3893    }
3894
3895    /// Deferred-replacement form of [`Self::add_declaration`].
3896    fn replace_declaration_deferred(
3897        &mut self,
3898        code_unit: CodeUnit,
3899        node: Node<'_>,
3900        parent: Option<CodeUnit>,
3901        top_level: Option<CodeUnit>,
3902    ) {
3903        self.note_replaced_declaration(&code_unit);
3904        let source = self.source;
3905        self.parsed
3906            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
3907    }
3908
3909    /// Range-based form of [`Self::replace_declaration_deferred`].
3910    fn replace_declaration_with_range_deferred(
3911        &mut self,
3912        code_unit: CodeUnit,
3913        range: Range,
3914        parent: Option<CodeUnit>,
3915        top_level: Option<CodeUnit>,
3916    ) {
3917        self.note_replaced_declaration(&code_unit);
3918        self.parsed
3919            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
3920    }
3921
3922    /// Notes one declaration about to enter the parse product.
3923    ///
3924    /// A declaration the product already holds is not a creation, so an open
3925    /// recovery capture ignores it -- which is the membership test the set
3926    /// difference it replaces performed. A creation inside a nested recovery
3927    /// belongs to the recoveries around it too, so every open capture takes it.
3928    fn note_declaration(&mut self, code_unit: &CodeUnit) {
3929        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
3930            for capture in &mut self.recovery_captures {
3931                if capture.removed_pre_existing.contains(code_unit) {
3932                    continue;
3933                }
3934                if capture.created_units.insert(code_unit.clone()) {
3935                    capture.created.push(code_unit.clone());
3936                }
3937            }
3938        }
3939        if let Some(field_owners) = self.field_owners.as_mut() {
3940            field_owners.record(code_unit, self.file);
3941        }
3942    }
3943
3944    /// Notes one declaration about to replace an existing one.
3945    ///
3946    /// A deferred replacement of a declaration that already owns children
3947    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
3948    /// a removal is the one thing the field index cannot absorb by addition.
3949    /// Drop it; the next question rebuilds it from the declarations that
3950    /// survive. A replacement of a unit with no children, and a "replacement"
3951    /// of a unit that is not there at all, remove nothing.
3952    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
3953        let removes_children = self.parsed.contains_declaration(code_unit)
3954            && self
3955                .parsed
3956                .children
3957                .get(code_unit)
3958                .is_some_and(|children| !children.is_empty());
3959        if removes_children {
3960            if !self.recovery_captures.is_empty() {
3961                let removed = self.declarations_a_replacement_removes(code_unit);
3962                for capture in &mut self.recovery_captures {
3963                    for unit in &removed {
3964                        // A declaration this capture watched being created is
3965                        // its own; one it did not is a declaration that was
3966                        // already there when the capture opened, so creating it
3967                        // again is a restoration and not a mint.
3968                        if !capture.created_units.contains(unit) {
3969                            capture.removed_pre_existing.insert(unit.clone());
3970                        }
3971                    }
3972                }
3973            }
3974            self.field_owners = None;
3975        }
3976        self.note_declaration(code_unit);
3977    }
3978
3979    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
3980    /// when `code_unit` is replaced: its children, transitively.
3981    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
3982        let mut removed = Vec::new();
3983        let mut seen = HashSet::default();
3984        let mut pending: Vec<CodeUnit> = self
3985            .parsed
3986            .children
3987            .get(code_unit)
3988            .cloned()
3989            .unwrap_or_default();
3990        while let Some(unit) = pending.pop() {
3991            if !seen.insert(unit.clone()) {
3992                continue;
3993            }
3994            if let Some(children) = self.parsed.children.get(&unit) {
3995                pending.extend(children.iter().cloned());
3996            }
3997            removed.push(unit);
3998        }
3999        removed
4000    }
4001
4002    fn visit_function_like_export_class_pair<'tree>(
4003        &mut self,
4004        node: Node<'tree>,
4005        scope: &ScopeInfo,
4006        stack: &mut Vec<CppWork<'tree>>,
4007        ancestry: &ParentIndex<'tree>,
4008    ) -> bool {
4009        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
4010            return false;
4011        };
4012        let member_outcome = self
4013            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
4014        // A malformed class body can escape into several following siblings
4015        // before the next export-macro class head appears. Inspect siblings in
4016        // order and stop at the first envelope that contains such a head. One
4017        // envelope can contain several following classes, all recovered in a
4018        // single bounded traversal.
4019        let mut displaced = node.next_named_sibling();
4020        while let Some(candidate) = displaced {
4021            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
4022                break;
4023            }
4024            displaced = candidate.next_named_sibling();
4025        }
4026        let class_unit = self.visit_named_class_like_shape(
4027            node,
4028            recovered.name,
4029            // The adjacent initializer_list proves the class body envelope,
4030            // but its children are expression-shaped rather than declaration-
4031            // preserving. Index the class identity here; callable definitions
4032            // remain available from their ordinary out-of-line declarations.
4033            None,
4034            true,
4035            Some(recovered.range),
4036            recovered.raw_supertypes,
4037            scope,
4038            stack,
4039            ancestry,
4040        );
4041        self.parsed
4042            .record_materialization(MaterializationRecord::RecoveredDeclaration {
4043                recovery: recovered.range,
4044                unit: class_unit.clone(),
4045            });
4046        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4047            && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4048                tree.root_node(),
4049                class_unit.identifier(),
4050                self.source,
4051            )
4052        {
4053            self.visit_recovered_fragment_constructor(
4054                range,
4055                body,
4056                node,
4057                &class_unit,
4058                scope,
4059                ancestry,
4060            );
4061        }
4062        if let Some(outcome) = member_outcome {
4063            self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4064        }
4065        self.consumed_fragment_regions
4066            .push((node.start_byte(), recovered.range.end_byte));
4067        true
4068    }
4069
4070    fn visit_embedded_function_like_export_classes<'tree>(
4071        &mut self,
4072        node: Node<'tree>,
4073        scope: &ScopeInfo,
4074        stack: &mut Vec<CppWork<'tree>>,
4075        ancestry: &ParentIndex<'tree>,
4076    ) -> bool {
4077        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
4078        let found = !recovered_classes.is_empty();
4079        for recovered in recovered_classes {
4080            let member_outcome = self.reparse_fragmented_export_class_members(
4081                &recovered.fragmented_body,
4082                &recovered.name,
4083            );
4084            let class_unit = self.visit_named_class_like_shape(
4085                node,
4086                recovered.name,
4087                None,
4088                true,
4089                Some(recovered.range),
4090                Some(recovered.raw_supertypes),
4091                scope,
4092                stack,
4093                ancestry,
4094            );
4095            self.parsed
4096                .record_materialization(MaterializationRecord::RecoveredDeclaration {
4097                    recovery: recovered.range,
4098                    unit: class_unit.clone(),
4099                });
4100            if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4101                && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4102                    tree.root_node(),
4103                    class_unit.identifier(),
4104                    self.source,
4105                )
4106            {
4107                self.visit_recovered_fragment_constructor(
4108                    range,
4109                    body,
4110                    node,
4111                    &class_unit,
4112                    scope,
4113                    ancestry,
4114                );
4115            }
4116            if let Some(outcome) = member_outcome {
4117                self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4118            }
4119        }
4120        found
4121    }
4122
4123    /// Walk `node`'s container, answering every ancestor question from
4124    /// `ancestry`.
4125    ///
4126    /// `ancestry` must index the tree `node` belongs to. The caller owns it
4127    /// because one tree can be walked more than once -- a header's C and C++
4128    /// readings are the same tree under different tag semantics -- and the
4129    /// parent relation is a property of the tree, not of the reading.
4130    #[allow(clippy::too_many_arguments)]
4131    pub fn visit_container<'tree>(
4132        &mut self,
4133        node: Node<'tree>,
4134        ancestry: &ParentIndex<'tree>,
4135        package_name: &str,
4136        module: Option<CodeUnit>,
4137        class_unit: Option<CodeUnit>,
4138        template_signature: Option<String>,
4139        visible_using_namespaces: Vec<String>,
4140    ) {
4141        let scope = ScopeInfo {
4142            package_name: package_name.to_string(),
4143            module,
4144            class_unit,
4145            template_signature,
4146            template_metadata: None,
4147            declarations_are_fields: false,
4148            recovered_specialization_member_scope: false,
4149            visible_using_namespaces,
4150        };
4151        // The work loop dispatches a container's children, never the container
4152        // itself, so an `ERROR` container that is the seed does not reach the
4153        // ERROR arm of `visit_node`. A header whose first collapsed aggregate
4154        // breaks the parse leaves the whole translation unit as that seed
4155        // (libuv's `include/uv.h`, issue #2985), which is exactly the shape the
4156        // macro-error-class recovery reads.
4157        if node.is_error() {
4158            self.visit_object_macro_error_classes(node, &scope);
4159        }
4160        self.run_container_work(node, scope, ancestry);
4161        while let Some((tree, range, scope)) = self.partitioned_regions.pop() {
4162            let root = tree.root_node();
4163            let container = root
4164                .descendant_for_byte_range(range.start, range.end)
4165                .expect("the queued container belongs to this tree");
4166            assert_eq!(container.byte_range(), range);
4167            self.run_container_work(container, scope, &ParentIndex::new(root));
4168        }
4169    }
4170
4171    /// Whether a work node lies entirely inside a byte region consumed by a
4172    /// fragmented export-class recovery (#938); such nodes were already indexed
4173    /// as members of the recovered class by the region reparse.
4174    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
4175        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
4176    }
4177
4178    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
4179    /// candidate a recovery is about to reparse but has not yet turned into a
4180    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
4181    /// prior structured recovery.
4182    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
4183        self.consumed_fragment_regions
4184            .iter()
4185            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
4186    }
4187
4188    /// Drive the container work loop from an explicit seed scope to completion. The
4189    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
4190    /// stays alive for the whole traversal.
4191    ///
4192    /// Every ancestor question this walk asks is answered from `ancestry`, which
4193    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
4194    /// node's position in the tree, which made a generated header with thousands
4195    /// of top-level declarations quadratic (#2361). The index is the caller's
4196    /// because it outlives any one walk: the file's tree is walked twice when a
4197    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
4198    /// builds its own index for its own tree.
4199    fn run_container_work<'tree>(
4200        &mut self,
4201        node: Node<'tree>,
4202        scope: ScopeInfo,
4203        ancestry: &ParentIndex<'tree>,
4204    ) {
4205        self.drain_cpp_work(
4206            vec![CppWork::Container(CppContainer { node, scope })],
4207            ancestry,
4208        );
4209    }
4210
4211    /// The work loop itself, from whatever seed the caller built.
4212    ///
4213    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
4214    /// that must walk only part of a reparsed tree seeds it with the sibling
4215    /// range it may walk instead, which keeps the `using namespace X;` scope
4216    /// accumulation `advance_cpp_siblings` performs.
4217    fn drain_cpp_work<'tree>(
4218        &mut self,
4219        mut stack: Vec<CppWork<'tree>>,
4220        ancestry: &ParentIndex<'tree>,
4221    ) {
4222        while let Some(work) = stack.pop() {
4223            match work {
4224                CppWork::Container(container) => {
4225                    push_cpp_container_work(container.node, container.scope, &mut stack);
4226                }
4227                CppWork::Siblings(siblings) => {
4228                    advance_cpp_siblings(siblings, self.source, &mut stack);
4229                }
4230                CppWork::Node(work) => {
4231                    if self.node_is_inside_consumed_fragment(work.node) {
4232                        continue;
4233                    }
4234                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
4235                }
4236            }
4237        }
4238    }
4239
4240    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
4241    /// it only when the entire region is member-shaped. This validation must happen
4242    /// before registering the recovered class because a rejected speculative range
4243    /// must not leak into the ordinary recovery path.
4244    fn reparse_fragmented_export_class_members(
4245        &self,
4246        fragmented: &FragmentedExportBody,
4247        class_name: &str,
4248    ) -> Option<FragmentedExportMembers> {
4249        if fragmented.reparse_start >= fragmented.reparse_end {
4250            return None;
4251        }
4252        let tree = cpp_reparse_fragmented_class_body(
4253            self.source,
4254            fragmented.reparse_start,
4255            fragmented.reparse_end,
4256        )?;
4257        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
4258            return Some(FragmentedExportMembers::Complete(tree));
4259        }
4260        let has_conditional_constructor = {
4261            let root = tree.root_node();
4262            let mut cursor = root.walk();
4263            root.named_children(&mut cursor).any(|child| {
4264                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
4265            })
4266        };
4267        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
4268    }
4269
4270    /// Index an already validated fragmented body as members of `class_unit`. The
4271    /// region reparse keeps each member's exact original byte and line positions.
4272    fn visit_fragmented_export_class_members(
4273        &mut self,
4274        outcome: FragmentedExportMembers,
4275        class_unit: CodeUnit,
4276        scope: &ScopeInfo,
4277    ) -> bool {
4278        let (tree, complete) = match outcome {
4279            FragmentedExportMembers::Complete(tree) => (tree, true),
4280            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
4281        };
4282        let root = tree.root_node();
4283        let class_name = class_unit.identifier().to_string();
4284        let member_scope = ScopeInfo {
4285            // A recovered export-macro class may borrow its namespace from an
4286            // earlier forward declaration even when the malformed node itself
4287            // sits at file scope. Use the recovered class identity as the
4288            // authoritative package for reparsed members as well.
4289            package_name: class_unit.package_name().to_string(),
4290            module: scope.module.clone(),
4291            class_unit: Some(class_unit),
4292            template_signature: scope.template_signature.clone(),
4293            template_metadata: None,
4294            declarations_are_fields: true,
4295            recovered_specialization_member_scope: false,
4296            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4297        };
4298        if !complete {
4299            // A conditional beginning immediately after an access label can
4300            // fragment one constructor declaration while leaving the rest of
4301            // the class body as unsafe statement soup. Recover only that
4302            // structurally proven constructor and leave the outer-tree
4303            // siblings unconsumed for their ordinary walk.
4304            let mut cursor = root.walk();
4305            let constructors = root
4306                .named_children(&mut cursor)
4307                .filter_map(|child| {
4308                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
4309                })
4310                .collect::<Vec<_>>();
4311            // The reparsed region is its own tree, so this drain walks it with
4312            // its own parent index.
4313            let reparsed_ancestry = ParentIndex::new(root);
4314            for constructor in constructors {
4315                let mut stack = Vec::new();
4316                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
4317                while let Some(work) = stack.pop() {
4318                    match work {
4319                        CppWork::Container(container) => {
4320                            push_cpp_container_work(container.node, container.scope, &mut stack);
4321                        }
4322                        CppWork::Siblings(siblings) => {
4323                            advance_cpp_siblings(siblings, self.source, &mut stack);
4324                        }
4325                        CppWork::Node(work) => {
4326                            self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
4327                        }
4328                    }
4329                }
4330            }
4331            return false;
4332        }
4333        // The reparsed region is its own tree, so this walk indexes it itself.
4334        self.run_container_work(root, member_scope, &ParentIndex::new(root));
4335        true
4336    }
4337
4338    fn visit_recovered_fragment_constructor<'tree>(
4339        &mut self,
4340        range: std::ops::Range<usize>,
4341        constructor_body: Node<'tree>,
4342        class_declaration: Node<'tree>,
4343        class_unit: &CodeUnit,
4344        scope: &ScopeInfo,
4345        ancestry: &ParentIndex<'tree>,
4346    ) {
4347        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
4348            return;
4349        };
4350        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
4351            tree.root_node(),
4352            range.start,
4353            class_unit.identifier(),
4354            self.source,
4355        ) else {
4356            return;
4357        };
4358        let member_scope = ScopeInfo {
4359            package_name: class_unit.package_name().to_string(),
4360            module: scope.module.clone(),
4361            class_unit: Some(class_unit.clone()),
4362            template_signature: scope.template_signature.clone(),
4363            template_metadata: None,
4364            declarations_are_fields: true,
4365            recovered_specialization_member_scope: false,
4366            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4367        };
4368        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
4369        else {
4370            return;
4371        };
4372        debug_assert_eq!(function.name, class_unit.identifier());
4373        let code_unit = function.code_unit(self.file.clone());
4374        self.add_declaration_with_range(
4375            code_unit.clone(),
4376            Range {
4377                start_byte: function_declarator.start_byte(),
4378                end_byte: constructor_body.end_byte(),
4379                start_line: function_declarator.start_position().row + 1,
4380                end_line: constructor_body.end_position().row + 1,
4381            },
4382            None,
4383            None,
4384        );
4385        self.parsed.add_signature_with_metadata(
4386            code_unit.clone(),
4387            cpp_signature_metadata(
4388                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
4389                function_declarator,
4390                self.source,
4391                ancestry,
4392            )
4393            .with_declaration_only(false)
4394            .with_callable_linkage(cpp_callable_linkage(
4395                class_declaration,
4396                self.source,
4397                ancestry,
4398            )),
4399        );
4400        self.parsed.add_child(class_unit.clone(), code_unit);
4401    }
4402
4403    fn visit_recovered_fragment_prefix_members<'tree>(
4404        &mut self,
4405        root: Node<'tree>,
4406        constructor_start: usize,
4407        class_unit: &CodeUnit,
4408        scope: &ScopeInfo,
4409        ancestry: &ParentIndex<'tree>,
4410    ) {
4411        let member_scope = ScopeInfo {
4412            package_name: class_unit.package_name().to_string(),
4413            module: scope.module.clone(),
4414            class_unit: Some(class_unit.clone()),
4415            template_signature: scope.template_signature.clone(),
4416            template_metadata: None,
4417            declarations_are_fields: true,
4418            recovered_specialization_member_scope: false,
4419            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4420        };
4421        let mut stack = vec![root];
4422        while let Some(current) = stack.pop() {
4423            if current.kind() == "comment" || current.start_byte() >= constructor_start {
4424                continue;
4425            }
4426            if current.end_byte() <= constructor_start
4427                && current.kind() != "translation_unit"
4428                && current.kind() != "labeled_statement"
4429                && current.kind() != "ERROR"
4430            {
4431                let mut work_stack = Vec::new();
4432                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
4433                while let Some(work) = work_stack.pop() {
4434                    match work {
4435                        CppWork::Container(container) => {
4436                            push_cpp_container_work(
4437                                container.node,
4438                                container.scope,
4439                                &mut work_stack,
4440                            );
4441                        }
4442                        CppWork::Siblings(siblings) => {
4443                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
4444                        }
4445                        CppWork::Node(work) => {
4446                            self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
4447                        }
4448                    }
4449                }
4450                continue;
4451            }
4452            if matches!(
4453                current.kind(),
4454                "translation_unit" | "labeled_statement" | "ERROR"
4455            ) {
4456                let mut cursor = current.walk();
4457                stack.extend(current.named_children(&mut cursor));
4458            }
4459        }
4460    }
4461
4462    fn visit_node<'tree>(
4463        &mut self,
4464        node: Node<'tree>,
4465        scope: &ScopeInfo,
4466        stack: &mut Vec<CppWork<'tree>>,
4467        ancestry: &ParentIndex<'tree>,
4468    ) {
4469        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
4470            self.visit_node(node, &recovered_scope, stack, ancestry);
4471            return;
4472        }
4473        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
4474            self.visit_node(node, &recovered_scope, stack, ancestry);
4475            return;
4476        }
4477        // Fragmented-class recovery below may consume a malformed function
4478        // envelope before the ordinary kind dispatch runs. Recover any
4479        // export-macro class embedded in that envelope first; the strict class
4480        // head/base/body predicate is independent of which later recovery owns
4481        // the surrounding parser fragment.
4482        if node.kind() == "function_definition" && node.has_error() {
4483            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4484        }
4485        if let Some(FragmentedClassRecovery {
4486            declaration_node: class_node,
4487            name,
4488            raw_supertypes,
4489            body: fragmented,
4490        }) = fragmented_class_body(node, self.source)
4491        {
4492            let displaced_namespace_items =
4493                displaced_fragment_namespace_geometry(node, self.source)
4494                    .map(|boundary| boundary.namespace_items)
4495                    .unwrap_or_default();
4496            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
4497            let mut class_stack = Vec::new();
4498            // When the full body cannot be safely reparsed, the original class
4499            // node still proves ownership for its parser-visible prefix.
4500            let parser_visible_body =
4501                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
4502                    .then(|| cpp_body_node(class_node))
4503                    .flatten();
4504            let class_unit = self.visit_named_class_like_shape(
4505                class_node,
4506                name,
4507                parser_visible_body,
4508                true,
4509                Some(fragmented.class_range),
4510                Some(raw_supertypes),
4511                scope,
4512                &mut class_stack,
4513                ancestry,
4514            );
4515            let member_scope = ScopeInfo {
4516                package_name: class_unit.package_name().to_string(),
4517                module: scope.module.clone(),
4518                class_unit: Some(class_unit.clone()),
4519                template_signature: scope.template_signature.clone(),
4520                template_metadata: None,
4521                declarations_are_fields: true,
4522                recovered_specialization_member_scope: false,
4523                visible_using_namespaces: scope.visible_using_namespaces.clone(),
4524            };
4525            let complete = outcome.is_some_and(|outcome| {
4526                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
4527            });
4528            if complete {
4529                self.consumed_fragment_regions
4530                    .push((node.start_byte(), fragmented.class_range.end_byte));
4531            } else {
4532                // A macro-constrained member can make the full body reparse
4533                // unsafe while tree-sitter still exposes later class members
4534                // as bounded siblings up to the displaced `}`/`;`. Keep the
4535                // structurally proven class/base declaration and re-own those
4536                // sibling nodes under it. They retain their original parser
4537                // nodes and exact ranges; the close boundary comes solely from
4538                // `fragmented_class_body`.
4539                // Template wrappers put the escaped members beside the
4540                // template rather than beside its malformed declaration.
4541                for candidate in cpp_following_named_siblings(node, self.source) {
4542                    if candidate.start_byte() >= fragmented.reparse_end {
4543                        break;
4544                    }
4545                    if cpp_fragment_sibling_is_class_member(
4546                        candidate,
4547                        fragmented.reparse_end,
4548                        self.source,
4549                    ) {
4550                        self.recovered_class_sibling_scopes
4551                            .insert(candidate.id(), member_scope.clone());
4552                    }
4553                }
4554            }
4555            for item in displaced_namespace_items {
4556                self.recovered_class_sibling_scopes
4557                    .insert(item.id(), scope.clone());
4558            }
4559            stack.extend(class_stack);
4560            return;
4561        }
4562        if self.visit_folded_aggregate(node, scope) {
4563            return;
4564        }
4565        match node.kind() {
4566            "template_declaration" => {
4567                if let Some(recovered) =
4568                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
4569                {
4570                    let mut template_scope = scope.clone();
4571                    template_scope.template_signature =
4572                        cpp_template_signature(node, recovered.declaration_node, self.source);
4573                    template_scope.template_metadata =
4574                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
4575                    let raw_supertypes =
4576                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
4577                    let mut class_stack = Vec::new();
4578                    let class_unit = self.visit_named_class_like_shape(
4579                        recovered.class_node,
4580                        recovered.name,
4581                        Some(recovered.body),
4582                        true,
4583                        Some(recovered.range),
4584                        raw_supertypes,
4585                        &template_scope,
4586                        &mut class_stack,
4587                        ancestry,
4588                    );
4589                    self.parsed.record_materialization(
4590                        MaterializationRecord::RecoveredDeclaration {
4591                            recovery: recovered.range,
4592                            unit: class_unit.clone(),
4593                        },
4594                    );
4595                    let member_scope = ScopeInfo {
4596                        package_name: template_scope.package_name.clone(),
4597                        module: template_scope.module.clone(),
4598                        class_unit: Some(class_unit.clone()),
4599                        template_signature: template_scope.template_signature.clone(),
4600                        template_metadata: None,
4601                        declarations_are_fields: true,
4602                        recovered_specialization_member_scope: recovered
4603                            .class_node
4604                            .child_by_field_name("name")
4605                            .is_some_and(|name| name.kind() == "template_type"),
4606                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
4607                    };
4608                    for tail_member in recovered.tail_members.into_iter().rev() {
4609                        stack.push(CppWork::Node(CppNodeWork {
4610                            node: tail_member,
4611                            scope: member_scope.clone(),
4612                        }));
4613                    }
4614                    stack.extend(class_stack);
4615                    for sibling in recovered.member_siblings {
4616                        self.recovered_class_sibling_scopes
4617                            .insert(sibling.id(), member_scope.clone());
4618                    }
4619                    return;
4620                }
4621                for index in (0..node.named_child_count()).rev() {
4622                    let Some(child) = node.named_child(index) else {
4623                        continue;
4624                    };
4625                    if matches!(
4626                        child.kind(),
4627                        "class_specifier"
4628                            | "struct_specifier"
4629                            | "union_specifier"
4630                            | "enum_specifier"
4631                            | "function_definition"
4632                            | "declaration"
4633                            | "field_declaration"
4634                            | "alias_declaration"
4635                            | "namespace_definition"
4636                    ) {
4637                        let mut template_scope = scope.clone();
4638                        template_scope.template_signature =
4639                            cpp_template_signature(node, child, self.source);
4640                        template_scope.template_metadata =
4641                            cpp_template_metadata(node, child, self.source, ancestry);
4642                        if let Some(recovered) = recover_fragmented_partial_specialization(
4643                            node,
4644                            child,
4645                            self.source,
4646                            ancestry,
4647                        ) {
4648                            let code_unit = self.visit_named_class_like_shape(
4649                                recovered.declaration_node,
4650                                recovered.name,
4651                                None,
4652                                true,
4653                                Some(recovered.range),
4654                                None,
4655                                &template_scope,
4656                                stack,
4657                                ancestry,
4658                            );
4659                            self.parsed.record_materialization(
4660                                MaterializationRecord::RecoveredDeclaration {
4661                                    recovery: recovered.range,
4662                                    unit: code_unit.clone(),
4663                                },
4664                            );
4665                            let mut member_scope = template_scope.clone();
4666                            member_scope.class_unit = Some(code_unit);
4667                            member_scope.declarations_are_fields = true;
4668                            member_scope.recovered_specialization_member_scope = true;
4669                            for prefix_member in recovered.prefix_members.into_iter().rev() {
4670                                stack.push(CppWork::Node(CppNodeWork {
4671                                    node: prefix_member,
4672                                    scope: member_scope.clone(),
4673                                }));
4674                            }
4675                            for sibling in recovered.member_siblings {
4676                                self.recovered_class_sibling_scopes
4677                                    .insert(sibling.id(), member_scope.clone());
4678                            }
4679                            for following in recovered.following_declarations.into_iter().rev() {
4680                                stack.push(CppWork::Node(CppNodeWork {
4681                                    node: following,
4682                                    scope: scope.clone(),
4683                                }));
4684                            }
4685                            return;
4686                        }
4687                        stack.push(CppWork::Node(CppNodeWork {
4688                            node: child,
4689                            scope: template_scope,
4690                        }));
4691                    }
4692                }
4693            }
4694            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4695            "linkage_specification" => {
4696                if let Some(body) = cpp_body_node(node) {
4697                    stack.push(CppWork::Container(CppContainer {
4698                        node: body,
4699                        scope: scope.clone(),
4700                    }));
4701                } else {
4702                    stack.push(CppWork::Container(CppContainer {
4703                        node,
4704                        scope: scope.clone(),
4705                    }));
4706                }
4707            }
4708            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4709                self.visit_class_like(node, scope, stack, ancestry)
4710            }
4711            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4712            // A bare namespace-begin sentinel can make tree-sitter promote the
4713            // wrapped declaration to an ERROR node instead of the usual bogus
4714            // function_definition envelope. Keep the recovery entry point on
4715            // the same structured path for both shapes; ordinary ERROR nodes
4716            // retain their declaration-preserving wrapper traversal when the
4717            // sentinel predicate does not match.
4718            "ERROR" => {
4719                self.visit_object_macro_error_classes(node, scope);
4720                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4721                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4722                    if self.visit_collapsed_macro_declaration_run(node, scope) {
4723                        return;
4724                    }
4725                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4726                        return;
4727                    }
4728                    self.visit_macro_swallowed_function_declarations(node, scope);
4729                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
4730                    self.visit_stranded_class_members(node, scope, ancestry);
4731                    stack.push(CppWork::Container(CppContainer {
4732                        node,
4733                        scope: scope.clone(),
4734                    }));
4735                }
4736            }
4737            "declaration" => {
4738                if node.has_error() {
4739                    // Object-like macro lines before a function-like
4740                    // export-macro class head keep the head in a `declaration`
4741                    // rather than in an `ERROR` (#2924). It is the same head
4742                    // and the same following-sibling body, so it takes the same
4743                    // recovery; without it the head's macro invocation indexes
4744                    // as a field named after the macro.
4745                    if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4746                        return;
4747                    }
4748                    self.visit_prototype_macro_declarations(node, scope);
4749                    if self.node_is_inside_consumed_fragment(node) {
4750                        // The whole declaration was a recovered K&R
4751                        // prototype macro invocation (issue #2932); the
4752                        // reparse above already indexed its Function.
4753                        // Falling through to the ordinary declaration
4754                        // visitor would additionally mint the swapped-field
4755                        // wreckage this recovery exists to replace.
4756                        return;
4757                    }
4758                }
4759                if scope.class_unit.is_some()
4760                    && scope.declarations_are_fields
4761                    && scope.recovered_specialization_member_scope
4762                    && let Some(alias_name) =
4763                        recovered_using_declaration_alias_name(node, self.source)
4764                {
4765                    self.add_type_aliases(node, scope, vec![alias_name], ancestry);
4766                } else {
4767                    self.visit_declaration(
4768                        node,
4769                        scope,
4770                        scope.declarations_are_fields,
4771                        stack,
4772                        ancestry,
4773                    )
4774                }
4775            }
4776            // A K&R prototype macro invocation with a pointer return type and a
4777            // simple argument list parses with no ERROR node at all: tree-sitter
4778            // reads `T *name _(( args ));` as a multiplication of a type by a
4779            // call (`T * name_(...)`), wrapped in an `expression_statement`
4780            // that `has_error()` only because of a `MISSING "::"` inside it
4781            // (issue #2932). Nothing else mints a declaration from an
4782            // `expression_statement` at declaration scope today, so there is
4783            // no existing behavior to preserve here if this node is not the
4784            // K&R shape; the admission gate inside
4785            // `visit_prototype_macro_declarations` (exactly one clean function
4786            // prototype spanning the recovered run) is what keeps this arm
4787            // safe on an ordinary errorful expression statement that happens
4788            // to contain nested parentheses.
4789            "expression_statement" => {
4790                if node.has_error() {
4791                    self.visit_prototype_macro_declarations(node, scope);
4792                }
4793            }
4794            "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4795            "preproc_call" => self.visit_preproc_call(node, scope),
4796            "type_definition" | "alias_declaration" => {
4797                self.visit_type_declaration(node, scope, stack, ancestry)
4798            }
4799            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4800            // `#include` is collected by `collect_cpp_includes` before the
4801            // walk, so a directive the container walk never reaches -- inside
4802            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
4803            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
4804            // include claim.
4805            "preproc_include" => {}
4806            kind if preserves_declaration_scope_through_wrapper(
4807                kind,
4808                scope.class_unit.is_some(),
4809            ) =>
4810            {
4811                // A preprocessor conditional gates every declaration inside it
4812                // on a configuration this analyzer never evaluates; record the
4813                // interval so declaration state can say so (issue #1476). The
4814                // else/elif branches are children of the `preproc_if` node, so
4815                // recording the openers covers every branch.
4816                if kind == "labeled_statement" {
4817                    self.visit_access_label_constructor(node, scope);
4818                }
4819                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4820                    let mut range = cpp_declaration_range(node);
4821                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4822                        range.end_byte = boundary.end_byte;
4823                        range.end_line = boundary.end_line;
4824                    }
4825                    self.parsed.record_materialization(
4826                        MaterializationRecord::ConfigurationConditional { range },
4827                    );
4828                    if node.has_error() {
4829                        // A malformed export-macro class can close the namespace
4830                        // node early while the enclosing include guard still owns
4831                        // the remaining class-head/body pairs. The ordinary walk
4832                        // cannot carry the lost namespace through those promoted
4833                        // siblings. Scan only structured ERROR nodes in this
4834                        // already-malformed conditional; the pair recovery's
4835                        // exact class/macro/body predicate remains the admission
4836                        // gate, and its namespace lifting restores the owner.
4837                        let mut candidates = vec![node];
4838                        while let Some(candidate) = candidates.pop() {
4839                            // An `ERROR` still inside a namespace body is one
4840                            // the ordinary walk reaches with that namespace in
4841                            // scope. Claiming it here registers the recovered
4842                            // class at file scope and the consumed region then
4843                            // suppresses the walk that would have named it
4844                            // correctly -- which is why Botan's `DL_Group` was
4845                            // `DL_Group` and not `Botan.DL_Group` in the real
4846                            // header, where an include guard wraps the
4847                            // namespace, and was right in a fixture without one
4848                            // (#2552).
4849                            if candidate.kind() == "ERROR"
4850                                && !cpp_is_inside_namespace_body(candidate, ancestry)
4851                                && self.visit_function_like_export_class_pair(
4852                                    candidate, scope, stack, ancestry,
4853                                )
4854                            {
4855                                continue;
4856                            }
4857                            for index in (0..candidate.named_child_count()).rev() {
4858                                candidates.push(
4859                                    candidate
4860                                        .named_child(index)
4861                                        .expect("index below the node's own named child count"),
4862                                );
4863                            }
4864                        }
4865                    }
4866                }
4867                stack.push(CppWork::Container(CppContainer {
4868                    node,
4869                    scope: scope.clone(),
4870                }))
4871            }
4872            // A macro invocation the parser could not group leaves its head in
4873            // whatever slot was free. The `function_definition` and `ERROR`
4874            // arms above try the same recovery on the envelopes they get; a
4875            // flattened run leaves a bare `identifier`/`type_identifier`, and a
4876            // swallowed tail can land in a `parameter_declaration` (#3094).
4877            _ => {
4878                self.visit_collapsed_macro_declaration_run(node, scope);
4879            }
4880        }
4881    }
4882
4883    fn visit_macro_swallowed_function_declarations<'tree>(
4884        &mut self,
4885        envelope: Node<'tree>,
4886        scope: &ScopeInfo,
4887    ) {
4888        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
4889            || envelope.kind() == "ERROR"
4890                && envelope
4891                    .parent()
4892                    .is_some_and(|parent| parent.kind() == "ERROR")
4893        {
4894            return;
4895        }
4896        let mut stack = (0..envelope.named_child_count())
4897            .filter_map(|index| envelope.named_child(index))
4898            .collect::<Vec<_>>();
4899        while let Some(node) = stack.pop() {
4900            if node.kind() == "function_declarator" {
4901                self.visit_error_swallowed_function_declaration(node, scope);
4902            }
4903            for child in named_children_iter(node) {
4904                stack.push(child);
4905            }
4906        }
4907    }
4908
4909    /// Index the declarations an attribute-like macro invocation swallowed into
4910    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
4911    /// the shape and why the parser produces it.
4912    fn visit_macro_wrapped_declarations<'tree>(
4913        &mut self,
4914        envelope: Node<'tree>,
4915        scope: &ScopeInfo,
4916        ancestry: &ParentIndex<'tree>,
4917    ) {
4918        let recovered = macro_wrapped_declarations(envelope, self.source);
4919        if recovered.is_empty() {
4920            return;
4921        }
4922        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
4923        self.record_recovered_declarations(recovery, |visitor| {
4924            for declaration in recovered {
4925                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
4926            }
4927        });
4928    }
4929
4930    /// Index the declarations a macro invocation collapsed. See
4931    /// [`collapsed_macro_declaration_run`] for the shape and why the parser
4932    /// produces it. Returns whether it claimed the region beginning at
4933    /// `envelope`.
4934    ///
4935    /// The parser's own nodes cannot be read the way the swallowed tail of the
4936    /// one-line shape can. Of the 214 declarations whisper's `llama.h` hides in
4937    /// its envelope, 57 are shredded to bare identifier and punctuation tokens
4938    /// with no declarator left at all, and the declarators that do survive on
4939    /// the envelope's declarator spine pair one declaration's name with the
4940    /// *next* declaration's parameter list. Reading those would be a guess.
4941    ///
4942    /// The bytes are still ordinary declarations, though, and the collapse is
4943    /// the parser carrying the failure forward from one macro invocation. So
4944    /// reparse the region, walk the items the parser makes of it, and when one
4945    /// item is itself a collapsed run, recover that invocation from its own
4946    /// bytes and resume the scan just past it. Each pass starts later than the
4947    /// last, so the scan is a loop over the invocations that collapse, not over
4948    /// the declarations: `llama.h` needs three passes for 214 declarations.
4949    ///
4950    /// The region can reach past `envelope` into the siblings the parser handed
4951    /// the rest of one invocation, so it is recorded as consumed: those
4952    /// siblings are this recovery's, and the ordinary walk must not read them
4953    /// again. Recording it after the scan, not before, keeps the scan's own
4954    /// reparsed nodes -- which carry their original byte offsets -- visible to
4955    /// the walk it drives.
4956    fn visit_collapsed_macro_declaration_run(
4957        &mut self,
4958        envelope: Node<'_>,
4959        scope: &ScopeInfo,
4960    ) -> bool {
4961        let Some(run) = collapsed_macro_declaration_run(envelope, self.source) else {
4962            return false;
4963        };
4964        let start = envelope.start_byte();
4965        let end = run.region_end;
4966        let recovery = cpp_recovery_window(self.source, start, end);
4967        self.record_recovered_declarations(recovery, |visitor| {
4968            let mut position = start;
4969            while position < end {
4970                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
4971                    return;
4972                };
4973                let root = tree.root_node();
4974                // A region reparse is its own tree and needs its own parent
4975                // index; the caller's answers nothing about these nodes.
4976                let ancestry = ParentIndex::new(root);
4977                let mut cursor = root.walk();
4978                let collapsed =
4979                    root.named_children(&mut cursor)
4980                        .enumerate()
4981                        .find_map(|(index, item)| {
4982                            collapsed_macro_declaration_run(item, visitor.source)
4983                                .map(|run| (index, item, run))
4984                        });
4985                // Walk only the items before the collapsed one. It swallowed
4986                // the rest of the region, so handing it to the ordinary walk
4987                // would re-enter this recovery on a region that starts where
4988                // this one did.
4989                let mut stack = Vec::new();
4990                push_cpp_sibling_range(
4991                    root,
4992                    0,
4993                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
4994                    scope.clone(),
4995                    &mut stack,
4996                );
4997                visitor.drain_cpp_work(stack, &ancestry);
4998                let Some((_, item, run)) = collapsed else {
4999                    return;
5000                };
5001                // On its own bytes the invocation is the declaration-scope
5002                // shape `macro_wrapped_declarations` reads, so its wrapped
5003                // declaration needs no reader of its own here.
5004                if let Some(head) =
5005                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
5006                {
5007                    let head_root = head.root_node();
5008                    visitor.run_container_work(
5009                        head_root,
5010                        scope.clone(),
5011                        &ParentIndex::new(head_root),
5012                    );
5013                }
5014                assert!(
5015                    run.invocation_end > position,
5016                    "a collapsed run at {position} must end after the byte the scan resumed \
5017                     from, but ended at {}",
5018                    run.invocation_end
5019                );
5020                position = run.invocation_end;
5021            }
5022        });
5023        self.consumed_fragment_regions.push((start, end));
5024        true
5025    }
5026
5027    /// Index the members a string-argument attribute macro stranded in an
5028    /// `ERROR` inside a class body.
5029    ///
5030    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
5031    /// grouping of the attributed member and of every member written after it
5032    /// until it recovers. The members are still whole `function_declarator`
5033    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
5034    /// export-macro class such as Botan's `DL_Group` was indexed with no
5035    /// members at all (#2552).
5036    fn visit_stranded_class_members<'tree>(
5037        &mut self,
5038        node: Node<'tree>,
5039        scope: &ScopeInfo,
5040        ancestry: &ParentIndex<'tree>,
5041    ) {
5042        if scope.class_unit.is_none() || !scope.declarations_are_fields {
5043            return;
5044        }
5045        for member in stranded_declaration_run(node, self.source).declarations {
5046            self.add_macro_wrapped_declaration(member, scope, ancestry);
5047        }
5048    }
5049
5050    /// Index the constructor an access label swallowed in a reparsed class
5051    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
5052    ///
5053    /// The declarator is recovered by reparsing from that call to the end of
5054    /// the label's own statement, which is the same offset-preserving region
5055    /// reparse every other recovery here uses, so the recovered nodes carry
5056    /// their true source positions.
5057    fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5058        let Some(class_unit) = scope.class_unit.clone() else {
5059            return;
5060        };
5061        if !scope.declarations_are_fields {
5062            return;
5063        }
5064        let class_name = class_unit.identifier().to_string();
5065        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
5066        else {
5067            return;
5068        };
5069        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
5070            return;
5071        };
5072        let root = tree.root_node();
5073        let Some(declarator) =
5074            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
5075        else {
5076            return;
5077        };
5078        let reparsed_ancestry = ParentIndex::new(root);
5079        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
5080        let range = cpp_declaration_range(definition.unwrap_or(declarator));
5081        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
5082        self.record_recovered_declarations(recovery, |visitor| {
5083            visitor.add_macro_wrapped_declaration(
5084                MacroWrappedDeclaration {
5085                    declarator,
5086                    range,
5087                    is_static: false,
5088                },
5089                scope,
5090                &reparsed_ancestry,
5091            );
5092        });
5093    }
5094
5095    fn add_macro_wrapped_declaration<'tree>(
5096        &mut self,
5097        declaration: MacroWrappedDeclaration<'tree>,
5098        scope: &ScopeInfo,
5099        ancestry: &ParentIndex<'tree>,
5100    ) {
5101        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
5102        else {
5103            return;
5104        };
5105        let code_unit =
5106            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
5107        if self.parsed.contains_declaration(&code_unit) {
5108            self.parsed
5109                .record_navigation_range(code_unit, declaration.range);
5110            return;
5111        }
5112        self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
5113        let signature = normalize_cpp_whitespace(
5114            self.source
5115                .get(declaration.range.start_byte..declaration.range.end_byte)
5116                .expect("a recovered declaration range covers one source range"),
5117        );
5118        let linkage = if declaration.is_static {
5119            CallableLinkage::Internal
5120        } else {
5121            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
5122        };
5123        // Whether this is a definition is a property of the recovered node, not
5124        // of the caller: a declarator that a `function_definition` gives a body
5125        // is a definition wherever the recovery found it.
5126        let declaration_only =
5127            cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
5128        self.parsed.add_signature_with_metadata(
5129            code_unit.clone(),
5130            cpp_signature_metadata(signature, declaration.declarator, self.source, ancestry)
5131                .with_declaration_only(declaration_only)
5132                .with_callable_linkage(linkage),
5133        );
5134        if let Some(parent) = &scope.class_unit {
5135            self.parsed.add_child(parent.clone(), code_unit);
5136        } else if let Some(module) = &scope.module {
5137            self.parsed.add_child(module.clone(), code_unit);
5138        }
5139    }
5140
5141    /// Index only anonymous aggregate declarations from an ordinary C
5142    /// function body. Function bodies are otherwise outside the declaration
5143    /// walk, but their anonymous aggregate owners must exist so structured
5144    /// local binding inference can name their fields (#2994).
5145    fn visit_c_anonymous_local_aggregates_in_function<'tree>(
5146        &mut self,
5147        function: Node<'tree>,
5148        scope: &ScopeInfo,
5149        stack: &mut Vec<CppWork<'tree>>,
5150        ancestry: &ParentIndex<'tree>,
5151    ) {
5152        if !self.c_tag_semantics || scope.class_unit.is_some() {
5153            return;
5154        }
5155        let Some(body) = cpp_body_node(function) else {
5156            return;
5157        };
5158        let mut pending = vec![body];
5159        while let Some(node) = pending.pop() {
5160            if matches!(node.kind(), "function_definition" | "lambda_expression") {
5161                continue;
5162            }
5163            if node.kind() == "declaration"
5164                && self.visit_c_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
5165            {
5166                continue;
5167            }
5168            if matches!(
5169                node.kind(),
5170                "class_specifier" | "struct_specifier" | "union_specifier"
5171            ) {
5172                continue;
5173            }
5174            let mut cursor = node.walk();
5175            let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
5176            children.reverse();
5177            pending.extend(children);
5178        }
5179    }
5180
5181    fn visit_error_swallowed_function_declaration<'tree>(
5182        &mut self,
5183        node: Node<'tree>,
5184        scope: &ScopeInfo,
5185    ) -> bool {
5186        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
5187            return false;
5188        };
5189        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5190            return false;
5191        };
5192        let root = tree.root_node();
5193        let mut cursor = root.walk();
5194        let declarations = root
5195            .named_children(&mut cursor)
5196            .filter(|child| child.kind() != "comment")
5197            .collect::<Vec<_>>();
5198        let [declaration] = declarations.as_slice() else {
5199            return false;
5200        };
5201        if declaration.kind() != "declaration"
5202            || declaration.has_error()
5203            || declaration.start_byte() != start
5204            || declaration.end_byte() != end
5205        {
5206            return false;
5207        }
5208        let recovery = cpp_recovery_window(self.source, start, end);
5209        // The reparsed region is its own tree, so this walk indexes it itself.
5210        let reparsed_ancestry = ParentIndex::new(root);
5211        self.record_recovered_declarations(recovery, |visitor| {
5212            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5213        });
5214        true
5215    }
5216
5217    /// Recover a K&R-compatibility prototype macro invocation such as
5218    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
5219    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
5220    /// defined only in headers outside this workspace (ruby's own
5221    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
5222    /// `name _((args))` and turns each such line into a malformed
5223    /// `declaration` or pointer-expression statement. Before this
5224    /// recovery, the ordinary declaration visitor read that wreckage as a
5225    /// field with the type and name swapped (e.g. a field literally named
5226    /// `VALUE`) and indexed no prototype for the real name at all.
5227    ///
5228    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
5229    /// preserve the declared name, a known one-argument compatibility macro,
5230    /// the nested argument list, and a live terminator. It rejects arbitrary
5231    /// identifiers and bare `ERROR` envelopes that flattened those relations;
5232    /// guessing there previously turned malformed non-macro declarations into
5233    /// invented functions. For each proven candidate this reparses the range
5234    /// before the macro token, the inner parenthesized argument node, and the
5235    /// terminating `;` as one included-range parse. That is the parser-native
5236    /// equivalent of expanding `#define _(args) args` while retaining original
5237    /// byte offsets. The result is admitted only when it is exactly one clean
5238    /// function prototype spanning the recovered run.
5239    fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5240        for candidate in cpp_prototype_macro_candidates(node, self.source) {
5241            let start = candidate.run_start;
5242            let end = candidate.semicolon_end;
5243            if self.byte_range_is_inside_consumed_fragment(start, end) {
5244                continue;
5245            }
5246            let Some(tree) = parse_source_ranges_with_cancellation(
5247                &tree_sitter_cpp::LANGUAGE.into(),
5248                self.source,
5249                &candidate.ranges(),
5250                None,
5251            ) else {
5252                continue;
5253            };
5254            let root = tree.root_node();
5255            let mut cursor = root.walk();
5256            let declarations = root
5257                .named_children(&mut cursor)
5258                .filter(|child| child.kind() != "comment")
5259                .collect::<Vec<_>>();
5260            let [declaration] = declarations.as_slice() else {
5261                continue;
5262            };
5263            if declaration.kind() != "declaration"
5264                || declaration.has_error()
5265                || declaration.start_byte() != start
5266                || declaration.end_byte() != end
5267                || declaration
5268                    .child_by_field_name("declarator")
5269                    .and_then(extract_function_declarator)
5270                    .is_none()
5271            {
5272                continue;
5273            }
5274            let recovery = cpp_recovery_window(self.source, start, end);
5275            // The reparsed region is its own tree, so this walk indexes it
5276            // itself.
5277            let reparsed_ancestry = ParentIndex::new(root);
5278            self.record_recovered_declarations(recovery, |visitor| {
5279                visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5280            });
5281            self.consumed_fragment_regions.push((start, end));
5282        }
5283    }
5284
5285    /// Declare one Module per namespace level of `components` under
5286    /// `package_name`, and return the innermost level's package name and
5287    /// Module. A level an earlier definition already declared is reused.
5288    fn declare_namespace_levels(
5289        &mut self,
5290        mut package_name: String,
5291        components: Vec<String>,
5292        node: Node<'_>,
5293    ) -> (String, Option<CodeUnit>) {
5294        let mut module = None;
5295        for component in components {
5296            let full_name = if package_name.is_empty() {
5297                component
5298            } else {
5299                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
5300            };
5301            let level = CodeUnit::new_fq(
5302                self.file.clone(),
5303                CodeUnitType::Module,
5304                "",
5305                full_name.clone(),
5306                cpp_namespace_fq(&full_name),
5307            );
5308            if !self.parsed.contains_declaration(&level) {
5309                self.add_declaration(level.clone(), node, None, None);
5310            }
5311            package_name = full_name;
5312            module = Some(level);
5313        }
5314        (package_name, module)
5315    }
5316
5317    /// Restore the complete lexical namespace path when recovery dropped an
5318    /// ancestor or retained it past its real close. Parsed namespaces beginning
5319    /// inside the recovered region still contribute their normal nesting.
5320    /// Crossing that boundary also clears any malformed class owner.
5321    fn recovered_namespace_scope(
5322        &mut self,
5323        node: Node<'_>,
5324        scope: &ScopeInfo,
5325    ) -> Option<ScopeInfo> {
5326        self.orphaned_namespaces.region_at(node.start_byte())?;
5327        let components = self
5328            .orphaned_namespaces
5329            .enclosing_namespace_components(node, self.source);
5330        let package_name = components.join(CPP_PACKAGE_SEPARATOR);
5331        if package_name == scope.package_name {
5332            return None;
5333        }
5334        let (package_name, module) = self.declare_namespace_levels(String::new(), components, node);
5335        Some(ScopeInfo {
5336            package_name,
5337            module,
5338            // A recovered namespace body is no longer inside the class (or
5339            // function) that tree-sitter accidentally used as its wrapper.
5340            // Keep namespace/module context, but never carry that owner into
5341            // declarations published from the recovered region.
5342            class_unit: None,
5343            template_signature: None,
5344            template_metadata: None,
5345            declarations_are_fields: false,
5346            recovered_specialization_member_scope: false,
5347            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5348        })
5349    }
5350
5351    fn visit_namespace<'tree>(
5352        &mut self,
5353        node: Node<'tree>,
5354        scope: &ScopeInfo,
5355        stack: &mut Vec<CppWork<'tree>>,
5356        ancestry: &ParentIndex<'tree>,
5357    ) {
5358        let name_node = node.child_by_field_name("name");
5359        let Some(name_node) = name_node else {
5360            if let Some(body) = cpp_body_node(node) {
5361                stack.push(CppWork::Container(CppContainer {
5362                    node: body,
5363                    scope: scope.clone(),
5364                }));
5365            }
5366            return;
5367        };
5368        // Diagnostic corpora contain deliberately ill-formed global namespace
5369        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
5370        // the leading global `::` as the first anonymous child. Honor that AST
5371        // boundary instead of appending the name to the lexical namespace;
5372        // appending produced legacy names such as `outer::::outer::inner`, which
5373        // could not round-trip through the structured FqName boundary.
5374        let explicitly_global = name_node
5375            .child(0)
5376            .is_some_and(|child| !child.is_named() && child.kind() == "::");
5377        let components = cpp_namespace_name_components(name_node, self.source);
5378        if components.is_empty() {
5379            return;
5380        }
5381        // One Module per namespace level. C++17's `namespace a::b { ... }` is
5382        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
5383        // shorthand must declare `a` as well as `a::b` -- extracting only the
5384        // innermost level left the enclosing namespace undeclared and made the
5385        // two spellings of one construct disagree (issue #1878).
5386        let package_name = if explicitly_global {
5387            String::new()
5388        } else {
5389            scope.package_name.clone()
5390        };
5391        let (package_name, module) = self.declare_namespace_levels(package_name, components, node);
5392
5393        let namespace_scope = ScopeInfo {
5394            package_name,
5395            module,
5396            // C++ never nests a namespace inside a class, so a surviving
5397            // class_unit here is always recovery bleed: a malformed-region
5398            // boundary upstream mis-scoped this namespace block. Keeping the
5399            // owner would mint the namespace's declarations as class members
5400            // under a re-appended package, desyncing the fq boundary assert
5401            // (#2306). Dropping it is identity-neutral for valid code, where
5402            // class_unit is always empty at a namespace definition.
5403            class_unit: None,
5404            template_signature: scope.template_signature.clone(),
5405            template_metadata: scope.template_metadata.clone(),
5406            declarations_are_fields: false,
5407            recovered_specialization_member_scope: false,
5408            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5409        };
5410        let container = cpp_body_node(node).unwrap_or(node);
5411        // A malformed export-macro class body may turn the following class
5412        // into a descendant of a bogus function/labeled/error envelope. Those
5413        // descendants are not declaration containers and the ordinary walk
5414        // intentionally does not descend into them. Scan the namespace tree
5415        // once for the strict embedded class geometry before scheduling its
5416        // normal declarations. When one envelope matches, its helper recovers
5417        // every embedded class and the walk need not inspect its descendants.
5418        let mut candidates = vec![container];
5419        while let Some(candidate) = candidates.pop() {
5420            if matches!(
5421                candidate.kind(),
5422                "ERROR" | "function_definition" | "labeled_statement"
5423            ) && self.visit_embedded_function_like_export_classes(
5424                candidate,
5425                &namespace_scope,
5426                stack,
5427                ancestry,
5428            ) {
5429                continue;
5430            }
5431            for index in (0..candidate.named_child_count()).rev() {
5432                candidates.push(
5433                    candidate
5434                        .named_child(index)
5435                        .expect("index below the node's own named child count"),
5436                );
5437            }
5438        }
5439        stack.push(CppWork::Container(CppContainer {
5440            node: container,
5441            scope: namespace_scope,
5442        }));
5443    }
5444
5445    fn visit_class_like<'tree>(
5446        &mut self,
5447        node: Node<'tree>,
5448        scope: &ScopeInfo,
5449        stack: &mut Vec<CppWork<'tree>>,
5450        ancestry: &ParentIndex<'tree>,
5451    ) {
5452        let Some(name) = class_like_name(node, self.source, ancestry) else {
5453            return;
5454        };
5455        let name = qualified_class_name_chain(node, self.source, scope)
5456            .map(|chain| chain.join("$"))
5457            .unwrap_or(name);
5458        self.visit_named_class_like(node, name, scope, stack, ancestry);
5459    }
5460
5461    fn visit_named_class_like<'tree>(
5462        &mut self,
5463        node: Node<'tree>,
5464        name: String,
5465        scope: &ScopeInfo,
5466        stack: &mut Vec<CppWork<'tree>>,
5467        ancestry: &ParentIndex<'tree>,
5468    ) {
5469        let body = cpp_body_node(node);
5470        let definition_body_present = body.is_some();
5471        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
5472            .then(|| extract_cpp_supertypes(node, self.source));
5473        self.visit_named_class_like_shape(
5474            node,
5475            name,
5476            body,
5477            definition_body_present,
5478            None,
5479            raw_supertypes,
5480            scope,
5481            stack,
5482            ancestry,
5483        );
5484    }
5485
5486    /// Whether this class-like declaration is a C tag that belongs to the
5487    /// enclosing non-aggregate scope rather than to the aggregate it is
5488    /// lexically written inside.
5489    ///
5490    /// `class_specifier` is deliberately excluded: `class` is not C, so text
5491    /// that spells one in a `.c` file is not C code and keeps the C++ reading
5492    /// rather than getting a half-C identity.
5493    fn mints_tag_at_enclosing_c_scope(
5494        &self,
5495        declaration_node: Node<'_>,
5496        scope: &ScopeInfo,
5497        ancestry: &ParentIndex<'_>,
5498    ) -> bool {
5499        self.c_tag_semantics
5500            && scope.class_unit.is_some()
5501            && class_like_name(declaration_node, self.source, ancestry).is_some()
5502            && matches!(
5503                declaration_node.kind(),
5504                "struct_specifier" | "union_specifier" | "enum_specifier"
5505            )
5506    }
5507
5508    #[allow(clippy::too_many_arguments)]
5509    fn visit_named_class_like_shape<'tree>(
5510        &mut self,
5511        declaration_node: Node<'tree>,
5512        name: String,
5513        body: Option<Node<'tree>>,
5514        definition_body_present: bool,
5515        explicit_range: Option<Range>,
5516        raw_supertypes: Option<Vec<String>>,
5517        scope: &ScopeInfo,
5518        stack: &mut Vec<CppWork<'tree>>,
5519        ancestry: &ParentIndex<'tree>,
5520    ) -> CodeUnit {
5521        let displaced_macro_tail = if explicit_range.is_none() {
5522            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
5523        } else {
5524            None
5525        };
5526        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
5527        let recovered_scope = self.scope_for_recovered_exported_class(
5528            declaration_node,
5529            &name,
5530            definition_body_present,
5531            scope,
5532            ancestry,
5533        );
5534        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
5535        // aggregate's member list is declared at the enclosing non-aggregate
5536        // scope, not nested inside the aggregate. `scope.class_unit` is the
5537        // only aggregate carrier in this walk, so dropping it puts the tag at
5538        // the nearest enclosing non-aggregate scope -- the module at file or
5539        // namespace scope, and the same block-scope representation a
5540        // function-local aggregate already gets. The tag's own body scope
5541        // below still owns its members, so fields and enumerators are
5542        // unaffected.
5543        let c_tag_scope;
5544        let scope =
5545            if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
5546                c_tag_scope = ScopeInfo {
5547                    class_unit: None,
5548                    ..recovered_scope.clone()
5549                };
5550                &c_tag_scope
5551            } else {
5552                &recovered_scope
5553            };
5554        let short_name = if let Some(parent) = &scope.class_unit {
5555            cpp_join_nested_short(parent.short_name(), &name)
5556        } else {
5557            name.clone()
5558        };
5559        // A top-level out-of-line qualified class definition (`struct
5560        // Outer::Inner { ... }` inside its namespace, #2246) carries its
5561        // nesting chain as the `$`-joined display name; push one Type/Nested
5562        // segment per class so segment-pop owner navigation keeps working.
5563        // Every other leaf name stays opaque so a literal `$` in a source
5564        // identifier never crosses the split/join boundary (#2140).
5565        let qualified_chain = if scope.class_unit.is_none() {
5566            qualified_class_name_chain(declaration_node, self.source, scope)
5567                .filter(|chain| chain.join("$") == name)
5568        } else {
5569            None
5570        };
5571        let fq = if let Some(chain) = qualified_chain {
5572            let mut fq = FqName::new();
5573            cpp_push_package(&mut fq, &scope.package_name);
5574            let mut first = true;
5575            for component in chain {
5576                let kind = if first {
5577                    SegmentKind::Type
5578                } else {
5579                    SegmentKind::Nested
5580                };
5581                fq.push(cpp_segment(&component, kind));
5582                first = false;
5583            }
5584            fq
5585        } else {
5586            cpp_leaf_fq(
5587                &scope.package_name,
5588                scope.class_unit.as_ref(),
5589                &name,
5590                SegmentKind::Nested,
5591                SegmentKind::Type,
5592            )
5593        };
5594        let code_unit = CodeUnit::with_signature_and_fq(
5595            self.file.clone(),
5596            CodeUnitType::Class,
5597            scope.package_name.clone(),
5598            short_name,
5599            scope.template_signature.clone(),
5600            false,
5601            fq,
5602        );
5603        let has_body = definition_body_present;
5604        if !has_body && self.parsed.contains_declaration(&code_unit) {
5605            self.parsed.record_navigation_range(
5606                code_unit.clone(),
5607                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
5608            );
5609            return code_unit;
5610        }
5611        if has_body {
5612            if let Some(range) = explicit_range {
5613                self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
5614            } else {
5615                self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
5616            }
5617        } else {
5618            self.add_declaration(code_unit.clone(), declaration_node, None, None);
5619        }
5620        if let Some(raw_supertypes) = raw_supertypes {
5621            self.parsed
5622                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
5623        }
5624        self.parsed.add_signature(
5625            code_unit.clone(),
5626            render_cpp_type_signature(
5627                declaration_node,
5628                self.source,
5629                scope.template_signature.as_deref(),
5630            ),
5631        );
5632        if let Some(metadata) = &scope.template_metadata {
5633            let primary_short_name = if let Some(parent) = &scope.class_unit {
5634                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
5635            } else {
5636                metadata.primary_name.clone()
5637            };
5638            let primary_fq_name = CodeUnit::new(
5639                self.file.clone(),
5640                CodeUnitType::Class,
5641                scope.package_name.clone(),
5642                primary_short_name,
5643            )
5644            .fq_name();
5645            let mut metadata = metadata.clone();
5646            metadata.primary_fq_name = primary_fq_name;
5647            self.parsed
5648                .set_cpp_template_metadata(code_unit.clone(), metadata);
5649        }
5650        if let Some(parent) = &scope.class_unit {
5651            self.parsed.add_child(parent.clone(), code_unit.clone());
5652        } else if let Some(module) = &scope.module {
5653            self.parsed.add_child(module.clone(), code_unit.clone());
5654        }
5655
5656        if let Some(body) = body {
5657            let mut nested_scope = scope.clone();
5658            nested_scope.class_unit = Some(code_unit.clone());
5659            nested_scope.template_signature = scope.template_signature.clone();
5660            // Template metadata describes the class just created. It must not
5661            // leak into ordinary nested declarations in that class's body.
5662            // Recovered export-macro specializations carry a separate scope bit
5663            // for their declaration-shaped body members.
5664            nested_scope.template_metadata = None;
5665            // Export-macro class bodies recovered from a function_definition use
5666            // compound_statement children, whose direct fields are declarations.
5667            nested_scope.recovered_specialization_member_scope =
5668                scope.template_metadata.as_ref().is_some_and(|metadata| {
5669                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
5670                });
5671            nested_scope.declarations_are_fields =
5672                is_recovered_exported_class_container(declaration_node, self.source)
5673                    || nested_scope.recovered_specialization_member_scope;
5674            if let Some(displaced) = displaced_macro_tail {
5675                // A macro-shaped field without a source semicolon can make
5676                // tree-sitter consume the real class terminator as an ERROR
5677                // inside that field, then retain following namespace items as
5678                // later field-list children. Drain the proven class prefix
5679                // first and re-own only the structured tail with the outer
5680                // scope. The tail is pushed first because the work stack is
5681                // LIFO.
5682                push_cpp_sibling_range(
5683                    body,
5684                    displaced.split_index,
5685                    usize::MAX,
5686                    scope.clone(),
5687                    stack,
5688                );
5689                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
5690            } else {
5691                stack.push(CppWork::Container(CppContainer {
5692                    node: body,
5693                    scope: nested_scope,
5694                }));
5695            }
5696        }
5697        if declaration_node.kind() == "enum_specifier" {
5698            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
5699            if !self.has_enum_enumerator_units(&code_unit) {
5700                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
5701            }
5702        }
5703        code_unit
5704    }
5705
5706    /// Whether the parse product already holds enumerator fields for `parent`,
5707    /// answered from the walk's carried-forward field ownership index.
5708    ///
5709    /// Built on the first enum's question and advanced by every declaration
5710    /// recorded after it, so a file that declares no enum -- most files -- pays
5711    /// nothing, and one that declares thousands pays a single pass instead of
5712    /// one per enum (#2786).
5713    fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
5714        if self.field_owners.is_none() {
5715            self.field_owners = Some(CppFieldOwnerIndex::of(
5716                self.parsed.declarations().iter(),
5717                self.file,
5718            ));
5719        }
5720        debug_assert_eq!(
5721            parent.source(),
5722            self.file,
5723            "the walk's declarations are declarations of the file it is walking"
5724        );
5725        let carried = self
5726            .field_owners
5727            .as_ref()
5728            .expect("the index was just ensured")
5729            .owns_fields(parent.package_name(), parent.short_name());
5730
5731        #[cfg(debug_assertions)]
5732        assert_eq!(
5733            carried,
5734            cpp_declarations_hold_owned_fields(
5735                self.parsed.declarations(),
5736                self.file,
5737                parent.package_name(),
5738                parent.short_name()
5739            ),
5740            "the carried-forward field index must answer what a fresh declaration scan \
5741             answers for {}",
5742            parent.fq_name()
5743        );
5744
5745        carried
5746    }
5747
5748    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
5749        walk_named_tree_preorder(node, false, |child| {
5750            if child.kind() != "enumerator" {
5751                return WalkControl::Continue;
5752            }
5753            let Some(name_node) = child.child_by_field_name("name") else {
5754                return WalkControl::Continue;
5755            };
5756            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
5757            if name.is_empty() {
5758                return WalkControl::Continue;
5759            }
5760            let code_unit = CodeUnit::new_fq(
5761                self.file.clone(),
5762                CodeUnitType::Field,
5763                scope.package_name.clone(),
5764                cpp_join_member_short(parent.short_name(), &name),
5765                parent
5766                    .fq()
5767                    .clone()
5768                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
5769            );
5770            if self.parsed.contains_declaration(&code_unit) {
5771                return WalkControl::Continue;
5772            }
5773            self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
5774            self.parsed.add_signature(
5775                code_unit,
5776                normalize_cpp_whitespace(node_text(child, self.source)),
5777            );
5778            WalkControl::Continue
5779        });
5780    }
5781
5782    fn visit_enum_enumerators_from_text(
5783        &mut self,
5784        node: Node<'_>,
5785        scope: &ScopeInfo,
5786        parent: &CodeUnit,
5787    ) {
5788        let text = node_text(node, self.source);
5789        let Some((_, body)) = text.split_once('{') else {
5790            return;
5791        };
5792        let Some((body, _)) = body.rsplit_once('}') else {
5793            return;
5794        };
5795        for entry in body.split(',') {
5796            let trimmed = entry.trim();
5797            let name = trimmed
5798                .split('=')
5799                .next()
5800                .unwrap_or("")
5801                .split_whitespace()
5802                .next()
5803                .unwrap_or("");
5804            if name.is_empty() {
5805                continue;
5806            }
5807            let code_unit = CodeUnit::new_fq(
5808                self.file.clone(),
5809                CodeUnitType::Field,
5810                scope.package_name.clone(),
5811                cpp_join_member_short(parent.short_name(), name),
5812                parent
5813                    .fq()
5814                    .clone()
5815                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
5816            );
5817            if self.parsed.contains_declaration(&code_unit) {
5818                continue;
5819            }
5820            self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
5821            self.parsed.add_signature(code_unit, trimmed.to_string());
5822        }
5823    }
5824
5825    fn visit_function_definition<'tree>(
5826        &mut self,
5827        node: Node<'tree>,
5828        scope: &ScopeInfo,
5829        stack: &mut Vec<CppWork<'tree>>,
5830        ancestry: &ParentIndex<'tree>,
5831    ) {
5832        // An attribute-like macro invocation whose argument is a declaration can
5833        // swallow every declaration written after it into one bogus
5834        // `function_definition` (#2551). This owns the whole region, so it runs
5835        // ahead of `visit_macro_swallowed_function_declarations` below, which
5836        // admits the same envelope by its head macro token and reads single
5837        // declarators out of nodes this recovery reparses properly.
5838        if self.visit_collapsed_macro_declaration_run(node, scope) {
5839            return;
5840        }
5841        // A file-scope object-like macro sentinel the parser cannot see (issue
5842        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
5843        // prefixes as a bogus `function_definition` that swallows real namespaces,
5844        // classes, and members. Reparse the swallowed interior as C++ items so the
5845        // ordinary declaration visitors index it with byte/line-exact ownership.
5846        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5847            return;
5848        }
5849        if node.has_error() {
5850            self.visit_macro_swallowed_function_declarations(node, scope);
5851        }
5852        if let Some((class_node, name, raw_supertypes)) =
5853            recover_exported_class_function_definition(node, self.source)
5854        {
5855            if let Some(body) = cpp_body_node(node)
5856                && let Some(close) = self
5857                    .orphaned_namespaces
5858                    .matching_close_brace(body.start_byte())
5859                && close.end_byte < body.end_byte()
5860            {
5861                let class_range = Range {
5862                    start_byte: node.start_byte(),
5863                    end_byte: close.end_byte,
5864                    start_line: node.start_position().row + 1,
5865                    end_line: close.end_line,
5866                };
5867                // Parse the body in class context, preserving the original
5868                // keyword, recovered name and body as included ranges. Parsing
5869                // the interior at file scope turns constructors and access
5870                // labels into another oversized ERROR instead of members.
5871                let mut head = vec![node];
5872                let mut keyword = None;
5873                let mut name_node = None;
5874                while let Some(part) = head.pop() {
5875                    if part.start_byte() >= body.start_byte() || part.is_missing() {
5876                        continue;
5877                    }
5878                    if matches!(part.kind(), "class" | "struct" | "union") {
5879                        keyword = Some(part);
5880                    }
5881                    if matches!(part.kind(), "identifier" | "type_identifier")
5882                        && node_text(part, self.source) == name
5883                    {
5884                        name_node = Some(part);
5885                    }
5886                    let mut cursor = part.walk();
5887                    head.extend(part.children(&mut cursor));
5888                }
5889                if let (Some(keyword), Some(name_node)) = (keyword, name_node)
5890                    && let Some(type_name) = keyword
5891                        .parent()
5892                        .and_then(|parent| parent.child_by_field_name("name"))
5893                    && let Some(tree) = parse_source_ranges_with_cancellation(
5894                        &tree_sitter_cpp::LANGUAGE.into(),
5895                        self.source,
5896                        &[
5897                            (keyword.start_byte(), type_name.start_byte()),
5898                            (name_node.start_byte(), name_node.end_byte()),
5899                            (body.start_byte(), close.end_byte),
5900                        ],
5901                        None,
5902                    )
5903                    && let Some(reparsed_class) = tree.root_node().named_child(0)
5904                    && let Some(class_body) = cpp_body_node(reparsed_class)
5905                    && class_body.start_byte() == body.start_byte()
5906                    && class_body.end_byte() == close.end_byte
5907                    && let Some(tail) =
5908                        cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
5909                {
5910                    let class_unit = self.visit_named_class_like_shape(
5911                        class_node,
5912                        name,
5913                        None,
5914                        true,
5915                        Some(class_range),
5916                        raw_supertypes,
5917                        scope,
5918                        stack,
5919                        ancestry,
5920                    );
5921                    self.parsed.record_materialization(
5922                        MaterializationRecord::RecoveredDeclaration {
5923                            recovery: class_range,
5924                            unit: class_unit.clone(),
5925                        },
5926                    );
5927                    let member_scope = ScopeInfo {
5928                        package_name: class_unit.package_name().to_string(),
5929                        class_unit: Some(class_unit),
5930                        declarations_are_fields: true,
5931                        template_metadata: None,
5932                        recovered_specialization_member_scope: false,
5933                        ..scope.clone()
5934                    };
5935                    let class_body_range = class_body.byte_range();
5936                    let tail_range = tail.root_node().byte_range();
5937                    self.partitioned_regions
5938                        .push((tail, tail_range, scope.clone()));
5939                    self.partitioned_regions
5940                        .push((tree, class_body_range, member_scope));
5941                    return;
5942                }
5943            }
5944            let body = cpp_body_node(class_node);
5945            let displaced_namespace = cpp_body_node(node)
5946                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
5947            let fragmented = cpp_body_node(node).and_then(|body| {
5948                fragmented_export_function_body_region(
5949                    node,
5950                    body,
5951                    self.source,
5952                    displaced_namespace.as_ref(),
5953                )
5954            });
5955            // The recovery tuple's first node is the class-like type when the
5956            // parser exposes one, but the synthetic wrapper owns the compound
5957            // statement that contains the truncated class body. Use the
5958            // wrapper body for fragmented-member detection; retain the
5959            // class-node body for the ordinary (non-fragmented) path below.
5960            if let Some(fragmented) = fragmented {
5961                // The lifted sibling no longer sits below the parser-visible
5962                // namespace node. Restore the current parent scope when the
5963                // ordinary work walk reaches that class.
5964                if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
5965                    .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
5966                {
5967                    let mut boundary_scope = scope.clone();
5968                    for sibling in cpp_following_named_siblings(node, self.source) {
5969                        if sibling.start_byte() >= boundary.start_byte() {
5970                            break;
5971                        }
5972                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
5973                            boundary_scope.visible_using_namespaces.push(namespace);
5974                        }
5975                    }
5976                    self.recovered_class_sibling_scopes
5977                        .insert(boundary.id(), boundary_scope);
5978                }
5979                let mut recovered_constructor = None;
5980                let mut recovered_prefix_tree = None;
5981                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
5982                {
5983                    Some(FragmentedExportMembers::Complete(tree)) => {
5984                        if let Some(body) = body
5985                            && let Some(range) =
5986                                cpp_reparsed_synthetic_initializer_constructor_range(
5987                                    tree.root_node(),
5988                                    &name,
5989                                    self.source,
5990                                    body.end_byte(),
5991                                )
5992                        {
5993                            recovered_constructor = Some(range);
5994                            recovered_prefix_tree = Some(tree);
5995                            None
5996                        } else {
5997                            Some(FragmentedExportMembers::Complete(tree))
5998                        }
5999                    }
6000                    outcome => outcome,
6001                };
6002                let mut class_stack = Vec::new();
6003                let class_unit = self.visit_named_class_like_shape(
6004                    class_node,
6005                    name,
6006                    None,
6007                    true,
6008                    Some(fragmented.class_range),
6009                    raw_supertypes,
6010                    scope,
6011                    &mut class_stack,
6012                    ancestry,
6013                );
6014                self.parsed
6015                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
6016                        recovery: fragmented.class_range,
6017                        unit: class_unit.clone(),
6018                    });
6019                let complete = outcome.is_some_and(|outcome| {
6020                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
6021                });
6022                if complete {
6023                    self.consumed_fragment_regions
6024                        .push((node.start_byte(), fragmented.class_range.end_byte));
6025                } else {
6026                    // The reparse can fail when the first constructor or a
6027                    // method body is split into statement-shaped siblings.
6028                    // Keep the recovered class envelope, but do not visit the
6029                    // synthetic wrapper body: its initializer expressions can
6030                    // look like same-named member functions (for example
6031                    // `Token.location(loc)`). Re-own only the original sibling
6032                    // nodes that fall inside the proven class range. Their CST
6033                    // shapes retain the real field/function kinds and ranges.
6034                    let member_scope = ScopeInfo {
6035                        package_name: class_unit.package_name().to_string(),
6036                        module: scope.module.clone(),
6037                        class_unit: Some(class_unit.clone()),
6038                        template_signature: scope.template_signature.clone(),
6039                        template_metadata: None,
6040                        declarations_are_fields: true,
6041                        recovered_specialization_member_scope: false,
6042                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
6043                    };
6044                    for candidate in cpp_following_named_siblings(node, self.source) {
6045                        if candidate.start_byte() >= fragmented.reparse_end {
6046                            break;
6047                        }
6048                        if cpp_fragment_sibling_is_class_member(
6049                            candidate,
6050                            fragmented.reparse_end,
6051                            self.source,
6052                        ) {
6053                            self.recovered_class_sibling_scopes
6054                                .insert(candidate.id(), member_scope.clone());
6055                        }
6056                    }
6057                    if let Some(range) = recovered_constructor
6058                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
6059                    {
6060                        self.visit_recovered_fragment_prefix_members(
6061                            prefix_tree.root_node(),
6062                            range.start,
6063                            &class_unit,
6064                            scope,
6065                            ancestry,
6066                        );
6067                        self.visit_recovered_fragment_constructor(
6068                            range,
6069                            body,
6070                            class_node,
6071                            &class_unit,
6072                            scope,
6073                            ancestry,
6074                        );
6075                    }
6076                }
6077                if let Some(boundary) = displaced_namespace {
6078                    for item in boundary.namespace_items {
6079                        self.recovered_class_sibling_scopes
6080                            .insert(item.id(), scope.clone());
6081                    }
6082                }
6083                stack.extend(class_stack);
6084                return;
6085            }
6086            let mut stack = Vec::new();
6087            let class_unit = self.visit_named_class_like_shape(
6088                class_node,
6089                name,
6090                body,
6091                body.is_some(),
6092                None,
6093                raw_supertypes,
6094                scope,
6095                &mut stack,
6096                ancestry,
6097            );
6098            self.parsed
6099                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6100                    recovery: cpp_declaration_range(node),
6101                    unit: class_unit,
6102                });
6103            // Issue #1524: the bogus `function_definition` body can run past
6104            // the class's true closing brace (the parse ends it with a
6105            // zero-width `MISSING "}"`), swallowing following namespace-scope
6106            // siblings -- they would index as members of the recovered class.
6107            // When the body's text-balanced close lands before the body's own
6108            // end, re-own the swallowed tail with the outer scope instead.
6109            if let Some(body) = body
6110                && let Some(class_close) = self
6111                    .orphaned_namespaces
6112                    .matching_close_brace(body.start_byte())
6113                && class_close.start_byte < body.end_byte()
6114            {
6115                let split = {
6116                    let mut cursor = body.walk();
6117                    body.named_children(&mut cursor)
6118                        .position(|child| child.start_byte() > class_close.start_byte)
6119                };
6120                if let Some(split) = split {
6121                    // The seeded work is a single Container over the whole
6122                    // body with the class scope; replace it with the bounded
6123                    // head (class scope) plus the swallowed tail (outer
6124                    // scope). Push tail first so the head drains first.
6125                    let seeded = stack.pop();
6126                    match seeded {
6127                        Some(CppWork::Container(container)) => {
6128                            push_cpp_sibling_range(
6129                                body,
6130                                split,
6131                                usize::MAX,
6132                                scope.clone(),
6133                                &mut stack,
6134                            );
6135                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
6136                        }
6137                        // visit_named_class_like_shape always seeds exactly
6138                        // one Container when a body is present.
6139                        _ => unreachable!("exported-class seed is always one Container"),
6140                    }
6141                }
6142            }
6143            while let Some(work) = stack.pop() {
6144                match work {
6145                    CppWork::Container(container) => {
6146                        push_cpp_container_work(container.node, container.scope, &mut stack);
6147                    }
6148                    CppWork::Siblings(siblings) => {
6149                        advance_cpp_siblings(siblings, self.source, &mut stack);
6150                    }
6151                    CppWork::Node(work) => {
6152                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
6153                    }
6154                }
6155            }
6156            return;
6157        }
6158        let recovered_constraint_constructor =
6159            cpp_recovered_template_macro_constructor(node, self.source);
6160        let declarator = recovered_constraint_constructor
6161            .map(|(declarator, _)| declarator)
6162            .or_else(|| node.child_by_field_name("declarator"));
6163        let Some(declarator) = declarator else {
6164            self.visit_malformed_function_definition_container(node, scope, stack);
6165            return;
6166        };
6167        let Some(function_declarator) = extract_function_declarator(declarator) else {
6168            self.visit_malformed_function_definition_container(node, scope, stack);
6169            return;
6170        };
6171        let function = if let Some((_, callable_name)) =
6172            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
6173        {
6174            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
6175        } else {
6176            extract_function_info(function_declarator, self.source, scope)
6177        };
6178        let Some(mut function) = function else {
6179            self.visit_malformed_function_definition_container(node, scope, stack);
6180            return;
6181        };
6182        if let Some((_, template_parameter)) = recovered_constraint_constructor {
6183            function.signature = format!(
6184                "template <{}>{}",
6185                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
6186                function.signature
6187            );
6188        }
6189        let code_unit = function.code_unit(self.file.clone());
6190        // Keep an earlier same-file prototype as another physical occurrence
6191        // of this callable. `CodeUnit` already identifies the role-neutral
6192        // overload, while ranges and signature metadata describe its
6193        // declaration/definition occurrences.
6194        self.add_declaration(code_unit.clone(), node, None, None);
6195        let signature = if recovered_constraint_constructor.is_some() {
6196            normalize_cpp_whitespace(node_text(function_declarator, self.source))
6197        } else {
6198            render_cpp_function_display_signature_from_node(
6199                node,
6200                self.source,
6201                scope.template_signature.as_deref(),
6202                true,
6203                ancestry,
6204            )
6205        };
6206        self.parsed.add_signature_with_metadata(
6207            code_unit.clone(),
6208            cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
6209                .with_declaration_only(false)
6210                .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
6211        );
6212        if let Some(parent) = &scope.class_unit {
6213            self.parsed.add_child(parent.clone(), code_unit);
6214        } else if let Some(module) = &scope.module {
6215            self.parsed.add_child(module.clone(), code_unit);
6216        }
6217        self.visit_c_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
6218    }
6219
6220    /// Recover the namespace lost when tree-sitter promotes an export-macro
6221    /// class definition to a root-level `function_definition`.  Only a
6222    /// body-bearing, top-level recovery may borrow a namespace, and only when
6223    /// one earlier namespace-scope forward declaration proves the identity.
6224    fn scope_for_recovered_exported_class<'tree>(
6225        &mut self,
6226        node: Node<'tree>,
6227        name: &str,
6228        definition_body_present: bool,
6229        scope: &ScopeInfo,
6230        ancestry: &ParentIndex<'tree>,
6231    ) -> ScopeInfo {
6232        if !definition_body_present
6233            || !scope.package_name.is_empty()
6234            || scope.class_unit.is_some()
6235            || !(is_recovered_exported_class_container(node, self.source)
6236                || recover_function_like_export_class_pair(node, self.source).is_some()
6237                || recover_embedded_function_like_export_classes(node, self.source)
6238                    .iter()
6239                    .any(|recovered| recovered.name == name)
6240                || matches!(node.kind(), "declaration" | "field_declaration")
6241                    && recover_exported_class_declaration(node, self.source).is_some()
6242                || matches!(
6243                    node.kind(),
6244                    "class_specifier" | "struct_specifier" | "union_specifier"
6245                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
6246                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
6247                        name_node,
6248                        self.source,
6249                    )))
6250                }) || ancestry.parent(node).is_some_and(|parent| {
6251                    matches!(parent.kind(), "declaration" | "field_declaration")
6252                        && recover_exported_class_declaration(parent, self.source).is_some()
6253                        || is_recovered_exported_class_container(parent, self.source)
6254                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
6255        {
6256            return scope.clone();
6257        }
6258        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
6259        let Some(package_name) = borrowed_namespace
6260            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
6261        else {
6262            return scope.clone();
6263        };
6264
6265        let module = CodeUnit::new_fq(
6266            self.file.clone(),
6267            CodeUnitType::Module,
6268            "",
6269            package_name.clone(),
6270            cpp_namespace_fq(&package_name),
6271        );
6272        let mut recovered = scope.clone();
6273        recovered.package_name = package_name;
6274        recovered.module = Some(module);
6275        recovered
6276    }
6277
6278    /// The unique namespace-scope forward declaration of `name` that precedes
6279    /// `recovered_node`, answered from the walk's carried-forward scan of the
6280    /// tree `recovered_node` belongs to.
6281    ///
6282    /// The scan is built on the first question and advanced by each later one,
6283    /// so a file that never reaches this path -- almost every file -- pays
6284    /// nothing, and one that reaches it thousands of times pays a single pass
6285    /// (#2754).
6286    fn unique_earlier_namespace_forward<'tree>(
6287        &mut self,
6288        recovered_node: Node<'tree>,
6289        name: &str,
6290        ancestry: &ParentIndex<'tree>,
6291    ) -> Option<String> {
6292        let mut root = recovered_node;
6293        while let Some(parent) = ancestry.parent(root) {
6294            root = parent;
6295        }
6296        let source = self.source;
6297        let scan = self
6298            .namespace_forward_scans
6299            .entry(CppTreeIdentity::of(root))
6300            .or_default();
6301        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
6302        let borrowed = scan.unique_earlier_forward(name, recovered_node);
6303
6304        #[cfg(debug_assertions)]
6305        assert_eq!(
6306            borrowed,
6307            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
6308            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
6309             for {name} at byte {}",
6310            recovered_node.start_byte()
6311        );
6312
6313        borrowed
6314    }
6315
6316    fn visit_malformed_function_definition_container<'tree>(
6317        &mut self,
6318        node: Node<'tree>,
6319        scope: &ScopeInfo,
6320        stack: &mut Vec<CppWork<'tree>>,
6321    ) {
6322        let Some(body) = cpp_body_node(node) else {
6323            return;
6324        };
6325        if !cpp_contains_namespace_definition(body) {
6326            return;
6327        }
6328        stack.push(CppWork::Container(CppContainer {
6329            node: body,
6330            scope: scope.clone(),
6331        }));
6332    }
6333
6334    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
6335    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
6336    /// emits for a sentinel-prefixed region, reparse the interior after the
6337    /// sentinel identifier as real C++ items -- confined to the region so
6338    /// every reparsed node keeps its original byte/line position -- and run the
6339    /// ordinary container visitation over the result. Returns `true` when it fired
6340    /// (the caller must then skip normal function processing). Nested sentinel
6341    /// regions recover recursively: the reparsed interior is walked through the
6342    /// same `visit_function_definition` path, so a sentinel inside the region hits
6343    /// this recovery again.
6344    /// Runs `reparse_walk` and records every declaration it mints as a
6345    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
6346    /// `recovery` (issue #1657). A reparsed sentinel region has no single
6347    /// recovered envelope unit: the ordinary visitors mint namespaces,
6348    /// classes, and members directly from the reparsed tree, so the walk's
6349    /// declaration delta is the recovered set. Records are ordered by
6350    /// declaration start byte so the parse product stays deterministic.
6351    fn record_recovered_declarations(
6352        &mut self,
6353        recovery: Range,
6354        reparse_walk: impl FnOnce(&mut Self),
6355    ) {
6356        // The set difference this used to be, kept as the oracle every answer
6357        // is asserted against (#2787).
6358        #[cfg(any(debug_assertions, test))]
6359        let before = self.parsed.declarations().clone();
6360
6361        self.recovery_captures.push(CppRecoveryCapture::default());
6362        reparse_walk(self);
6363        let captured = self
6364            .recovery_captures
6365            .pop()
6366            .expect("the capture this call pushed is the one it pops");
6367
6368        // The capture holds every declaration created while it was open, once
6369        // each and in creation order, so the recovered set costs what the
6370        // recovery made rather than everything the file has declared so far.
6371        // One filter is left to apply: a created declaration that a later
6372        // deferred replacement removed is not in the parse product to report.
6373        let mut minted: Vec<CodeUnit> = captured
6374            .created
6375            .into_iter()
6376            .filter(|unit| self.parsed.contains_declaration(unit))
6377            .collect();
6378        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6379
6380        #[cfg(any(debug_assertions, test))]
6381        {
6382            let mut rediscovered: Vec<CodeUnit> = self
6383                .parsed
6384                .declarations()
6385                .iter()
6386                .filter(|unit| !before.contains(*unit))
6387                .cloned()
6388                .collect();
6389            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6390            assert_eq!(
6391                minted, rediscovered,
6392                "the captured recovered set must be the declaration delta of the reparse \
6393                 walk over {recovery:?}"
6394            );
6395        }
6396
6397        for unit in minted {
6398            self.parsed
6399                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6400                    recovery,
6401                    unit,
6402                });
6403        }
6404    }
6405
6406    /// Where one recovered declaration sorts: by start byte, then by name, so
6407    /// the parse product stays deterministic.
6408    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
6409        let start = self
6410            .parsed
6411            .declaration_ranges(unit)
6412            .first()
6413            .map(|range| range.start_byte)
6414            .unwrap_or(usize::MAX);
6415        (start, unit.fq_name().to_string())
6416    }
6417
6418    fn visit_sentinel_macro_region<'tree>(
6419        &mut self,
6420        node: Node<'tree>,
6421        scope: &ScopeInfo,
6422        stack: &mut Vec<CppWork<'tree>>,
6423        ancestry: &ParentIndex<'tree>,
6424    ) -> bool {
6425        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
6426            return true;
6427        }
6428        if let Some((
6429            reparse_start,
6430            class_start,
6431            body_start,
6432            class_close_start,
6433            class_close_end,
6434            class_close_line,
6435        )) = cpp_sentinel_macro_class_region(node, self.source)
6436        {
6437            let Some(class_tree) =
6438                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
6439            else {
6440                return false;
6441            };
6442            let class_root = class_tree.root_node();
6443            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
6444            // A region reparse is its own tree and needs its own parent index.
6445            let class_ancestry = ParentIndex::new(class_root);
6446            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
6447                class_root,
6448                template_node,
6449                self.source,
6450                &class_ancestry,
6451            ) else {
6452                return false;
6453            };
6454            let class_node = reparsed_class.declaration_node;
6455            let name = reparsed_class.name;
6456            let mut class_scope = scope.clone();
6457            if let Some(template_node) = template_node {
6458                class_scope.template_signature =
6459                    cpp_template_signature(template_node, class_node, self.source);
6460                class_scope.template_metadata =
6461                    cpp_template_metadata(template_node, class_node, self.source, ancestry);
6462            }
6463            let Some(body_tree) =
6464                cpp_reparse_region_items(self.source, body_start, class_close_start)
6465            else {
6466                return false;
6467            };
6468            let raw_supertypes = reparsed_class.raw_supertypes;
6469            let class_range = Range {
6470                start_byte: class_start,
6471                end_byte: class_close_end,
6472                start_line: class_node.start_position().row + 1,
6473                end_line: class_close_line,
6474            };
6475            let class_scope = self.scope_for_recovered_exported_class(
6476                class_node,
6477                &name,
6478                true,
6479                &class_scope,
6480                ancestry,
6481            );
6482            let mut class_stack = Vec::new();
6483            let class_unit = self.visit_named_class_like_shape(
6484                class_node,
6485                name,
6486                None,
6487                true,
6488                Some(class_range),
6489                raw_supertypes,
6490                &class_scope,
6491                &mut class_stack,
6492                ancestry,
6493            );
6494            self.parsed
6495                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6496                    recovery: class_range,
6497                    unit: class_unit.clone(),
6498                });
6499            let member_scope = ScopeInfo {
6500                package_name: class_scope.package_name.clone(),
6501                module: class_scope.module.clone(),
6502                class_unit: Some(class_unit),
6503                template_signature: class_scope.template_signature.clone(),
6504                template_metadata: None,
6505                declarations_are_fields: true,
6506                recovered_specialization_member_scope: false,
6507                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
6508            };
6509            // The padded body reparse is its own tree, so it indexes itself.
6510            let body_root = body_tree.root_node();
6511            self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
6512            // Register only after the padded body reparse: its nodes deliberately
6513            // retain offsets inside the consumed region and must be visited first.
6514            self.consumed_fragment_regions
6515                .push((node.start_byte(), class_close_end));
6516            // An ERROR envelope can hold real sibling declarations after the
6517            // recovered class's close (the suffix-reparse boundary in
6518            // `cpp_sentinel_macro_class_region` partitions, it does not
6519            // consume). Walk the envelope's remaining children normally; the
6520            // consumed region above keeps the recovered class from being
6521            // indexed twice.
6522            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
6523                stack.push(CppWork::Container(CppContainer {
6524                    node,
6525                    scope: scope.clone(),
6526                }));
6527            }
6528            return true;
6529        }
6530        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
6531            return false;
6532        };
6533        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6534            return false;
6535        };
6536        let root = tree.root_node();
6537        if !cpp_reparsed_items_are_indexable(root, self.source) {
6538            return false;
6539        }
6540        let recovery = cpp_recovery_window(self.source, start, end);
6541        // The reparsed region is its own tree, so this walk indexes it itself.
6542        let reparsed_ancestry = ParentIndex::new(root);
6543        self.record_recovered_declarations(recovery, |visitor| {
6544            visitor.visit_container(
6545                root,
6546                &reparsed_ancestry,
6547                &scope.package_name,
6548                scope.module.clone(),
6549                scope.class_unit.clone(),
6550                scope.template_signature.clone(),
6551                scope.visible_using_namespaces.clone(),
6552            );
6553        });
6554        if end > node.end_byte() {
6555            self.consumed_fragment_regions
6556                .push((node.start_byte(), end));
6557        } else if node.kind() == "ERROR" && node.end_byte() > end {
6558            // The sentinel region ended at the first recovered class-like item
6559            // but the ERROR envelope keeps real sibling declarations after it
6560            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
6561            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
6562            // envelope's remaining children normally; the consumed region
6563            // keeps the reparsed prefix from being indexed twice.
6564            self.consumed_fragment_regions
6565                .push((node.start_byte(), end));
6566            stack.push(CppWork::Container(CppContainer {
6567                node,
6568                scope: scope.clone(),
6569            }));
6570        }
6571        true
6572    }
6573
6574    /// Re-own complete class declarations from the structured Abseil
6575    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
6576    /// its direct CST children already prove both namespace components and the
6577    /// class bodies, so the ordinary class/member visitor can retain ownership
6578    /// and exact source ranges without admitting unrelated callable bodies.
6579    fn visit_nested_namespace_sentinel<'tree>(
6580        &mut self,
6581        node: Node<'tree>,
6582        scope: &ScopeInfo,
6583        ancestry: &ParentIndex<'tree>,
6584    ) -> bool {
6585        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
6586            return false;
6587        };
6588
6589        let mut package_name = scope.package_name.clone();
6590        let mut module = scope.module.clone();
6591        for component in recovered.namespace_components {
6592            package_name = if package_name.is_empty() {
6593                component
6594            } else {
6595                format!("{package_name}::{component}")
6596            };
6597            let namespace_module = CodeUnit::new_fq(
6598                self.file.clone(),
6599                CodeUnitType::Module,
6600                "",
6601                package_name.clone(),
6602                cpp_namespace_fq(&package_name),
6603            );
6604            if !self.parsed.contains_declaration(&namespace_module) {
6605                self.add_declaration(namespace_module.clone(), recovered.function, None, None);
6606            }
6607            module = Some(namespace_module);
6608        }
6609
6610        let recovered_scope = ScopeInfo {
6611            package_name,
6612            module,
6613            // This scope's package comes from the namespace levels the sentinel
6614            // shape proves, so its owner must come from the same place. C++
6615            // never nests a namespace inside a class, so a surviving
6616            // `class_unit` here is recovery bleed from an earlier malformed
6617            // region, and keeping it would publish declarations whose package
6618            // names one lexical position and whose owner names another -- the
6619            // hybrid identity that trips the package/short boundary assert in
6620            // `CodeUnit::with_signature_and_fq` (#2306, #2979). Dropping it is
6621            // identity-neutral for valid code, where `class_unit` is always
6622            // empty at a namespace.
6623            class_unit: None,
6624            template_signature: scope.template_signature.clone(),
6625            template_metadata: scope.template_metadata.clone(),
6626            declarations_are_fields: false,
6627            recovered_specialization_member_scope: false,
6628            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6629        };
6630        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
6631            recovered.function,
6632            recovered.body,
6633            self.source,
6634            ancestry,
6635        ) {
6636            let mut class_scope = recovered_scope.clone();
6637            if let Some(template_node) = fragmented.template_node {
6638                class_scope.template_signature =
6639                    cpp_template_signature(template_node, fragmented.class_node, self.source);
6640                class_scope.template_metadata = cpp_template_metadata(
6641                    template_node,
6642                    fragmented.class_node,
6643                    self.source,
6644                    ancestry,
6645                );
6646            }
6647            if let Some(outcome) = self
6648                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
6649            {
6650                let mut class_stack = Vec::new();
6651                let class_unit = self.visit_named_class_like_shape(
6652                    fragmented.class_node,
6653                    fragmented.name.clone(),
6654                    None,
6655                    true,
6656                    Some(fragmented.fragmented.class_range),
6657                    fragmented.raw_supertypes.clone(),
6658                    &class_scope,
6659                    &mut class_stack,
6660                    ancestry,
6661                );
6662                self.parsed
6663                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
6664                        recovery: fragmented.fragmented.class_range,
6665                        unit: class_unit.clone(),
6666                    });
6667                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
6668                    self.consumed_fragment_regions.push((
6669                        fragmented.consumed_start,
6670                        fragmented.fragmented.class_range.end_byte,
6671                    ));
6672                }
6673            }
6674        }
6675        // The class requirement above is the admission gate; once admitted,
6676        // traverse the whole proven inner namespace body so sibling aliases,
6677        // functions, and variables are not silently discarded. The body is a
6678        // node of the tree being walked, so it reuses that tree's index.
6679        self.run_container_work(recovered.body, recovered_scope, ancestry);
6680        true
6681    }
6682
6683    fn visit_declaration<'tree>(
6684        &mut self,
6685        node: Node<'tree>,
6686        scope: &ScopeInfo,
6687        in_class_body: bool,
6688        stack: &mut Vec<CppWork<'tree>>,
6689        ancestry: &ParentIndex<'tree>,
6690    ) {
6691        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6692            return;
6693        }
6694        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
6695            return;
6696        }
6697        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
6698            !cpp_active_template_type_parameter(
6699                node,
6700                node_text(declarator, self.source),
6701                self.source,
6702                ancestry,
6703            )
6704        }) {
6705            return;
6706        }
6707        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
6708            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
6709            // the following `Imaging image` member into one malformed field.
6710            // The ERROR's identifier is the actual declarator; route it
6711            // through the ordinary field path so its parent, range, and
6712            // signature metadata remain consistent with every other member.
6713            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
6714            return;
6715        }
6716        if in_class_body
6717            && let Some(parent) = scope.class_unit.as_ref()
6718            && let Some(call) =
6719                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
6720        {
6721            self.visit_recovered_macro_qualified_constructor_definition(
6722                node, call, scope, ancestry,
6723            );
6724            return;
6725        }
6726        if in_class_body
6727            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
6728        {
6729            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
6730            return;
6731        }
6732        if in_class_body
6733            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
6734        {
6735            for member in members {
6736                self.add_macro_wrapped_declaration(member, scope, ancestry);
6737            }
6738            return;
6739        }
6740        if in_class_body
6741            && let Some(declarators) =
6742                recovered_macro_qualified_field_declarators(node, self.source)
6743        {
6744            for declarator in declarators {
6745                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6746            }
6747            return;
6748        }
6749        let recovered_alias_names = recovered_type_alias_names(node, self.source);
6750        if !recovered_alias_names.is_empty() {
6751            self.add_type_aliases(node, scope, recovered_alias_names, ancestry);
6752            return;
6753        }
6754        if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
6755        {
6756            return;
6757        }
6758        if self.visit_c_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
6759            return;
6760        }
6761
6762        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
6763            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
6764                // Issue #938: the members tree-sitter scattered out of the fragmented
6765                // multiple-base export node are reparsed from their true body region
6766                // and re-owned as members of the recovered class, with an explicit
6767                // navigation range spanning to the displaced closing brace.
6768                if let Some(outcome) =
6769                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
6770                {
6771                    let consumed_region = (
6772                        recovered.declaration_node.end_byte(),
6773                        fragmented.class_range.end_byte,
6774                    );
6775                    let code_unit = self.visit_named_class_like_shape(
6776                        recovered.declaration_node,
6777                        recovered.name,
6778                        None,
6779                        true,
6780                        Some(fragmented.class_range),
6781                        recovered.raw_supertypes,
6782                        scope,
6783                        stack,
6784                        ancestry,
6785                    );
6786                    self.parsed.record_materialization(
6787                        MaterializationRecord::RecoveredDeclaration {
6788                            recovery: fragmented.class_range,
6789                            unit: code_unit.clone(),
6790                        },
6791                    );
6792                    let consume_fragment =
6793                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
6794                    // Everything between the fragmented declaration and its displaced
6795                    // closing brace now belongs to the recovered class; keep the
6796                    // ordinary walk from re-indexing those scattered siblings at top
6797                    // level. Register the consumed region only after indexing because
6798                    // the reparsed nodes retain byte offsets inside that same region.
6799                    if consume_fragment {
6800                        self.consumed_fragment_regions.push(consumed_region);
6801                    }
6802                    return;
6803                }
6804            }
6805            let uses_initializer_body = recovered.uses_initializer_body;
6806            let definition_body_present = recovered.body.is_some();
6807            let class_unit = self.visit_named_class_like_shape(
6808                recovered.declaration_node,
6809                recovered.name,
6810                recovered.body,
6811                definition_body_present,
6812                None,
6813                recovered.raw_supertypes,
6814                scope,
6815                stack,
6816                ancestry,
6817            );
6818            self.parsed
6819                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6820                    recovery: cpp_declaration_range(node),
6821                    unit: class_unit,
6822                });
6823            if uses_initializer_body {
6824                return;
6825            }
6826        }
6827
6828        let mut handled_function = false;
6829        let mut handled_declarator = false;
6830        let mut cursor = node.walk();
6831        for child in node.named_children(&mut cursor) {
6832            if matches!(
6833                child.kind(),
6834                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6835            ) {
6836                // A named class-like definition remains a declaration even when
6837                // the same statement also declares an object, for example
6838                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
6839                // declaration's type and `kind` as its declarator.  Dropping the
6840                // type here loses both its nested owner and every later lexical
6841                // reference to it.  A body is the structured proof that this is
6842                // a definition rather than an elaborated type use such as
6843                // `class Kind value;`.
6844                if cpp_body_node(child).is_some() {
6845                    self.visit_class_like(child, scope, stack, ancestry);
6846                }
6847                continue;
6848            }
6849        }
6850
6851        let mut cursor = node.walk();
6852        for child in node.children_by_field_name("declarator", &mut cursor) {
6853            if crate::structural::is_recovered_designator_init_declarator(child) {
6854                handled_declarator = true;
6855                continue;
6856            }
6857            if in_class_body
6858                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
6859            {
6860                handled_declarator = true;
6861                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
6862                continue;
6863            }
6864            if let Some(kind) = classify_declarator(child) {
6865                handled_declarator = true;
6866                match kind {
6867                    DeclaratorKind::Function(function_declarator) => {
6868                        handled_function = true;
6869                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
6870                    }
6871                    DeclaratorKind::Variable(variable_declarator) => {
6872                        self.visit_variable_declaration(
6873                            node,
6874                            variable_declarator,
6875                            scope,
6876                            in_class_body,
6877                            ancestry,
6878                        );
6879                    }
6880                }
6881            }
6882        }
6883
6884        if !handled_declarator {
6885            let mut cursor = node.walk();
6886            for child in node.named_children(&mut cursor) {
6887                if crate::structural::is_recovered_designator_init_declarator(child) {
6888                    handled_declarator = true;
6889                    continue;
6890                }
6891                if !is_unfielded_declarator_candidate(child) {
6892                    continue;
6893                }
6894                let Some(kind) = classify_declarator(child) else {
6895                    continue;
6896                };
6897                handled_declarator = true;
6898                match kind {
6899                    DeclaratorKind::Function(function_declarator) => {
6900                        handled_function = true;
6901                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
6902                    }
6903                    DeclaratorKind::Variable(variable_declarator) => {
6904                        self.visit_variable_declaration(
6905                            node,
6906                            variable_declarator,
6907                            scope,
6908                            in_class_body,
6909                            ancestry,
6910                        );
6911                    }
6912                }
6913            }
6914        }
6915
6916        if handled_function {
6917            return;
6918        }
6919
6920        if !handled_declarator {
6921            if in_class_body {
6922                self.visit_class_members_from_declaration(node, scope, ancestry);
6923            } else {
6924                self.visit_global_variables_from_declaration(node, scope, ancestry);
6925            }
6926        }
6927    }
6928
6929    /// Preserve the member structure of an anonymous C aggregate.
6930    ///
6931    /// An anonymous union with no declarator promotes its fields into the
6932    /// containing aggregate. An anonymous struct/union followed by a named
6933    /// declarator, such as `struct { T *ops; } sock`, declares both the field
6934    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
6935    /// the declarator's structured nested identity so a later `value.sock.ops`
6936    /// chain can traverse it without parsing a type spelling (#2407).
6937    fn visit_c_anonymous_aggregate_declaration<'tree>(
6938        &mut self,
6939        node: Node<'tree>,
6940        scope: &ScopeInfo,
6941        in_class_body: bool,
6942        stack: &mut Vec<CppWork<'tree>>,
6943        ancestry: &ParentIndex<'tree>,
6944    ) -> bool {
6945        if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
6946            return false;
6947        }
6948        let Some(aggregate) = node.child_by_field_name("type") else {
6949            return false;
6950        };
6951        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
6952            || aggregate.child_by_field_name("name").is_some()
6953        {
6954            return false;
6955        }
6956        let Some(body) = cpp_body_node(aggregate) else {
6957            return false;
6958        };
6959
6960        let mut cursor = node.walk();
6961        let declarators = node
6962            .children_by_field_name("declarator", &mut cursor)
6963            .filter_map(|declarator| match classify_declarator(declarator) {
6964                Some(DeclaratorKind::Variable(variable)) => Some(variable),
6965                Some(DeclaratorKind::Function(_)) | None => None,
6966            })
6967            .collect::<Vec<_>>();
6968        if declarators.is_empty() {
6969            stack.push(CppWork::Container(CppContainer {
6970                node: body,
6971                scope: scope.clone(),
6972            }));
6973            return true;
6974        }
6975
6976        for declarator in declarators {
6977            let Some(name) = extract_variable_name(declarator, self.source) else {
6978                continue;
6979            };
6980            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6981            self.visit_named_class_like_shape(
6982                aggregate,
6983                name,
6984                Some(body),
6985                true,
6986                None,
6987                None,
6988                scope,
6989                stack,
6990                ancestry,
6991            );
6992        }
6993        true
6994    }
6995
6996    /// Give a function-local anonymous C aggregate a structured owner so its
6997    /// direct pointer bindings can be typed by the usage graph.  Unlike an
6998    /// anonymous aggregate in a class body, there is no source-level tag or
6999    /// typedef to supply an identity.  The aggregate's CST range is therefore
7000    /// part of a generated, collision-resistant identity; no source spelling
7001    /// is consulted.  The declaration's own variable remains a file-level
7002    /// field projection, while the aggregate body is visited as the generated
7003    /// class's field list.
7004    fn visit_c_anonymous_local_aggregate_declaration<'tree>(
7005        &mut self,
7006        node: Node<'tree>,
7007        scope: &ScopeInfo,
7008        stack: &mut Vec<CppWork<'tree>>,
7009        ancestry: &ParentIndex<'tree>,
7010    ) -> bool {
7011        if !self.c_tag_semantics || scope.class_unit.is_some() || !has_function_scope_ancestor(node)
7012        {
7013            return false;
7014        }
7015        let Some(aggregate) = node.child_by_field_name("type") else {
7016            return false;
7017        };
7018        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7019            || aggregate.child_by_field_name("name").is_some()
7020        {
7021            return false;
7022        }
7023        let Some(body) = cpp_body_node(aggregate) else {
7024            return false;
7025        };
7026        let mut cursor = node.walk();
7027        let declarators = node
7028            .children_by_field_name("declarator", &mut cursor)
7029            .filter_map(|declarator| match classify_declarator(declarator) {
7030                Some(DeclaratorKind::Variable(variable)) => Some(variable),
7031                Some(DeclaratorKind::Function(_)) | None => None,
7032            })
7033            .collect::<Vec<_>>();
7034        if declarators.is_empty() {
7035            return false;
7036        }
7037
7038        for declarator in &declarators {
7039            self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
7040        }
7041        let name = format!("<anonymous:{}>", aggregate.start_byte());
7042        self.visit_named_class_like_shape(
7043            aggregate,
7044            name,
7045            Some(body),
7046            true,
7047            None,
7048            None,
7049            scope,
7050            stack,
7051            ancestry,
7052        );
7053        true
7054    }
7055
7056    fn visit_function_declaration<'tree>(
7057        &mut self,
7058        declaration_node: Node<'tree>,
7059        declarator: Node<'tree>,
7060        scope: &ScopeInfo,
7061        ancestry: &ParentIndex<'tree>,
7062    ) {
7063        let Some(function) = extract_function_info(declarator, self.source, scope) else {
7064            return;
7065        };
7066        let code_unit =
7067            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7068        if self.parsed.contains_declaration(&code_unit) {
7069            self.parsed
7070                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7071            return;
7072        }
7073        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7074        let signature = render_cpp_function_display_signature_from_node(
7075            declaration_node,
7076            self.source,
7077            scope.template_signature.as_deref(),
7078            false,
7079            ancestry,
7080        );
7081        self.parsed.add_signature_with_metadata(
7082            code_unit.clone(),
7083            cpp_signature_metadata(signature, declarator, self.source, ancestry)
7084                .with_declaration_only(true)
7085                .with_callable_linkage(cpp_callable_linkage(
7086                    declaration_node,
7087                    self.source,
7088                    ancestry,
7089                )),
7090        );
7091        if let Some(parent) = &scope.class_unit {
7092            self.parsed.add_child(parent.clone(), code_unit);
7093        } else if let Some(module) = &scope.module {
7094            self.parsed.add_child(module.clone(), code_unit);
7095        }
7096    }
7097
7098    fn visit_recovered_macro_qualified_function_declaration<'tree>(
7099        &mut self,
7100        declaration_node: Node<'tree>,
7101        call: Node<'tree>,
7102        scope: &ScopeInfo,
7103        ancestry: &ParentIndex<'tree>,
7104    ) {
7105        let Some(parent) = &scope.class_unit else {
7106            return;
7107        };
7108        let Some(name_node) = call.child_by_field_name("function") else {
7109            return;
7110        };
7111        let Some(arguments) = call.child_by_field_name("arguments") else {
7112            return;
7113        };
7114        let Some((signature, parameter_labels)) =
7115            recovered_macro_qualified_function_parameters(arguments, self.source)
7116        else {
7117            return;
7118        };
7119        let arity = parameter_labels.len();
7120        let function = FunctionInfo {
7121            package_name: scope.package_name.clone(),
7122            owner: Some(CppMemberOwner::Unit(parent.clone())),
7123            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
7124            signature,
7125        };
7126        if function.name.is_empty() {
7127            return;
7128        }
7129        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7130        if self.parsed.contains_declaration(&code_unit) {
7131            self.parsed
7132                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7133            return;
7134        }
7135        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7136        let signature_label = render_cpp_function_display_signature_from_node(
7137            declaration_node,
7138            self.source,
7139            scope.template_signature.as_deref(),
7140            false,
7141            ancestry,
7142        );
7143        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7144            .with_declaration_only(true)
7145            .with_callable_arity(CallableArity::exact(arity))
7146            .with_callable_linkage(cpp_callable_linkage(
7147                declaration_node,
7148                self.source,
7149                ancestry,
7150            ));
7151        self.parsed
7152            .add_signature_with_metadata(code_unit.clone(), metadata);
7153        self.parsed.add_child(parent.clone(), code_unit);
7154    }
7155
7156    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
7157        &mut self,
7158        declaration_node: Node<'tree>,
7159        call: Node<'tree>,
7160        scope: &ScopeInfo,
7161        ancestry: &ParentIndex<'tree>,
7162    ) {
7163        let Some(parent) = &scope.class_unit else {
7164            return;
7165        };
7166        let Some(arguments) = call.child_by_field_name("arguments") else {
7167            return;
7168        };
7169        let Some((mut signature, parameter_labels)) =
7170            recovered_macro_qualified_function_parameters(arguments, self.source)
7171        else {
7172            return;
7173        };
7174        if let Some(template_signature) = &scope.template_signature {
7175            signature = format!("{template_signature}{signature}");
7176        }
7177        let arity = parameter_labels.len();
7178        let function = FunctionInfo {
7179            package_name: scope.package_name.clone(),
7180            owner: Some(CppMemberOwner::Unit(parent.clone())),
7181            name: parent.identifier().to_string(),
7182            signature,
7183        };
7184        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7185        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7186        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
7187        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7188            .with_declaration_only(false)
7189            .with_callable_arity(CallableArity::exact(arity))
7190            .with_callable_linkage(cpp_callable_linkage(
7191                declaration_node,
7192                self.source,
7193                ancestry,
7194            ));
7195        self.parsed
7196            .add_signature_with_metadata(code_unit.clone(), metadata);
7197        self.parsed.add_child(parent.clone(), code_unit);
7198    }
7199
7200    fn visit_variable_declaration<'tree>(
7201        &mut self,
7202        declaration_node: Node<'tree>,
7203        declarator: Node<'tree>,
7204        scope: &ScopeInfo,
7205        in_class_body: bool,
7206        ancestry: &ParentIndex<'tree>,
7207    ) {
7208        let Some(name) = extract_variable_name(declarator, self.source) else {
7209            return;
7210        };
7211        let parent = if in_class_body {
7212            let Some(parent) = &scope.class_unit else {
7213                return;
7214            };
7215            Some(parent)
7216        } else {
7217            None
7218        };
7219        let short_name = match parent {
7220            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
7221            None => name.clone(),
7222        };
7223        let fq = cpp_leaf_fq(
7224            &scope.package_name,
7225            parent,
7226            &name,
7227            SegmentKind::Member,
7228            SegmentKind::Member,
7229        );
7230        let code_unit = CodeUnit::new_fq(
7231            self.file.clone(),
7232            CodeUnitType::Field,
7233            scope.package_name.clone(),
7234            short_name,
7235            fq,
7236        );
7237        if self.parsed.contains_declaration(&code_unit) {
7238            return;
7239        }
7240        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7241        self.parsed.add_signature_with_metadata(
7242            code_unit.clone(),
7243            SignatureMetadata::new(
7244                render_cpp_field_signature(declaration_node, declarator, self.source),
7245                Vec::new(),
7246            )
7247            .with_cpp_field_linkage(cpp_field_declaration_linkage(
7248                declaration_node,
7249                self.source,
7250                ancestry,
7251            )),
7252        );
7253        if let Some(parent) = &scope.class_unit {
7254            self.parsed.add_child(parent.clone(), code_unit);
7255        } else if let Some(module) = &scope.module {
7256            self.parsed.add_child(module.clone(), code_unit);
7257        }
7258    }
7259
7260    fn visit_class_members_from_declaration<'tree>(
7261        &mut self,
7262        node: Node<'tree>,
7263        scope: &ScopeInfo,
7264        ancestry: &ParentIndex<'tree>,
7265    ) {
7266        let mut cursor = node.walk();
7267        for child in node.named_children(&mut cursor) {
7268            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
7269                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
7270            } else if child.kind() == "init_declarator"
7271                && let Some(inner) = child.child_by_field_name("declarator")
7272            {
7273                self.visit_variable_declaration(node, inner, scope, true, ancestry);
7274            } else if matches!(
7275                child.kind(),
7276                "identifier"
7277                    | "field_identifier"
7278                    | "pointer_declarator"
7279                    | "reference_declarator"
7280                    | "array_declarator"
7281                    | "parenthesized_declarator"
7282            ) {
7283                self.visit_variable_declaration(node, child, scope, true, ancestry);
7284            }
7285        }
7286    }
7287
7288    fn visit_global_variables_from_declaration<'tree>(
7289        &mut self,
7290        node: Node<'tree>,
7291        scope: &ScopeInfo,
7292        ancestry: &ParentIndex<'tree>,
7293    ) {
7294        let mut cursor = node.walk();
7295        for child in node.named_children(&mut cursor) {
7296            if child.kind() == "init_declarator"
7297                && let Some(inner) = child.child_by_field_name("declarator")
7298            {
7299                self.visit_variable_declaration(node, inner, scope, false, ancestry);
7300            } else if matches!(
7301                child.kind(),
7302                "identifier"
7303                    | "field_identifier"
7304                    | "pointer_declarator"
7305                    | "reference_declarator"
7306                    | "array_declarator"
7307                    | "parenthesized_declarator"
7308            ) {
7309                self.visit_variable_declaration(node, child, scope, false, ancestry);
7310            }
7311        }
7312    }
7313
7314    fn visit_type_declaration<'tree>(
7315        &mut self,
7316        node: Node<'tree>,
7317        scope: &ScopeInfo,
7318        stack: &mut Vec<CppWork<'tree>>,
7319        ancestry: &ParentIndex<'tree>,
7320    ) {
7321        let type_node = node.child_by_field_name("type");
7322        if let Some(type_node) = type_node
7323            && matches!(
7324                type_node.kind(),
7325                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7326            )
7327        {
7328            self.visit_class_like(type_node, scope, stack, ancestry);
7329        }
7330
7331        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
7332            let range = Range {
7333                start_byte: node.start_byte(),
7334                end_byte: recovered.end_node.end_byte(),
7335                start_line: node.start_position().row + 1,
7336                end_line: recovered.end_node.end_position().row + 1,
7337            };
7338            let signature = self
7339                .source
7340                .get(range.start_byte..range.end_byte)
7341                .map(normalize_cpp_whitespace)
7342                .unwrap_or_default();
7343            self.record_type_aliases(
7344                node,
7345                scope,
7346                vec![recovered.name],
7347                signature,
7348                range,
7349                ancestry,
7350            );
7351            return;
7352        }
7353
7354        let alias_names = match node.kind() {
7355            "alias_declaration" => extract_alias_declaration_name(node, self.source)
7356                .into_iter()
7357                .collect::<Vec<_>>(),
7358            "type_definition" => extract_typedef_alias_names(node, self.source),
7359            _ => Vec::new(),
7360        };
7361        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
7362            (type_node, alias_names.as_slice())
7363            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
7364            && type_node.child_by_field_name("name").is_none()
7365        {
7366            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
7367        } else {
7368            None
7369        };
7370        self.add_type_aliases(node, scope, alias_names, ancestry);
7371        if let Some((body, alias_name)) = anonymous_aggregate {
7372            // The typedef alias is also the only user-visible identity of an
7373            // anonymous aggregate. Reuse it as the member owner instead of
7374            // minting a second signatureless class with the same FQN. The
7375            // latter makes forward lookup ambiguous when conditional aliases
7376            // coexist and returns duplicate definitions even without guards.
7377            let signature = normalize_cpp_whitespace(node_text(node, self.source));
7378            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
7379            debug_assert!(self.parsed.contains_declaration(&alias_unit));
7380            let mut nested_scope = scope.clone();
7381            nested_scope.class_unit = Some(alias_unit);
7382            nested_scope.template_signature = scope.template_signature.clone();
7383            nested_scope.template_metadata = None;
7384            nested_scope.declarations_are_fields = false;
7385            nested_scope.recovered_specialization_member_scope = false;
7386            stack.push(CppWork::Container(CppContainer {
7387                node: body,
7388                scope: nested_scope,
7389            }));
7390        }
7391    }
7392
7393    fn add_type_aliases(
7394        &mut self,
7395        node: Node<'_>,
7396        scope: &ScopeInfo,
7397        alias_names: Vec<String>,
7398        ancestry: &ParentIndex<'_>,
7399    ) {
7400        let signature = normalize_cpp_whitespace(node_text(node, self.source));
7401        self.record_type_aliases(
7402            node,
7403            scope,
7404            alias_names,
7405            signature,
7406            cpp_declaration_range(node),
7407            ancestry,
7408        );
7409    }
7410
7411    fn record_type_aliases(
7412        &mut self,
7413        node: Node<'_>,
7414        scope: &ScopeInfo,
7415        alias_names: Vec<String>,
7416        signature: String,
7417        range: Range,
7418        ancestry: &ParentIndex<'_>,
7419    ) {
7420        if signature.is_empty() {
7421            return;
7422        }
7423        let type_name = node
7424            .child_by_field_name("type")
7425            .and_then(|type_node| type_node.child_by_field_name("name"))
7426            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
7427        for alias_name in alias_names {
7428            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
7429                continue;
7430            }
7431            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
7432            // Declaration identity does not include the alias signature. Keep
7433            // each physical range so conditional aliases retain their guards.
7434            self.add_declaration_with_range(code_unit.clone(), range, None, None);
7435            let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
7436            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
7437                cpp_structured_type_identity(type_node, self.source, &lexical_scope)
7438            });
7439            self.parsed.add_signature_with_metadata(
7440                code_unit.clone(),
7441                SignatureMetadata::new(signature.clone(), Vec::new())
7442                    .with_underlying_type_identity(underlying_type_identity),
7443            );
7444            if let Some(metadata) = &scope.template_metadata {
7445                let mut metadata = metadata.clone();
7446                metadata.primary_fq_name = code_unit.fq_name();
7447                self.parsed
7448                    .set_cpp_template_metadata(code_unit.clone(), metadata);
7449            }
7450            if let Some(parent) = &scope.class_unit {
7451                self.parsed.add_child(parent.clone(), code_unit.clone());
7452            } else if let Some(module) = &scope.module {
7453                self.parsed.add_child(module.clone(), code_unit.clone());
7454            }
7455            self.parsed.mark_type_alias(code_unit);
7456        }
7457    }
7458
7459    fn type_alias_unit(
7460        &self,
7461        scope: &ScopeInfo,
7462        alias_name: String,
7463        signature: String,
7464    ) -> CodeUnit {
7465        let short_name = if let Some(parent) = &scope.class_unit {
7466            cpp_join_nested_short(parent.short_name(), &alias_name)
7467        } else {
7468            alias_name.clone()
7469        };
7470        let fq = cpp_leaf_fq(
7471            &scope.package_name,
7472            scope.class_unit.as_ref(),
7473            &alias_name,
7474            SegmentKind::Nested,
7475            SegmentKind::Type,
7476        );
7477        CodeUnit::with_signature_and_fq(
7478            self.file.clone(),
7479            CodeUnitType::Class,
7480            scope.package_name.clone(),
7481            short_name,
7482            Some(signature),
7483            false,
7484            fq,
7485        )
7486    }
7487
7488    fn visit_macro(&mut self, node: Node<'_>) {
7489        // A comment can terminate tree-sitter's preproc_arg before the logical
7490        // directive ends. Its remaining declarations are replacement locals,
7491        // not file-scope fields, even when recovery exposes them as siblings.
7492        if let Some(replacement) =
7493            crate::graph::syntax::function_macro_replacement_span(node, self.source)
7494        {
7495            self.consumed_fragment_regions
7496                .push((replacement.start, replacement.end));
7497        }
7498        let Some(name) = extract_macro_name(node, self.source) else {
7499            return;
7500        };
7501        let signature = node_text(node, self.source).trim_end().to_string();
7502        if signature.is_empty() {
7503            return;
7504        }
7505        let fq = cpp_member_fq("", &name);
7506        // A macro can be undefined and redefined later in the same file. Its
7507        // structured directive is part of the declaration identity so the
7508        // temporal environment can navigate to the definition active at a
7509        // reference instead of collapsing every spelling to the first range.
7510        // The same physical directive parsed through another C/C++ reading
7511        // still produces the same unit and remains deduplicated.
7512        let code_unit = CodeUnit::with_signature_and_fq(
7513            self.file.clone(),
7514            CodeUnitType::Macro,
7515            "",
7516            name.clone(),
7517            Some(signature.clone()),
7518            false,
7519            fq,
7520        );
7521        if !self.parsed.contains_declaration(&code_unit) {
7522            self.add_declaration(code_unit.clone(), node, None, None);
7523            let name_range = node
7524                .child_by_field_name("name")
7525                .map(cpp_declaration_range)
7526                .unwrap_or_else(|| cpp_declaration_range(node));
7527            self.parsed
7528                .record_materialization(MaterializationRecord::GeneratedDeclaration {
7529                    site: cpp_declaration_range(node),
7530                    argument: name_range,
7531                    kind: GenerationKind::PreprocessorDefinition,
7532                    unit: code_unit.clone(),
7533                });
7534            self.parsed.add_signature(code_unit, signature);
7535        }
7536        if node.kind() == "preproc_def" {
7537            update_object_macro_field_environment(
7538                node,
7539                self.source,
7540                &mut self.object_macro_fields,
7541                &mut self.ambiguous_object_macro_fields,
7542            );
7543        } else {
7544            self.object_macro_fields.remove(&name);
7545            self.ambiguous_object_macro_fields.remove(&name);
7546        }
7547    }
7548
7549    fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7550        let Some(directive) = node.child_by_field_name("directive") else {
7551            return;
7552        };
7553        let name = node_text(directive, self.source).trim();
7554        let range = cpp_declaration_range(node);
7555        let fields = object_macro_field_closure(&self.object_macro_fields, name);
7556        self.materialize_object_macro_fields(fields, range, scope);
7557    }
7558
7559    /// Bare object-like field-list macros inside an otherwise well-formed
7560    /// aggregate remain identifier nodes in a field declaration. More than
7561    /// one adjacent invocation can be folded into the same declaration, so
7562    /// inspect all of its structured identifier nodes.
7563    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7564        if !matches!(node.kind(), "declaration" | "field_declaration") {
7565            return false;
7566        }
7567        // Claiming the declaration means its members are accounted for, which
7568        // is only true when there is an owner to materialize them into. A file
7569        // scope reaches this with a declaration that merely *contains* an
7570        // aggregate spelling an invocation (tree-sitter wraps libuv's
7571        // `struct uv_fs_poll_s { UV_HANDLE_FIELDS ... }` in a top-level
7572        // `field_declaration` under a malformed header), and consuming it there
7573        // would discard the aggregate itself (#2985).
7574        if scope.class_unit.is_none() {
7575            return false;
7576        }
7577        let macro_nodes =
7578            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7579        for macro_node in &macro_nodes {
7580            let name = node_text(*macro_node, self.source).trim();
7581            let fields = object_macro_field_closure(&self.object_macro_fields, name);
7582            self.materialize_object_macro_fields(fields, cpp_declaration_range(*macro_node), scope);
7583        }
7584        let Some(last) = macro_nodes.last() else {
7585            return false;
7586        };
7587        // Claiming the declaration accounts for every member it spells, not
7588        // only the donated ones: an invocation folded together with the
7589        // members after it (libuv's `UV_HANDLE_FIELDS ... void* poll_ctx;`)
7590        // leaves those members shapeless, and the grammar recovers them from
7591        // the byte range that follows the invocation.
7592        self.record_collapsed_aggregate_fields(
7593            last.end_byte()..node.end_byte(),
7594            node.start_position().row
7595                + 1
7596                + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
7597            scope,
7598        );
7599        true
7600    }
7601
7602    fn materialize_object_macro_fields(
7603        &mut self,
7604        fields: Vec<MacroReplacementField>,
7605        range: Range,
7606        scope: &ScopeInfo,
7607    ) {
7608        let Some(owner) = scope.class_unit.as_ref() else {
7609            return;
7610        };
7611        for field in fields {
7612            let signature = field.declaration.clone();
7613            let mut fq = owner.fq().clone();
7614            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7615            let short_name = if owner.short_name().is_empty() {
7616                field.name.clone()
7617            } else {
7618                format!("{}.{}", owner.short_name(), field.name)
7619            };
7620            let code_unit = CodeUnit::with_signature_and_fq(
7621                self.file.clone(),
7622                CodeUnitType::Field,
7623                owner.package_name().to_string(),
7624                short_name,
7625                Some(field.declaration),
7626                true,
7627                fq,
7628            );
7629            if self.parsed.contains_declaration(&code_unit) {
7630                continue;
7631            }
7632            self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7633            self.parsed.add_signature(code_unit, signature);
7634        }
7635    }
7636
7637    /// Recover ordinary aggregates whose field-list macro invocations made
7638    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
7639    /// The class markers, aggregate braces, field nodes, and macro identifier
7640    /// nodes are all read from the AST; no source delimiter or token scan is
7641    /// used. A close-brace node inside a malformed field closes the innermost
7642    /// recovered aggregate, preserving nested-owner identity.
7643    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7644        let mut cursor = node.walk();
7645        let children = node.children(&mut cursor).collect::<Vec<_>>();
7646        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember>)>::new();
7647        let mut object_macro_fields = self.object_macro_fields.clone();
7648        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
7649        let mut open = Vec::<usize>::new();
7650        let mut index = 0;
7651        while index < children.len() {
7652            let keyword = children[index];
7653            if update_object_macro_field_environment(
7654                keyword,
7655                self.source,
7656                &mut object_macro_fields,
7657                &mut ambiguous_object_macro_fields,
7658            ) {
7659                index += 1;
7660                continue;
7661            }
7662            if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
7663                let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7664                if !name.is_empty() {
7665                    let parent = open
7666                        .last()
7667                        .and_then(|class| recovered.get(*class))
7668                        .map(|(owner, _, _, _)| owner.clone())
7669                        .or_else(|| scope.class_unit.clone());
7670                    let short_name = parent.as_ref().map_or_else(
7671                        || name.clone(),
7672                        |parent| cpp_join_nested_short(parent.short_name(), &name),
7673                    );
7674                    let fq = cpp_leaf_fq(
7675                        &scope.package_name,
7676                        parent.as_ref(),
7677                        &name,
7678                        SegmentKind::Nested,
7679                        SegmentKind::Type,
7680                    );
7681                    let owner = CodeUnit::with_signature_and_fq(
7682                        self.file.clone(),
7683                        CodeUnitType::Class,
7684                        scope.package_name.clone(),
7685                        short_name,
7686                        None,
7687                        false,
7688                        fq,
7689                    );
7690                    recovered.push((
7691                        owner,
7692                        head.key.start_byte(),
7693                        head.opening.end_byte(),
7694                        Vec::new(),
7695                    ));
7696                    let class_index = recovered.len() - 1;
7697                    match head.folded_members {
7698                        // The ordinary shape leaves the aggregate open: every
7699                        // child that follows belongs to it until a close brace
7700                        // ends it.
7701                        None => open.push(class_index),
7702                        // A folded head carries its own member list and its own
7703                        // closing brace, so it closes here. The invocations that
7704                        // precede the class key are the dangling tail of the
7705                        // replacement that folded it, and belong to whatever
7706                        // aggregate is open around it, not to this one.
7707                        Some(members) => {
7708                            let macro_nodes = object_macro_identifier_nodes_with_environment(
7709                                children[index],
7710                                self.source,
7711                                &mut object_macro_fields,
7712                                &mut ambiguous_object_macro_fields,
7713                            );
7714                            let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7715                                .iter()
7716                                .partition(|node| node.start_byte() < head.key.start_byte());
7717                            if let Some(&enclosing) = open.last() {
7718                                for macro_node in preceding {
7719                                    recovered[enclosing]
7720                                        .3
7721                                        .push(CppCollapsedMember::MacroFields {
7722                                            range: cpp_declaration_range(macro_node),
7723                                            fields: object_macro_field_closure(
7724                                                &object_macro_fields,
7725                                                &normalize_cpp_whitespace(node_text(
7726                                                    macro_node,
7727                                                    self.source,
7728                                                )),
7729                                            ),
7730                                        });
7731                                }
7732                            }
7733                            let closing = cpp_collapsed_aggregate_closing_brace(members);
7734                            recovered[class_index]
7735                                .3
7736                                .extend(cpp_collapsed_aggregate_members(
7737                                    &inner,
7738                                    head.opening.end_byte()..closing,
7739                                    head.opening.end_position().row + 1,
7740                                    self.source,
7741                                    &object_macro_fields,
7742                                ));
7743                            recovered[class_index].2 = members.end_byte();
7744                            for &open_class in &open {
7745                                recovered[open_class].2 =
7746                                    recovered[open_class].2.max(members.end_byte());
7747                            }
7748                        }
7749                    }
7750                    index += head.width;
7751                    continue;
7752                }
7753            }
7754            if let Some(&class_index) = open.last()
7755                && children[index].kind() == "field_declaration"
7756            {
7757                let field = children[index];
7758                let macro_nodes = object_macro_identifier_nodes_with_environment(
7759                    field,
7760                    self.source,
7761                    &mut object_macro_fields,
7762                    &mut ambiguous_object_macro_fields,
7763                );
7764                recovered[class_index]
7765                    .3
7766                    .extend(cpp_collapsed_aggregate_members(
7767                        &macro_nodes,
7768                        field.start_byte()..field.end_byte(),
7769                        field.start_position().row + 1,
7770                        self.source,
7771                        &object_macro_fields,
7772                    ));
7773                let end = field.end_byte();
7774                for &open_class in &open {
7775                    recovered[open_class].2 = recovered[open_class].2.max(end);
7776                }
7777                let closes = count_close_brace_nodes(field);
7778                for _ in 0..closes {
7779                    if let Some(closed) = open.pop() {
7780                        recovered[closed].2 = end;
7781                    }
7782                }
7783            }
7784            index += 1;
7785        }
7786
7787        let mut owners = Vec::with_capacity(recovered.len());
7788        for (owner, start, end, members) in recovered {
7789            let parent = owners
7790                .iter()
7791                .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
7792                .cloned()
7793                .or_else(|| scope.class_unit.clone());
7794            self.declare_collapsed_aggregate(owner.clone(), start..end, parent, members, scope);
7795            owners.push(owner);
7796        }
7797    }
7798
7799    /// Record one recovered aggregate and the members it declares.
7800    fn declare_collapsed_aggregate(
7801        &mut self,
7802        owner: CodeUnit,
7803        span: std::ops::Range<usize>,
7804        parent: Option<CodeUnit>,
7805        members: Vec<CppCollapsedMember>,
7806        scope: &ScopeInfo,
7807    ) {
7808        let range = Range {
7809            start_byte: span.start,
7810            end_byte: span.end,
7811            start_line: self.source.get(..span.start).map_or(1, |source| {
7812                source.bytes().filter(|byte| *byte == b'\n').count() + 1
7813            }),
7814            end_line: self.source.get(..span.end).map_or(1, |source| {
7815                source.bytes().filter(|byte| *byte == b'\n').count() + 1
7816            }),
7817        };
7818        // The tag can already be declared by a typedef or a forward
7819        // declaration, and this range is the definition's. Record it either
7820        // way: a declaration carries every range it is written at, and
7821        // dropping this one leaves nothing able to prove that the aggregate
7822        // has a body (#3098). Recording a range that is already held, or a
7823        // unit that is already declared, changes nothing.
7824        self.add_declaration_with_range(owner.clone(), range, parent, None);
7825        let owner_scope = ScopeInfo {
7826            class_unit: Some(owner),
7827            declarations_are_fields: true,
7828            ..scope.clone()
7829        };
7830        for member in members {
7831            match member {
7832                CppCollapsedMember::MacroFields { range, fields } => {
7833                    self.materialize_object_macro_fields(fields, range, &owner_scope);
7834                }
7835                CppCollapsedMember::Declarations { span, start_line } => {
7836                    self.record_collapsed_aggregate_fields(span, start_line, &owner_scope);
7837                }
7838            }
7839        }
7840    }
7841
7842    /// Recover an aggregate whose head tree-sitter folded into the declaration
7843    /// that precedes it, outside any collapsed `ERROR` container.
7844    ///
7845    /// A comment inside a field-list macro's replacement ends the replacement
7846    /// token, so the rest of the replacement is read at file scope. When the
7847    /// aggregate that follows declares its member list with one invocation,
7848    /// the parser reads the dangling tail as that declaration's type, the
7849    /// class key as an `ERROR`, and the member list as an initializer (#3098).
7850    /// The invocations before the class key belong to the replacement, not to
7851    /// this aggregate, so they go to whatever aggregate encloses it.
7852    fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7853        if node.parent().is_some_and(|parent| parent.is_error()) {
7854            // The container's own recovery reads this child in source order,
7855            // with the preprocessor environment its earlier children build.
7856            return false;
7857        }
7858        let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
7859            return false;
7860        };
7861        let members = head
7862            .folded_members
7863            .expect("a folded aggregate head carries its member list");
7864        let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7865        if name.is_empty() {
7866            return false;
7867        }
7868        let macro_nodes =
7869            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7870        let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7871            .iter()
7872            .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
7873        for macro_node in preceding {
7874            let fields = object_macro_field_closure(
7875                &self.object_macro_fields,
7876                &normalize_cpp_whitespace(node_text(macro_node, self.source)),
7877            );
7878            self.materialize_object_macro_fields(fields, cpp_declaration_range(macro_node), scope);
7879        }
7880        let parent = scope.class_unit.clone();
7881        let short_name = parent.as_ref().map_or_else(
7882            || name.clone(),
7883            |parent| cpp_join_nested_short(parent.short_name(), &name),
7884        );
7885        let fq = cpp_leaf_fq(
7886            &scope.package_name,
7887            parent.as_ref(),
7888            &name,
7889            SegmentKind::Nested,
7890            SegmentKind::Type,
7891        );
7892        let owner = CodeUnit::with_signature_and_fq(
7893            self.file.clone(),
7894            CodeUnitType::Class,
7895            scope.package_name.clone(),
7896            short_name,
7897            None,
7898            false,
7899            fq,
7900        );
7901        let recovered = cpp_collapsed_aggregate_members(
7902            &inner,
7903            head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
7904            head.opening.end_position().row + 1,
7905            self.source,
7906            &self.object_macro_fields,
7907        );
7908        self.declare_collapsed_aggregate(
7909            owner,
7910            head.key.start_byte()..members.end_byte(),
7911            parent,
7912            recovered,
7913            scope,
7914        );
7915        true
7916    }
7917
7918    /// Record the members of a collapsed aggregate region as fields of its
7919    /// recovered owner. The names, declaration text, and byte ranges all come
7920    /// from the grammar's reading of the region, so a region that is not a
7921    /// member list contributes nothing.
7922    fn record_collapsed_aggregate_fields(
7923        &mut self,
7924        span: std::ops::Range<usize>,
7925        start_line: usize,
7926        scope: &ScopeInfo,
7927    ) {
7928        let Some(owner) = scope.class_unit.as_ref() else {
7929            return;
7930        };
7931        for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
7932            let range = Range {
7933                start_byte: field.range.start,
7934                end_byte: field.range.end,
7935                start_line: start_line
7936                    + cpp_line_breaks_between(self.source, span.start, field.range.start),
7937                end_line: start_line
7938                    + cpp_line_breaks_between(self.source, span.start, field.range.end),
7939            };
7940            let mut fq = owner.fq().clone();
7941            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7942            let short_name = if owner.short_name().is_empty() {
7943                field.name.clone()
7944            } else {
7945                format!("{}.{}", owner.short_name(), field.name)
7946            };
7947            let signature = normalize_cpp_whitespace(&field.declaration);
7948            let code_unit = CodeUnit::with_signature_and_fq(
7949                self.file.clone(),
7950                CodeUnitType::Field,
7951                owner.package_name().to_string(),
7952                short_name,
7953                Some(signature.clone()),
7954                false,
7955                fq,
7956            );
7957            if self.parsed.contains_declaration(&code_unit) {
7958                continue;
7959            }
7960            self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7961            self.parsed.add_signature(code_unit, signature);
7962        }
7963    }
7964
7965    fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7966        let Some(_directive) = node.child_by_field_name("directive") else {
7967            return;
7968        };
7969        if is_cpp_undef_directive(node, self.source) {
7970            update_object_macro_field_environment(
7971                node,
7972                self.source,
7973                &mut self.object_macro_fields,
7974                &mut self.ambiguous_object_macro_fields,
7975            );
7976            return;
7977        }
7978        let directly_in_field_list = node
7979            .parent()
7980            .is_some_and(|parent| parent.kind() == "field_declaration_list");
7981        if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
7982            self.visit_object_macro_fields(node, scope);
7983        }
7984    }
7985}
7986
7987/// Every member a field-list macro contributes under `environment`, including
7988/// the members of the field-list macros it composes.
7989///
7990/// Composition is resolved at the invocation, not at the definition, because a
7991/// nested name's active replacement is a property of the environment the
7992/// invocation sees. The traversal is iterative and visits each name once, so a
7993/// self-composing or mutually composing pair terminates.
7994fn object_macro_field_closure(
7995    environment: &HashMap<String, ObjectMacroReplacement>,
7996    name: &str,
7997) -> Vec<MacroReplacementField> {
7998    let mut fields = Vec::new();
7999    let mut visited = HashSet::default();
8000    let mut stack = vec![name.to_string()];
8001    while let Some(current) = stack.pop() {
8002        if !visited.insert(current.clone()) {
8003            continue;
8004        }
8005        let Some(replacement) = environment.get(&current) else {
8006            continue;
8007        };
8008        fields.extend(replacement.fields.iter().cloned());
8009        stack.extend(replacement.nested.iter().rev().cloned());
8010    }
8011    fields
8012}
8013
8014/// The structured replacement an object-like `#define` contributes.
8015///
8016/// The directive's whole logical line is the replacement list, which
8017/// [`object_macro_replacement_span`] recovers, because a comment inside the
8018/// replacement ends tree-sitter's `preproc_arg` token early.
8019fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
8020    crate::graph::syntax::object_macro_replacement_span(node, source)
8021        .and_then(|span| source.get(span))
8022        .map(crate::graph::syntax::object_macro_replacement)
8023        .unwrap_or_default()
8024}
8025
8026/// Apply one preprocessor directive to an object-like field-list environment.
8027/// The environment is intentionally separate from the declaration visitor so
8028/// malformed parser regions can replay directives in source order without
8029/// mutating the live walk's state ahead of those directives.
8030fn update_object_macro_field_environment(
8031    node: Node<'_>,
8032    source: &str,
8033    fields: &mut HashMap<String, ObjectMacroReplacement>,
8034    ambiguous: &mut HashSet<String>,
8035) -> bool {
8036    match node.kind() {
8037        "preproc_def" => {
8038            let Some(name) = extract_macro_name(node, source) else {
8039                return false;
8040            };
8041            let replacement = object_macro_replacement_of(node, source);
8042            if replacement.is_empty() || ambiguous.contains(&name) {
8043                fields.remove(&name);
8044                ambiguous.insert(name);
8045            } else if let Some(previous) = fields.get(&name) {
8046                if previous != &replacement {
8047                    fields.remove(&name);
8048                    ambiguous.insert(name);
8049                }
8050            } else {
8051                fields.insert(name, replacement);
8052            }
8053            true
8054        }
8055        "preproc_call" if is_cpp_undef_directive(node, source) => {
8056            if let Some(argument) = node.child_by_field_name("argument") {
8057                let name = node_text(argument, source).trim();
8058                fields.remove(name);
8059                if inside_preprocessor_conditional(node) {
8060                    ambiguous.insert(name.to_string());
8061                } else {
8062                    ambiguous.remove(name);
8063                }
8064            }
8065            true
8066        }
8067        _ => false,
8068    }
8069}
8070
8071fn object_macro_identifier_nodes<'tree>(
8072    node: Node<'tree>,
8073    source: &str,
8074    fields: &HashMap<String, ObjectMacroReplacement>,
8075) -> Vec<Node<'tree>> {
8076    let mut result = Vec::new();
8077    let mut stack = vec![node];
8078    while let Some(current) = stack.pop() {
8079        if matches!(
8080            current.kind(),
8081            "identifier" | "field_identifier" | "type_identifier"
8082        ) && fields.contains_key(node_text(current, source).trim())
8083        {
8084            result.push(current);
8085        }
8086        let mut cursor = current.walk();
8087        let children = current.children(&mut cursor).collect::<Vec<_>>();
8088        stack.extend(children.into_iter().rev());
8089    }
8090    result.sort_by_key(|node| node.start_byte());
8091    result
8092}
8093
8094/// Collect macro identifiers while replaying any nested preprocessor
8095/// directives in source order. Recovery regions can contain a later `#undef`
8096/// in the same parser error envelope; using the live visitor map for the
8097/// entire envelope would incorrectly materialize invocations after it.
8098fn object_macro_identifier_nodes_with_environment<'tree>(
8099    node: Node<'tree>,
8100    source: &str,
8101    fields: &mut HashMap<String, ObjectMacroReplacement>,
8102    ambiguous: &mut HashSet<String>,
8103) -> Vec<Node<'tree>> {
8104    let mut result = Vec::new();
8105    let mut stack = vec![node];
8106    while let Some(current) = stack.pop() {
8107        if update_object_macro_field_environment(current, source, fields, ambiguous) {
8108            continue;
8109        }
8110        if matches!(
8111            current.kind(),
8112            "identifier" | "field_identifier" | "type_identifier"
8113        ) && fields.contains_key(node_text(current, source).trim())
8114        {
8115            result.push(current);
8116        }
8117        let mut cursor = current.walk();
8118        let children = current.children(&mut cursor).collect::<Vec<_>>();
8119        stack.extend(children.into_iter().rev());
8120    }
8121    result.sort_by_key(|node| node.start_byte());
8122    result
8123}
8124
8125/// One member region of an aggregate whose head and body tree-sitter collapsed
8126/// into an `ERROR` container, in source order.
8127enum CppCollapsedMember {
8128    /// The members a field-list macro invocation donates, all sharing the
8129    /// invocation's range.
8130    MacroFields {
8131        range: Range,
8132        fields: Vec<MacroReplacementField>,
8133    },
8134    /// A byte span whose ordinary member declarations lost their shape with
8135    /// the aggregate body, with the 1-based line the span starts on.
8136    Declarations {
8137        span: std::ops::Range<usize>,
8138        start_line: usize,
8139    },
8140}
8141
8142/// One aggregate head recovered from the children of a collapsed `ERROR`
8143/// container.
8144///
8145/// A member list that opens with an object-like field-list macro invocation
8146/// has no grammar rule, so the aggregate never forms a specifier and its head
8147/// reaches the tree as loose children. Two shapes carry it. Ordinarily the
8148/// class key, the name, and the opening brace are three consecutive children.
8149/// When the aggregate follows the dangling tail of a comment-truncated
8150/// replacement, the parser reads that tail as the declaration's type and folds
8151/// the whole aggregate into it: libuv's `UV_HANDLE_PRIVATE_FIELDS` line ends
8152/// the `UV_HANDLE_FIELDS` replacement token, and the `struct uv_handle_s { ...
8153/// }` that follows becomes one `field_declaration` whose class key is an
8154/// `ERROR`, whose name is a `field_identifier`, and whose member list is an
8155/// `initializer_list` (#3098).
8156struct CppCollapsedAggregateHead<'tree> {
8157    key: Node<'tree>,
8158    name: Node<'tree>,
8159    opening: Node<'tree>,
8160    /// The member list when the head folded it into its own node, which also
8161    /// carries the aggregate's closing brace. `None` leaves the aggregate open
8162    /// for the children that follow it.
8163    folded_members: Option<Node<'tree>>,
8164    /// How many children the head consumes.
8165    width: usize,
8166}
8167
8168/// The aggregate head that starts at `children[index]`, if one does.
8169fn cpp_collapsed_aggregate_head<'tree>(
8170    children: &[Node<'tree>],
8171    index: usize,
8172    source: &str,
8173) -> Option<CppCollapsedAggregateHead<'tree>> {
8174    let key = *children.get(index)?;
8175    if matches!(key.kind(), "struct" | "class" | "union") {
8176        let name = *children.get(index + 1)?;
8177        let opening = *children.get(index + 2)?;
8178        if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
8179            return None;
8180        }
8181        return Some(CppCollapsedAggregateHead {
8182            key,
8183            name,
8184            opening,
8185            folded_members: None,
8186            width: 3,
8187        });
8188    }
8189    cpp_folded_aggregate_head(key, source)
8190}
8191
8192/// The aggregate head tree-sitter folded into one declaration node, reading
8193/// the class key as an `ERROR` and the member list as an initializer.
8194fn cpp_folded_aggregate_head<'tree>(
8195    node: Node<'tree>,
8196    source: &str,
8197) -> Option<CppCollapsedAggregateHead<'tree>> {
8198    if !matches!(node.kind(), "declaration" | "field_declaration") {
8199        return None;
8200    }
8201    let mut cursor = node.walk();
8202    let children = node.children(&mut cursor).collect::<Vec<_>>();
8203    let key_index = children.iter().position(|child| {
8204        child.is_error()
8205            && matches!(
8206                node_text(*child, source).trim(),
8207                "struct" | "class" | "union"
8208            )
8209    })?;
8210    let declarator = *children.get(key_index + 1)?;
8211    // At file scope the grammar has an `init_declarator` for the name and its
8212    // initializer; inside an `ERROR` container the two are loose siblings.
8213    let (name, members) = if declarator.kind() == "init_declarator" {
8214        (
8215            declarator.child_by_field_name("declarator")?,
8216            declarator.child_by_field_name("value")?,
8217        )
8218    } else {
8219        (declarator, *children.get(key_index + 2)?)
8220    };
8221    if !matches!(
8222        name.kind(),
8223        "field_identifier" | "type_identifier" | "identifier"
8224    ) {
8225        return None;
8226    }
8227    if members.kind() != "initializer_list" {
8228        return None;
8229    }
8230    let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
8231    Some(CppCollapsedAggregateHead {
8232        key: children[key_index],
8233        name,
8234        opening,
8235        folded_members: Some(members),
8236        width: 1,
8237    })
8238}
8239
8240/// Where a folded member list ends: at its own closing brace when it has one,
8241/// so the reparse of the declarations it holds sees a member list and not a
8242/// stray brace.
8243fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
8244    let mut cursor = members.walk();
8245    members
8246        .children(&mut cursor)
8247        .filter(|child| child.kind() == "}" && !child.is_missing())
8248        .last()
8249        .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
8250}
8251
8252/// The members one collapsed aggregate region declares, in source order: what
8253/// its field-list macro invocations donate, then the ordinary declarations
8254/// that follow the last invocation and lost their shape with the body.
8255fn cpp_collapsed_aggregate_members(
8256    macro_nodes: &[Node<'_>],
8257    region: std::ops::Range<usize>,
8258    region_start_line: usize,
8259    source: &str,
8260    environment: &HashMap<String, ObjectMacroReplacement>,
8261) -> Vec<CppCollapsedMember> {
8262    let mut members = macro_nodes
8263        .iter()
8264        .map(|macro_node| CppCollapsedMember::MacroFields {
8265            range: cpp_declaration_range(*macro_node),
8266            fields: object_macro_field_closure(
8267                environment,
8268                &normalize_cpp_whitespace(node_text(*macro_node, source)),
8269            ),
8270        })
8271        .collect::<Vec<_>>();
8272    // Whatever follows the last invocation is an ordinary member list that
8273    // lost its declaration shape with the aggregate's body; the grammar
8274    // recovers it from its own byte range.
8275    let declarations_start = macro_nodes
8276        .last()
8277        .map_or(region.start, |macro_node| macro_node.end_byte());
8278    members.push(CppCollapsedMember::Declarations {
8279        span: declarations_start..region.end,
8280        start_line: region_start_line
8281            + cpp_line_breaks_between(source, region.start, declarations_start),
8282    });
8283    members
8284}
8285
8286/// Line breaks in `source[from..to]`, for mapping a recovered byte offset to a
8287/// line without rescanning the file from its start.
8288fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
8289    source.get(from..to).map_or(0, |slice| {
8290        slice.bytes().filter(|byte| *byte == b'\n').count()
8291    })
8292}
8293
8294/// How many enclosing aggregates a collapsed member declaration closes: the
8295/// close braces it carries that its own braces do not balance. A member that
8296/// declares a nested aggregate of its own (`union { ... } active_reqs;` in
8297/// libuv's `uv_loop_s`) closes nothing, so counting every `}` node ended the
8298/// owner early and lost every member after it (#2985).
8299fn count_close_brace_nodes(node: Node<'_>) -> usize {
8300    let mut opened = 0usize;
8301    let mut closed = 0usize;
8302    let mut stack = vec![node];
8303    while let Some(current) = stack.pop() {
8304        if !current.is_missing() {
8305            match current.kind() {
8306                "{" => opened += 1,
8307                "}" => closed += 1,
8308                _ => {}
8309            }
8310        }
8311        let mut cursor = current.walk();
8312        stack.extend(current.children(&mut cursor));
8313    }
8314    closed.saturating_sub(opened)
8315}
8316
8317/// Classify a C++ field while its declaration syntax is already available.
8318///
8319/// The persisted result lets later visibility queries avoid reparsing the
8320/// complete source file only to recover linkage.
8321pub fn cpp_field_declaration_linkage<'tree>(
8322    declaration: Node<'tree>,
8323    source: &str,
8324    ancestry: &ParentIndex<'tree>,
8325) -> CppFieldLinkage {
8326    let mut current = ancestry.parent(declaration);
8327    let mut enclosed_by_class = false;
8328    while let Some(node) = current {
8329        if node.kind() == "namespace_definition"
8330            && node
8331                .child_by_field_name("name")
8332                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8333        {
8334            return CppFieldLinkage::Internal;
8335        }
8336        if matches!(
8337            node.kind(),
8338            "class_specifier" | "struct_specifier" | "union_specifier"
8339        ) && node
8340            .child_by_field_name("name")
8341            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8342        {
8343            return CppFieldLinkage::Internal;
8344        }
8345        if matches!(
8346            node.kind(),
8347            "class_specifier" | "struct_specifier" | "union_specifier"
8348        ) {
8349            enclosed_by_class = true;
8350        }
8351        if matches!(node.kind(), "function_definition" | "lambda_expression") {
8352            return CppFieldLinkage::Internal;
8353        }
8354        current = ancestry.parent(node);
8355    }
8356    if enclosed_by_class {
8357        return CppFieldLinkage::External;
8358    }
8359    let mut cursor = declaration.walk();
8360    let mut has_static = false;
8361    let mut has_extern = false;
8362    let mut has_inline = false;
8363    let mut has_const = false;
8364    let mut has_constexpr = false;
8365    for child in declaration.named_children(&mut cursor) {
8366        let text = normalize_cpp_whitespace(node_text(child, source));
8367        match (child.kind(), text.as_str()) {
8368            ("storage_class_specifier", "static") => has_static = true,
8369            ("storage_class_specifier", "extern") => has_extern = true,
8370            ("storage_class_specifier", "inline") => has_inline = true,
8371            ("storage_class_specifier", "constexpr") => has_constexpr = true,
8372            ("type_qualifier", "const") => has_const = true,
8373            ("type_qualifier", "constexpr") => has_constexpr = true,
8374            _ => {}
8375        }
8376    }
8377    if has_static {
8378        CppFieldLinkage::Internal
8379    } else if has_extern || has_inline {
8380        CppFieldLinkage::External
8381    } else if has_const || has_constexpr {
8382        CppFieldLinkage::InternalUnlessExternalPeer
8383    } else {
8384        CppFieldLinkage::External
8385    }
8386}
8387
8388fn cpp_declaration_range(node: Node<'_>) -> Range {
8389    Range {
8390        start_byte: node.start_byte(),
8391        end_byte: node.end_byte(),
8392        start_line: node.start_position().row + 1,
8393        end_line: node.end_position().row + 1,
8394    }
8395}
8396
8397/// A recovery interval as a [`Range`], for materialization records whose
8398/// window is a byte region rather than one parser node (the sentinel-macro
8399/// region reparses, issue #941/#1657).
8400fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
8401    let line_at = |byte: usize| {
8402        source.as_bytes()[..byte]
8403            .iter()
8404            .filter(|&&b| b == b'\n')
8405            .count()
8406            + 1
8407    };
8408    Range {
8409        start_byte,
8410        end_byte,
8411        start_line: line_at(start_byte),
8412        end_line: line_at(end_byte),
8413    }
8414}
8415
8416/// Every `#include` directive the tree holds, in source order.
8417///
8418/// A preorder sweep rather than a step of the declaration walk: the container
8419/// walk descends only through declaration scopes, so an include written inside
8420/// a class body or a function body would otherwise never be seen, and an
8421/// include is an include wherever it is written.
8422pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
8423    walk_named_tree_preorder(root, true, |node| {
8424        if node.kind() == "preproc_include" {
8425            let raw = normalize_cpp_whitespace(node_text(node, source));
8426            if !raw.is_empty() {
8427                parsed.imports.push(ImportInfo {
8428                    raw_snippet: raw,
8429                    is_wildcard: false,
8430                    is_global: false,
8431                    identifier: None,
8432                    alias: None,
8433                    path: None,
8434                    binder_span: None,
8435                });
8436            }
8437            return WalkControl::SkipChildren;
8438        }
8439        WalkControl::Continue
8440    });
8441}
8442
8443pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
8444    let mut in_block_comment = false;
8445    for line in source.lines() {
8446        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
8447        let trimmed = stripped.trim();
8448        if !looks_like_quoted_include_line(trimmed) {
8449            continue;
8450        }
8451
8452        let raw = normalize_cpp_whitespace(trimmed);
8453        // The tree-sitter walk already recorded every `#include` it could see;
8454        // this line scan only recovers the ones a parse error hid, so skip a
8455        // snippet that is already an import binding.
8456        if parsed
8457            .imports
8458            .iter()
8459            .any(|import| import.raw_snippet == raw)
8460        {
8461            continue;
8462        }
8463
8464        parsed.imports.push(ImportInfo {
8465            raw_snippet: raw,
8466            is_wildcard: false,
8467            is_global: false,
8468            identifier: None,
8469            alias: None,
8470            path: None,
8471            binder_span: None,
8472        });
8473    }
8474}
8475
8476fn looks_like_quoted_include_line(line: &str) -> bool {
8477    let Some(rest) = line.trim_start().strip_prefix('#') else {
8478        return false;
8479    };
8480    let Some(rest) = rest.trim_start().strip_prefix("include") else {
8481        return false;
8482    };
8483    rest.trim_start().starts_with('"')
8484}
8485
8486fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
8487    let mut raw = Vec::new();
8488    let mut cursor = node.walk();
8489    for child in node.named_children(&mut cursor) {
8490        if child.kind() == "base_class_clause" {
8491            collect_cpp_base_nodes(child, source, &mut raw);
8492        }
8493    }
8494    raw
8495}
8496
8497fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
8498    walk_named_tree_preorder(node, false, |child| match child.kind() {
8499        "type_identifier" | "qualified_identifier" | "template_type" => {
8500            let text = normalize_cpp_whitespace(node_text(child, source));
8501            if !text.is_empty() {
8502                raw.push(text);
8503            }
8504            WalkControl::SkipChildren
8505        }
8506        _ => WalkControl::Continue,
8507    });
8508}
8509
8510fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
8511    let mut out = String::new();
8512    let chars: Vec<char> = line.chars().collect();
8513    let mut index = 0;
8514    let mut in_string = false;
8515    let mut in_char = false;
8516    let mut escape = false;
8517
8518    while index < chars.len() {
8519        let ch = chars[index];
8520        let next = chars.get(index + 1).copied();
8521
8522        if *in_block_comment {
8523            if ch == '*' && next == Some('/') {
8524                *in_block_comment = false;
8525                index += 2;
8526            } else {
8527                index += 1;
8528            }
8529            continue;
8530        }
8531
8532        if in_string {
8533            out.push(ch);
8534            if escape {
8535                escape = false;
8536            } else if ch == '\\' {
8537                escape = true;
8538            } else if ch == '"' {
8539                in_string = false;
8540            }
8541            index += 1;
8542            continue;
8543        }
8544
8545        if in_char {
8546            out.push(ch);
8547            if escape {
8548                escape = false;
8549            } else if ch == '\\' {
8550                escape = true;
8551            } else if ch == '\'' {
8552                in_char = false;
8553            }
8554            index += 1;
8555            continue;
8556        }
8557
8558        if ch == '/' && next == Some('/') {
8559            break;
8560        }
8561        if ch == '/' && next == Some('*') {
8562            *in_block_comment = true;
8563            index += 2;
8564            continue;
8565        }
8566        if ch == '"' {
8567            in_string = true;
8568            out.push(ch);
8569            index += 1;
8570            continue;
8571        }
8572        if ch == '\'' {
8573            in_char = true;
8574            out.push(ch);
8575            index += 1;
8576            continue;
8577        }
8578
8579        out.push(ch);
8580        index += 1;
8581    }
8582
8583    out
8584}
8585
8586#[derive(Clone)]
8587struct FunctionInfo {
8588    package_name: String,
8589    owner: Option<CppMemberOwner>,
8590    name: String,
8591    signature: String,
8592}
8593
8594/// Owner of a member function, kept structured so a literal `$` inside a
8595/// source-level class name never crosses a join/split boundary: the legacy
8596/// `$`-joined owner string was re-split at fq construction, dropping a leading
8597/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
8598/// and tripping the package/short boundary assert -- the #2140 corruption one
8599/// level up (#2362).
8600#[derive(Clone)]
8601enum CppMemberOwner {
8602    /// Source-level owner class chain from a qualified declarator-id, one
8603    /// class name per component (`Outer::Inner::method` -> `["Outer",
8604    /// "Inner"]`); each component may itself contain a literal `$`.
8605    Chain(Vec<String>),
8606    /// The lexically enclosing or recovered class unit; the member fq extends
8607    /// its fq directly instead of re-splitting its `$`-joined short chain.
8608    Unit(CodeUnit),
8609}
8610
8611impl CppMemberOwner {
8612    /// The legacy `$`-joined owner chain used in the member's short name.
8613    fn short_chain(&self) -> String {
8614        match self {
8615            Self::Chain(chain) => chain.join("$"),
8616            Self::Unit(parent) => parent.short_name().to_string(),
8617        }
8618    }
8619}
8620
8621enum DeclaratorKind<'a> {
8622    Function(Node<'a>),
8623    Variable(Node<'a>),
8624}
8625
8626impl FunctionInfo {
8627    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
8628        self.code_unit_with_synthetic(file, false)
8629    }
8630
8631    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
8632        let short_name = match &self.owner {
8633            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
8634            None => self.name.clone(),
8635        };
8636        let fq = match &self.owner {
8637            Some(CppMemberOwner::Chain(chain)) => {
8638                debug_assert!(
8639                    !chain.is_empty(),
8640                    "an empty owner chain is no owner; producers return None instead"
8641                );
8642                let mut fq = FqName::new();
8643                cpp_push_package(&mut fq, &self.package_name);
8644                let mut first = true;
8645                for component in chain {
8646                    let kind = if first {
8647                        SegmentKind::Type
8648                    } else {
8649                        SegmentKind::Nested
8650                    };
8651                    fq.push(cpp_segment(component, kind));
8652                    first = false;
8653                }
8654                fq.push(cpp_segment(&self.name, SegmentKind::Member));
8655                fq
8656            }
8657            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
8658                .fq()
8659                .clone()
8660                .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
8661            // An anonymous parent (empty short chain) contributes no owner
8662            // segment -- the same guard as cpp_join_member_short above.
8663            Some(CppMemberOwner::Unit(_)) | None => {
8664                let mut fq = FqName::new();
8665                cpp_push_package(&mut fq, &self.package_name);
8666                fq.push(cpp_segment(&self.name, SegmentKind::Member));
8667                fq
8668            }
8669        };
8670        CodeUnit::with_signature_and_fq(
8671            file,
8672            CodeUnitType::Function,
8673            self.package_name.clone(),
8674            short_name,
8675            Some(self.signature.clone()),
8676            synthetic,
8677            fq,
8678        )
8679    }
8680}
8681
8682fn extract_function_info(
8683    declarator: Node<'_>,
8684    source: &str,
8685    scope: &ScopeInfo,
8686) -> Option<FunctionInfo> {
8687    let parameters_node = declarator.child_by_field_name("parameters")?;
8688    let declarator_name_node = declarator
8689        .child_by_field_name("declarator")
8690        .or_else(|| parameters_node.prev_named_sibling())?;
8691    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
8692}
8693
8694fn extract_function_info_from_name(
8695    declarator: Node<'_>,
8696    declarator_name_node: Node<'_>,
8697    source: &str,
8698    scope: &ScopeInfo,
8699) -> Option<FunctionInfo> {
8700    let parameters_node = declarator.child_by_field_name("parameters")?;
8701    let parameters_text = cpp_parameter_signature(parameters_node, source);
8702    let recovered_specialization_member = scope
8703        .recovered_specialization_member_scope
8704        .then(|| {
8705            let terminal = declarator_name_node
8706                .child_by_field_name("name")
8707                .unwrap_or(declarator_name_node);
8708            let name = canonical_cpp_qualified_component(terminal, source)?.name;
8709            let owner = scope.class_unit.as_ref()?;
8710            Some((
8711                Some(CppMemberOwner::Unit(owner.clone())),
8712                name,
8713                scope.package_name.clone(),
8714            ))
8715        })
8716        .flatten();
8717    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
8718        parts
8719    } else if let Some(parts) =
8720        split_structured_templated_cpp_name(declarator_name_node, source, scope)
8721    {
8722        parts
8723    } else {
8724        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
8725            declarator_name_node,
8726            source,
8727        )?);
8728        if raw_name.is_empty() {
8729            return None;
8730        }
8731        split_cpp_name(&raw_name, scope)
8732    };
8733    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
8734    let mut signature = if suffix.is_empty() {
8735        parameters_text
8736    } else {
8737        format!("{parameters_text} {suffix}")
8738    };
8739    if let Some(template_signature) = &scope.template_signature {
8740        signature = format!("{template_signature}{signature}");
8741    }
8742
8743    Some(FunctionInfo {
8744        package_name,
8745        owner,
8746        name,
8747        signature,
8748    })
8749}
8750
8751/// Recover the semantic return type and callable name when a declaration macro
8752/// occupies a function definition's `type` field. Tree-sitter either exposes a
8753/// scalar return as the declarator's apparent name and the callable as the sole
8754/// identifier in an `ERROR`, or joins a template return and callable into a
8755/// qualified identifier with a missing `::`. Both shapes retain the complete
8756/// parameter list and body; a concrete separator remains an out-of-line member.
8757fn cpp_macro_displaced_callable_parts<'tree>(
8758    function_declarator: Node<'tree>,
8759    source: &str,
8760    ancestry: &ParentIndex<'tree>,
8761) -> Option<(Node<'tree>, Node<'tree>)> {
8762    let definition = ancestry.parent(function_declarator)?;
8763    if definition.kind() != "function_definition"
8764        || definition.child_by_field_name("declarator") != Some(function_declarator)
8765        || definition
8766            .child_by_field_name("body")
8767            .is_none_or(|body| body.kind() != "compound_statement")
8768    {
8769        return None;
8770    }
8771    let macro_type = definition.child_by_field_name("type")?;
8772    if macro_type.kind() != "type_identifier"
8773        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8774    {
8775        return None;
8776    }
8777
8778    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
8779    if apparent_return_type.kind() == "qualified_identifier"
8780        && let (Some(return_type), Some(callable_name)) = (
8781            apparent_return_type.child_by_field_name("scope"),
8782            apparent_return_type.child_by_field_name("name"),
8783        )
8784        && return_type.kind() == "template_type"
8785        && matches!(callable_name.kind(), "identifier" | "field_identifier")
8786        && (0..apparent_return_type.child_count())
8787            .filter_map(|index| apparent_return_type.child(index))
8788            .any(|child| child.kind() == "::" && child.is_missing())
8789        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
8790        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8791    {
8792        return Some((return_type, callable_name));
8793    }
8794    if !matches!(
8795        apparent_return_type.kind(),
8796        "identifier" | "field_identifier" | "type_identifier"
8797    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
8798    {
8799        return None;
8800    }
8801    let parameters = function_declarator.child_by_field_name("parameters")?;
8802    let mut cursor = function_declarator.walk();
8803    let between = function_declarator
8804        .named_children(&mut cursor)
8805        .filter(|child| child.kind() != "comment")
8806        .filter(|child| {
8807            child.start_byte() >= apparent_return_type.end_byte()
8808                && child.end_byte() <= parameters.start_byte()
8809                && !same_node(*child, apparent_return_type)
8810                && !same_node(*child, parameters)
8811        })
8812        .collect::<Vec<_>>();
8813    let [name_error] = between.as_slice() else {
8814        return None;
8815    };
8816    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
8817        return None;
8818    }
8819    let callable_name = name_error.named_child(0)?;
8820    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
8821        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8822    {
8823        return None;
8824    }
8825    Some((apparent_return_type, callable_name))
8826}
8827
8828/// The part of a `function_declarator` after its parameter list that belongs to
8829/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
8830/// specification, a trailing return type and a trailing requires-clause.
8831///
8832/// The grammar makes each of these a distinct sibling of the `parameters`
8833/// field, so they are read from the tree. Splitting the declarator's text on
8834/// the parameter list instead silently dropped every qualifier whenever the
8835/// parameter list was spelled with whitespace that normalization rewrote - a
8836/// line break or a double space was enough to make a `const` member definition
8837/// a different logical symbol from its declaration (#1827).
8838///
8839/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
8840/// are deliberately excluded. C++ does not make them part of the signature and
8841/// an out-of-line definition never repeats them, so including them would split
8842/// a declaration from its own definition.
8843fn cpp_declarator_identity_suffix(
8844    declarator: Node<'_>,
8845    parameters_node: Node<'_>,
8846    source: &str,
8847) -> String {
8848    let mut cursor = declarator.walk();
8849    let parts = declarator
8850        .named_children(&mut cursor)
8851        .filter(|child| child.start_byte() >= parameters_node.end_byte())
8852        .filter(|child| {
8853            matches!(
8854                child.kind(),
8855                "type_qualifier"
8856                    | "ref_qualifier"
8857                    | "noexcept"
8858                    | "throw_specifier"
8859                    | "trailing_return_type"
8860                    | "requires_clause"
8861            )
8862        })
8863        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8864        .filter(|text| !text.is_empty())
8865        .collect::<Vec<_>>();
8866    normalize_cpp_qualifier_suffix(&parts.join(" "))
8867}
8868
8869/// The identity suffix of one callable declarator, for a consumer that holds
8870/// the declarator rather than the declaration walk's parts.
8871///
8872/// The persisted signature concatenates the parameter spelling and this suffix,
8873/// so a comparison that must agree on the suffix alone recomputes it here
8874/// instead of splitting the stored string.
8875pub(crate) fn cpp_callable_identity_suffix(
8876    function_declarator: Node<'_>,
8877    source: &str,
8878) -> Option<String> {
8879    let parameters_node = function_declarator.child_by_field_name("parameters")?;
8880    Some(cpp_declarator_identity_suffix(
8881        function_declarator,
8882        parameters_node,
8883        source,
8884    ))
8885}
8886
8887pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
8888    match classify_declarator(node)? {
8889        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
8890        DeclaratorKind::Variable(_) => None,
8891    }
8892}
8893
8894fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
8895    match node.kind() {
8896        "function_declarator" => {
8897            let inner = node
8898                .child_by_field_name("declarator")
8899                .or_else(|| node.child_by_field_name("name"))
8900                .or_else(|| last_named_child(node));
8901            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
8902                Some(DeclaratorKind::Variable(node))
8903            } else {
8904                Some(DeclaratorKind::Function(node))
8905            }
8906        }
8907        "init_declarator"
8908        | "pointer_declarator"
8909        | "reference_declarator"
8910        | "parenthesized_declarator"
8911        | "array_declarator"
8912        | "attributed_declarator"
8913        | "template_function" => node
8914            .child_by_field_name("declarator")
8915            .or_else(|| node.child_by_field_name("name"))
8916            .or_else(|| last_named_child(node))
8917            .and_then(classify_declarator),
8918        "identifier" | "field_identifier" | "qualified_identifier" => {
8919            Some(DeclaratorKind::Variable(node))
8920        }
8921        _ => node
8922            .child_by_field_name("declarator")
8923            .or_else(|| node.child_by_field_name("name"))
8924            .or_else(|| last_named_child(node))
8925            .and_then(classify_declarator),
8926    }
8927}
8928
8929fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
8930    matches!(
8931        node.kind(),
8932        "function_declarator"
8933            | "init_declarator"
8934            | "pointer_declarator"
8935            | "reference_declarator"
8936            | "parenthesized_declarator"
8937            | "array_declarator"
8938            | "attributed_declarator"
8939            | "template_function"
8940            | "identifier"
8941            | "field_identifier"
8942            | "qualified_identifier"
8943    )
8944}
8945
8946fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
8947    let class_like = first_class_like_child(node);
8948    let mut cursor = node.walk();
8949    node.named_children(&mut cursor).any(|child| {
8950        matches!(
8951            child.kind(),
8952            "init_declarator"
8953                | "pointer_declarator"
8954                | "reference_declarator"
8955                | "array_declarator"
8956                | "function_declarator"
8957                | "parenthesized_declarator"
8958                | "attributed_declarator"
8959        ) || matches!(
8960            child.kind(),
8961            "identifier" | "field_identifier" | "qualified_identifier"
8962        ) && class_like.is_none_or(|class_node| {
8963            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
8964        })
8965    })
8966}
8967
8968/// One namespace-scope forward class declaration that a recovered export-macro
8969/// class definition may borrow its identity from.  Tree-sitter can close a
8970/// malformed class at the enclosing namespace's closing brace, leaving the later
8971/// class definitions as root-level recovered `function_definition` nodes.  A
8972/// preceding `class Name;` in the same namespace is the only structured identity
8973/// signal available in that shape.
8974///
8975/// Everything recorded here is a property of the forward declaration alone.
8976/// What depends on the node doing the asking -- that the forward and its
8977/// namespace both close before it, with nothing but recovery trivia between --
8978/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
8979/// tree answers every later question about it.
8980struct CppNamespaceForward {
8981    name: String,
8982    start_byte: usize,
8983    /// Where the malformed namespace that held the forward ended.  No query
8984    /// asks anything else about that node.
8985    namespace_end_byte: usize,
8986    package_name: String,
8987}
8988
8989/// Read `node` as a borrowable namespace forward declaration.
8990///
8991/// The admission is deliberately conservative: only a body-less class specifier
8992/// whose declaration has no declarator, at namespace scope rather than inside a
8993/// function or class body, in a namespace that itself failed to parse.
8994fn cpp_namespace_forward_entry<'tree>(
8995    node: Node<'tree>,
8996    source: &str,
8997    ancestry: &ParentIndex<'tree>,
8998) -> Option<CppNamespaceForward> {
8999    if !matches!(
9000        node.kind(),
9001        "class_specifier" | "struct_specifier" | "union_specifier"
9002    ) || cpp_body_node(node).is_some()
9003    {
9004        return None;
9005    }
9006    let parent = node.parent()?;
9007    if !(parent.kind() == "declaration_list"
9008        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
9009    {
9010        return None;
9011    }
9012    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
9013    // Borrowing is only justified by the parser-recovery shape we are
9014    // repairing: the namespace that held the forward must itself contain a
9015    // syntax error. A clean, unrelated namespace forward is not an identity
9016    // proof.
9017    if !namespace.has_error() {
9018        return None;
9019    }
9020    Some(CppNamespaceForward {
9021        name: class_like_name(node, source, ancestry)?,
9022        start_byte: node.start_byte(),
9023        namespace_end_byte: namespace.end_byte(),
9024        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
9025    })
9026}
9027
9028/// Whether `forward` stands in the recovery relation to the node asking about
9029/// it: it and its malformed namespace both closed before the recovered class,
9030/// and nothing but recovery trivia separates the two.
9031fn cpp_namespace_forward_matches_recovery(
9032    forward: &CppNamespaceForward,
9033    recovered_node: Node<'_>,
9034) -> bool {
9035    forward.start_byte < recovered_node.start_byte()
9036        && forward.namespace_end_byte < recovered_node.start_byte()
9037        && malformed_namespace_is_nearest_recovery_region(
9038            forward.namespace_end_byte,
9039            recovered_node,
9040        )
9041}
9042
9043/// What one open [`CppVisitor::record_recovered_declarations`] has watched
9044/// happen to the declaration set.
9045///
9046/// The recovered set used to be a difference against a clone of the whole
9047/// declaration set, taken once per recovery: O(recoveries x declarations) on
9048/// exactly the error-recovered files that already walk slowest (#2787). The
9049/// walk knows which declarations it creates, so the capture collects them as
9050/// they are made and the difference is never needed.
9051///
9052/// `removed_pre_existing` is what makes that equal to the difference. A
9053/// deferred replacement removes the replaced declaration's children
9054/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
9055/// re-creates them. Creation alone cannot tell that apart from a first mint, so
9056/// a removal of a declaration this capture did not create records that it was
9057/// already there when the capture opened.
9058#[derive(Debug, Default)]
9059pub struct CppRecoveryCapture {
9060    /// Declarations created while this capture was open, in creation order.
9061    created: Vec<CodeUnit>,
9062    /// Membership for `created`.
9063    created_units: HashSet<CodeUnit>,
9064    /// Declarations that predate this capture and have been removed during it.
9065    removed_pre_existing: HashSet<CodeUnit>,
9066}
9067
9068/// Which owners the parse product already holds field declarations for, folded
9069/// in as the walk records them.
9070///
9071/// [`CppVisitor::has_enum_enumerator_units`] asks that question once per enum
9072/// and used to answer it by scanning every declaration accumulated so far:
9073/// O(enums x declarations) on exactly the generated headers that declare many
9074/// of both (#2786). The answer only grows by declaration, so the walk carries
9075/// it. A field's short name names its owner chain, `Owner.member`, so one field
9076/// answers for every dotted prefix of its own short name; an anonymous enum's
9077/// or union's enumerators carry bare short names instead (#2140), which is what
9078/// an empty owner short name asks about.
9079#[derive(Debug, Default)]
9080pub struct CppFieldOwnerIndex {
9081    /// Package name -> the owner short names its fields name.
9082    owners: HashMap<String, HashSet<String>>,
9083    /// Packages holding at least one field that names no owner.
9084    ownerless_packages: HashSet<String>,
9085}
9086
9087impl CppFieldOwnerIndex {
9088    /// The index of the declarations recorded so far, built when the first
9089    /// question arrives.
9090    fn of<'unit>(
9091        declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9092        file: &ProjectFile,
9093    ) -> Self {
9094        let mut index = Self::default();
9095        for declaration in declarations {
9096            index.record(declaration, file);
9097        }
9098        index
9099    }
9100
9101    fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
9102        if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
9103            return;
9104        }
9105        let short_name = code_unit.short_name();
9106        let package_name = code_unit.package_name();
9107        if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
9108            self.ownerless_packages.insert(package_name.to_string());
9109        }
9110        if !short_name.contains('.') {
9111            return;
9112        }
9113        if !self.owners.contains_key(package_name) {
9114            self.owners
9115                .insert(package_name.to_string(), HashSet::default());
9116        }
9117        let owners = self
9118            .owners
9119            .get_mut(package_name)
9120            .expect("the package entry was just ensured");
9121        for (offset, _) in short_name.match_indices('.') {
9122            let owner = &short_name[..offset];
9123            if !owners.contains(owner) {
9124                owners.insert(owner.to_string());
9125            }
9126        }
9127    }
9128
9129    /// Whether a field declaration in `package_name` names `owner_short_name`
9130    /// as its owner. An empty owner asks about ownerless fields instead.
9131    fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
9132        if owner_short_name.is_empty() {
9133            self.ownerless_packages.contains(package_name)
9134        } else {
9135            self.owners
9136                .get(package_name)
9137                .is_some_and(|owners| owners.contains(owner_short_name))
9138        }
9139    }
9140}
9141
9142/// The declaration scan [`CppFieldOwnerIndex`] replaces, kept as the oracle a
9143/// debug build asserts every carried answer against and as the release-mode
9144/// parity tests' reference (#2786).
9145#[cfg(any(debug_assertions, test))]
9146fn cpp_declarations_hold_owned_fields<'unit>(
9147    declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9148    file: &ProjectFile,
9149    package_name: &str,
9150    owner_short_name: &str,
9151) -> bool {
9152    let prefix = format!("{owner_short_name}.");
9153    declarations.into_iter().any(|unit| {
9154        unit.kind() == CodeUnitType::Field
9155            && unit.source() == file
9156            && unit.package_name() == package_name
9157            && if owner_short_name.is_empty() {
9158                // Anonymous enum/union parent: its enumerators carry bare
9159                // short names (#2140), so presence means any ownerless
9160                // field in this file.
9161                !unit.short_name().contains(['.', '$'])
9162            } else {
9163                unit.short_name().starts_with(&prefix)
9164            }
9165    })
9166}
9167
9168/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
9169///
9170/// A region reparse is its own tree and is dropped while the walk that made it
9171/// continues, so a later parse can be allocated at the same address and hand out
9172/// the same node ids.  The root's span and shape pin the identity its address
9173/// alone does not: two roots agreeing on all of this are the same parse of the
9174/// same bytes, and a scan of one is a scan of the other.
9175#[derive(PartialEq, Eq, Hash)]
9176pub struct CppTreeIdentity {
9177    root_id: usize,
9178    start_byte: usize,
9179    end_byte: usize,
9180    kind_id: u16,
9181    child_count: usize,
9182}
9183
9184impl CppTreeIdentity {
9185    fn of(root: Node<'_>) -> Self {
9186        Self {
9187            root_id: root.id(),
9188            start_byte: root.start_byte(),
9189            end_byte: root.end_byte(),
9190            kind_id: root.kind_id(),
9191            child_count: root.child_count(),
9192        }
9193    }
9194}
9195
9196/// The namespace forward declarations one tree's prefix holds, folded in as the
9197/// walk asks about them.
9198///
9199/// `scope_for_recovered_exported_class` asks the same question once per
9200/// recovered class, and the answer depends only on the part of the tree that
9201/// starts before the asking node.  Rescanning that prefix per question is
9202/// quadratic in the file, and a generated header whose parse recovery leaves
9203/// thousands of class-like declarations at file scope pays all of it: 1,904
9204/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
9205/// (#2754).  This carries the scan forward instead.  Each question advances the
9206/// traversal from wherever the last one stopped to the asking node's start byte,
9207/// so a whole walk pays at most one pass over the prefix its furthest question
9208/// reaches, and each question then costs a name lookup.
9209#[derive(Default)]
9210pub struct CppNamespaceForwardScan {
9211    /// Every named node starting before this byte has been folded in.
9212    scanned_through: usize,
9213    forwards: HashMap<String, Vec<CppNamespaceForward>>,
9214}
9215
9216impl CppNamespaceForwardScan {
9217    /// Fold in every named node of `root` that starts at or after the fold
9218    /// watermark and before `cutoff`.
9219    ///
9220    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
9221    /// pass owes are exactly the ones no earlier pass reached, and a question
9222    /// about an earlier byte than one already answered costs nothing.
9223    fn advance_to<'tree>(
9224        &mut self,
9225        root: Node<'tree>,
9226        cutoff: usize,
9227        source: &str,
9228        ancestry: &ParentIndex<'tree>,
9229    ) {
9230        if cutoff <= self.scanned_through {
9231            return;
9232        }
9233        let folded_through = self.scanned_through;
9234        let mut cursor = root.walk();
9235        let mut stack = vec![root];
9236        while let Some(current) = stack.pop() {
9237            if (folded_through..cutoff).contains(&current.start_byte())
9238                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9239            {
9240                self.forwards
9241                    .entry(forward.name.clone())
9242                    .or_default()
9243                    .push(forward);
9244            }
9245            // A subtree ending before the watermark holds only nodes an earlier
9246            // pass already folded, and one starting at or after the cutoff is
9247            // outside the prefix being asked about. Skipping both is what keeps
9248            // the total traversal to one pass.
9249            for child in current.named_children(&mut cursor) {
9250                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
9251                    stack.push(child);
9252                }
9253            }
9254        }
9255        self.scanned_through = cutoff;
9256    }
9257
9258    /// The one namespace `name` was forward declared in before `recovered_node`.
9259    /// More than one matching forward declaration is ambiguous and answers
9260    /// nothing rather than guessing.
9261    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
9262        let mut matching = self
9263            .forwards
9264            .get(name)
9265            .into_iter()
9266            .flatten()
9267            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
9268        let first = matching.next()?;
9269        matching
9270            .next()
9271            .is_none()
9272            .then(|| first.package_name.clone())
9273    }
9274}
9275
9276/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
9277/// debug build checks every answer against (and the one the parity tests drive
9278/// directly).  It walks the whole prefix per question, which is exactly the cost
9279/// #2754 removed from the release path.
9280#[cfg(any(debug_assertions, test))]
9281fn unique_earlier_cpp_namespace_forward<'tree>(
9282    recovered_node: Node<'tree>,
9283    name: &str,
9284    source: &str,
9285    ancestry: &ParentIndex<'tree>,
9286) -> Option<String> {
9287    let mut root = recovered_node;
9288    while let Some(parent) = ancestry.parent(root) {
9289        root = parent;
9290    }
9291
9292    let mut candidates = Vec::new();
9293    let mut stack = vec![root];
9294    while let Some(current) = stack.pop() {
9295        if current.start_byte() < recovered_node.start_byte()
9296            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9297            && forward.name == name
9298            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
9299        {
9300            candidates.push(forward.package_name);
9301        }
9302
9303        let mut cursor = current.walk();
9304        for child in current.named_children(&mut cursor) {
9305            if child.start_byte() < recovered_node.start_byte() {
9306                stack.push(child);
9307            }
9308        }
9309    }
9310
9311    if candidates.len() == 1 {
9312        candidates.pop()
9313    } else {
9314        None
9315    }
9316}
9317
9318fn malformed_namespace_is_nearest_recovery_region(
9319    namespace_end_byte: usize,
9320    recovered_node: Node<'_>,
9321) -> bool {
9322    let mut root = recovered_node;
9323    while let Some(parent) = root.parent() {
9324        root = parent;
9325    }
9326    let mut cursor = root.walk();
9327    root.named_children(&mut cursor)
9328        .filter(|sibling| {
9329            namespace_end_byte <= sibling.start_byte()
9330                && sibling.end_byte() <= recovered_node.start_byte()
9331        })
9332        .all(is_malformed_namespace_recovery_trivia)
9333}
9334
9335fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
9336    matches!(node.kind(), "ERROR" | "comment")
9337        || node.kind().starts_with("preproc_")
9338        || node.kind() == "expression_statement" && node.named_child_count() == 0
9339}
9340
9341/// Return the namespace path for a forward class only when the declaration is
9342/// at namespace scope.  A declaration nested in a function/class body may share
9343/// the same namespace ancestor but cannot identify a top-level class definition.
9344fn cpp_namespace_name_for_forward<'tree>(
9345    node: Node<'tree>,
9346    source: &str,
9347    ancestry: &ParentIndex<'tree>,
9348) -> Option<String> {
9349    cpp_namespace_definition_for_forward(node, ancestry)?;
9350    cpp_lexical_namespace_name(node, source, ancestry)
9351}
9352
9353fn cpp_namespace_definition_for_forward<'tree>(
9354    node: Node<'tree>,
9355    ancestry: &ParentIndex<'tree>,
9356) -> Option<Node<'tree>> {
9357    let declaration = ancestry.parent(node)?;
9358    let mut ancestor = ancestry.parent(declaration);
9359    while let Some(current) = ancestor {
9360        if matches!(
9361            current.kind(),
9362            "compound_statement"
9363                | "field_declaration_list"
9364                | "class_specifier"
9365                | "struct_specifier"
9366                | "union_specifier"
9367                | "function_definition"
9368                | "lambda_expression"
9369        ) {
9370            return None;
9371        }
9372        if current.kind() == "namespace_definition" {
9373            return Some(current);
9374        }
9375        ancestor = ancestry.parent(current);
9376    }
9377    None
9378}
9379
9380fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
9381    match node.kind() {
9382        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9383        "parenthesized_declarator" => node
9384            .child_by_field_name("declarator")
9385            .or_else(|| last_named_child(node))
9386            .is_some_and(is_pointer_wrapper_declarator),
9387        "template_function" => node
9388            .child_by_field_name("name")
9389            .is_some_and(is_function_pointer_like_inner_declarator),
9390        _ => false,
9391    }
9392}
9393
9394fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
9395    match node.kind() {
9396        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9397        "parenthesized_declarator" => node
9398            .child_by_field_name("declarator")
9399            .or_else(|| last_named_child(node))
9400            .is_some_and(is_pointer_wrapper_declarator),
9401        _ => false,
9402    }
9403}
9404
9405fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
9406    let cleaned = raw_name.trim_start_matches("template ").trim();
9407    // A leading `::` is the explicit-global marker, not an empty owner segment.
9408    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
9409    // erroneous macro envelope swallowing the first identifier of an
9410    // out-of-line `X::X` constructor, chromium #1573); without this strip the
9411    // split below yields owner_parts `[""]`, constructing a unit with an empty
9412    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
9413    let cleaned = cleaned.trim_start_matches("::");
9414    // Parser recovery can preserve two adjacent scope operators around a
9415    // missing component (for example `X::/**/::method` in compiler diagnostic
9416    // fixtures). Empty components are syntax-recovery artifacts, never C++
9417    // owners. Keeping one as the final owner constructed `short_name=".method"`
9418    // and violated the structured package/short boundary during a large LLVM
9419    // workspace build. This is the same legacy-string-to-FqName bridge as the
9420    // ordinary split above; discard only components that the delimiter itself
9421    // proves empty.
9422    let parts: Vec<_> = cleaned
9423        .split("::")
9424        .filter(|component| !component.is_empty())
9425        .collect();
9426    if parts.is_empty() {
9427        return (None, cleaned.to_string(), scope.package_name.clone());
9428    }
9429    if parts.len() > 1 {
9430        let name = parts.last().unwrap_or(&cleaned).to_string();
9431        let owner_parts = &parts[..parts.len() - 1];
9432        if let Some(class_unit) = &scope.class_unit {
9433            // Lexically inside a class body: the owner is that class, whatever
9434            // the declarator re-qualifies it as.
9435            return (
9436                Some(CppMemberOwner::Unit(class_unit.clone())),
9437                name,
9438                scope.package_name.clone(),
9439            );
9440        }
9441        if !scope.package_name.is_empty() {
9442            // Out-of-line member definition written *inside* an enclosing
9443            // `namespace {}` block (scope package is that namespace). Every
9444            // owner segment before the terminal member is a class-nesting step
9445            // -- an out-of-line nested-class member `Outer::Inner::method` in
9446            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
9447            // namespace path: `using namespace` never brings nested-class
9448            // access into unqualified scope, so C++ always writes the full
9449            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
9450            // that redundantly re-states the enclosing namespace it already
9451            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
9452            // strip that re-qualifying prefix (which duplicates a suffix of the
9453            // enclosing package path) before treating what remains as the
9454            // nested-class chain, so the redundant spelling still lands on the
9455            // same `log4cxx.Foo.method` identity as its header declaration.
9456            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
9457            let owner = (!nested.is_empty()).then(|| {
9458                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
9459            });
9460            return (owner, name, scope.package_name.clone());
9461        }
9462        // File scope (no enclosing `namespace {}` block, scope package empty).
9463        let (owner, package_name) = if owner_parts.len() > 1 {
9464            // A multi-segment qualifier at file scope with no enclosing
9465            // namespace: treat all but the last owner segment as the namespace
9466            // path and the last as the owning class (`ns1::ns2::Class::method`
9467            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
9468            // is really a namespace or an outer class cannot be told from the
9469            // declarator text alone here, and no enclosing namespace or
9470            // in-index owner is available at per-file extraction to confirm the
9471            // class reading, so the far-more-common namespace interpretation is
9472            // kept rather than guessed away (the nested-class-at-file-scope and
9473            // using-directive-qualified nested-class shapes remain on this
9474            // behavior; see #1121).
9475            (
9476                Some(CppMemberOwner::Chain(vec![
9477                    owner_parts.last().unwrap_or(&"").to_string(),
9478                ])),
9479                owner_parts[..owner_parts.len() - 1].join("::"),
9480            )
9481        } else {
9482            // A bare `Class::member` qualifier at file scope carries no
9483            // namespace segment of its own. The declarator alone cannot say
9484            // which namespace owns `Class` -- but a `using namespace X;`
9485            // directive already in effect at this point in the file (#1093,
9486            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
9487            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
9488            // is the remaining structural signal for it, so fall back to it
9489            // rather than leaving the definition's package empty while its
9490            // header declaration (parsed inside the `namespace {}` block) keeps
9491            // the real one -- an identity split that made the same member
9492            // unresolvable under its own displayed spelling.
9493            (
9494                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
9495                cpp_using_directive_namespace_for_bare_owner(scope),
9496            )
9497        };
9498        return (owner, name, package_name);
9499    }
9500
9501    let package_name = scope.package_name.clone();
9502    let owner = scope
9503        .class_unit
9504        .as_ref()
9505        .map(|parent| CppMemberOwner::Unit(parent.clone()));
9506    (owner, cleaned.to_string(), package_name)
9507}
9508
9509/// Drop the leading owner segments of an out-of-line member qualifier that
9510/// merely re-state the enclosing namespace the definition already sits in, so
9511/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
9512/// definition may redundantly write `a::b::Outer::Inner::method` (or the
9513/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
9514/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
9515/// noise, not class-nesting steps. Returns the owner segments with the longest
9516/// such re-qualifying prefix removed (possibly all of them, when the qualifier
9517/// names only the enclosing namespace before the terminal member -- a
9518/// re-qualified free function). `package_name` is the enclosing namespace path
9519/// in its stored `::`-joined form; both sides are split on the same delimiter
9520/// the namespace walker joined them with, so this compares namespace *segments*
9521/// rather than scanning text.
9522fn strip_redundant_namespace_prefix<'a>(
9523    owner_parts: &'a [&'a str],
9524    package_name: &str,
9525) -> &'a [&'a str] {
9526    if package_name.is_empty() {
9527        return owner_parts;
9528    }
9529    let package_segments: Vec<&str> = package_name.split("::").collect();
9530    let max_prefix = owner_parts.len().min(package_segments.len());
9531    for prefix_len in (1..=max_prefix).rev() {
9532        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
9533        if &owner_parts[..prefix_len] == package_suffix {
9534            return &owner_parts[prefix_len..];
9535        }
9536    }
9537    owner_parts
9538}
9539
9540/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
9541/// class name at file/namespace scope, from the `using namespace` directives
9542/// visible at this point in the file. Several may be in scope at once (a
9543/// primary `using namespace NS;` alongside deeper conveniences like `using
9544/// namespace NS::helpers;`); since the declarator gives no way to tell which
9545/// one actually declares the owner class, prefer the shallowest (fewest
9546/// `::`-separated segments) as the file's most likely "home" namespace,
9547/// breaking ties by declaration order. Returns an empty string (leaving the
9548/// caller's package unqualified, as before) when no using-namespace directive
9549/// is in scope.
9550fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
9551    scope
9552        .visible_using_namespaces
9553        .iter()
9554        .min_by_key(|namespace| namespace.split("::").count())
9555        .cloned()
9556        .unwrap_or_default()
9557}
9558
9559struct CppQualifiedNameComponent {
9560    name: String,
9561    is_template_id: bool,
9562}
9563
9564/// Canonical nested-class chain for an out-of-line class definition written
9565/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
9566/// component per class (`["Outer", "Inner"]`).
9567///
9568/// The enclosing namespace fixes the namespace/class boundary: after an
9569/// optional redundant spelling of that namespace, every component belongs to
9570/// the class chain. File-scope qualified class names remain untouched because
9571/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
9572///
9573/// The components stay structured (rather than being `$`-joined here) so the
9574/// fq construction can push one Type/Nested segment per class; the `$`-joined
9575/// short-name display form is derived at the call sites that need it.
9576fn qualified_class_name_chain(
9577    class_node: Node<'_>,
9578    source: &str,
9579    scope: &ScopeInfo,
9580) -> Option<Vec<String>> {
9581    if scope.package_name.is_empty() || scope.class_unit.is_some() {
9582        return None;
9583    }
9584    let name = class_node.child_by_field_name("name")?;
9585    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
9586    if explicitly_global
9587        || components.len() < 2
9588        || components.iter().any(|component| component.is_template_id)
9589    {
9590        return None;
9591    }
9592    let names = components
9593        .iter()
9594        .map(|component| component.name.as_str())
9595        .collect::<Vec<_>>();
9596    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
9597    if class_chain.is_empty() {
9598        return None;
9599    }
9600    Some(class_chain.iter().map(|name| name.to_string()).collect())
9601}
9602
9603fn structured_cpp_qualified_components(
9604    qualified_name: Node<'_>,
9605    source: &str,
9606) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
9607    if qualified_name.kind() != "qualified_identifier" {
9608        return None;
9609    }
9610
9611    let mut components = Vec::new();
9612    let mut current = qualified_name;
9613    let mut explicitly_global = false;
9614    loop {
9615        if current.kind() == "qualified_identifier" {
9616            if let Some(component) = current.child_by_field_name("scope") {
9617                components.push(canonical_cpp_qualified_component(component, source)?);
9618            } else if components.is_empty() {
9619                explicitly_global = true;
9620            } else {
9621                return None;
9622            }
9623            current = current.child_by_field_name("name")?;
9624        } else {
9625            components.push(canonical_cpp_qualified_component(current, source)?);
9626            break;
9627        }
9628    }
9629    Some((components, explicitly_global))
9630}
9631
9632fn split_structured_templated_cpp_name(
9633    declarator_name: Node<'_>,
9634    source: &str,
9635    scope: &ScopeInfo,
9636) -> Option<(Option<CppMemberOwner>, String, String)> {
9637    let (mut components, explicitly_global) =
9638        structured_cpp_qualified_components(declarator_name, source)?;
9639
9640    let terminal = components.pop()?;
9641    let owner_start = components
9642        .iter()
9643        .position(|component| component.is_template_id)?;
9644    let explicit_package = components[..owner_start]
9645        .iter()
9646        .map(|component| component.name.as_str())
9647        .collect::<Vec<_>>()
9648        .join("::");
9649    let explicit_package_is_empty = explicit_package.is_empty();
9650    let package_name = match (
9651        explicitly_global,
9652        scope.package_name.is_empty(),
9653        explicit_package_is_empty,
9654    ) {
9655        (true, _, _) => explicit_package,
9656        (false, _, true) => scope.package_name.clone(),
9657        (false, true, false) => explicit_package,
9658        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
9659    };
9660    // Same identity-split fallback as `split_cpp_name` (#1093): a template
9661    // specialization's owner class named with no namespace segment of its own
9662    // (`explicit_package` empty) at file scope (`explicitly_global` false)
9663    // with nothing enclosing (`package_name` still empty) has no structural
9664    // signal for its namespace besides an in-scope `using namespace X;`.
9665    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
9666    {
9667        cpp_using_directive_namespace_for_bare_owner(scope)
9668    } else {
9669        package_name
9670    };
9671    let owner_chain = components[owner_start..]
9672        .iter()
9673        .map(|component| component.name.clone())
9674        .collect::<Vec<_>>();
9675    if owner_chain.is_empty() || terminal.name.is_empty() {
9676        return None;
9677    }
9678
9679    Some((
9680        Some(CppMemberOwner::Chain(owner_chain)),
9681        terminal.name,
9682        package_name,
9683    ))
9684}
9685
9686fn canonical_cpp_qualified_component(
9687    mut component: Node<'_>,
9688    source: &str,
9689) -> Option<CppQualifiedNameComponent> {
9690    let mut is_template_id = false;
9691    loop {
9692        match component.kind() {
9693            "template_type" => {
9694                is_template_id = true;
9695                component = component.child_by_field_name("name")?;
9696            }
9697            "dependent_name" => component = component.named_child(0)?,
9698            "identifier"
9699            | "field_identifier"
9700            | "namespace_identifier"
9701            | "type_identifier"
9702            | "operator_name"
9703            | "destructor_name" => {
9704                let name = normalize_cpp_whitespace(node_text(component, source));
9705                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
9706                    name,
9707                    is_template_id,
9708                });
9709            }
9710            _ => component = component.child_by_field_name("name")?,
9711        }
9712    }
9713}
9714
9715fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
9716    if let Some(name) = macro_decorated_unqualified_name(node) {
9717        return extract_declarator_name(name, source);
9718    }
9719    match node.kind() {
9720        "identifier"
9721        | "field_identifier"
9722        | "type_identifier"
9723        | "operator_name"
9724        | "destructor_name"
9725        | "qualified_identifier" => node_text(node, source).to_string(),
9726        "function_declarator"
9727        | "pointer_declarator"
9728        | "reference_declarator"
9729        | "parenthesized_declarator"
9730        | "array_declarator"
9731        | "template_function" => node
9732            .child_by_field_name("declarator")
9733            .or_else(|| node.child_by_field_name("name"))
9734            .or_else(|| last_named_child(node))
9735            .map(|child| extract_declarator_name(child, source))
9736            .unwrap_or_else(|| node_text(node, source).to_string()),
9737        _ => node
9738            .child_by_field_name("name")
9739            .map(|child| extract_declarator_name(child, source))
9740            .unwrap_or_else(|| node_text(node, source).to_string()),
9741    }
9742}
9743
9744/// Extract a callable identity only through declaration-shaped AST nodes.
9745/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
9746/// expose the call's parameter list as a false function declarator; accepting
9747/// arbitrary node text there emitted bogus names such as `.*f`.
9748fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
9749    if let Some(name) = macro_decorated_unqualified_name(node) {
9750        return extract_callable_declarator_name(name, source);
9751    }
9752    match node.kind() {
9753        "identifier"
9754        | "field_identifier"
9755        | "type_identifier"
9756        | "operator_name"
9757        | "destructor_name"
9758        | "qualified_identifier" => Some(node_text(node, source).to_string()),
9759        "function_declarator"
9760        | "pointer_declarator"
9761        | "reference_declarator"
9762        | "parenthesized_declarator"
9763        | "array_declarator"
9764        | "template_function" => node
9765            .child_by_field_name("declarator")
9766            .or_else(|| node.child_by_field_name("name"))
9767            .and_then(|child| extract_callable_declarator_name(child, source)),
9768        _ => None,
9769    }
9770}
9771
9772fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
9773    match node.kind() {
9774        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
9775            let name = node_text(node, source).trim().to_string();
9776            (!name.is_empty()).then_some(name)
9777        }
9778        _ => node
9779            .child_by_field_name("declarator")
9780            .or_else(|| node.child_by_field_name("name"))
9781            .or_else(|| last_named_child(node))
9782            .and_then(|child| extract_variable_name(child, source)),
9783    }
9784}
9785
9786/// Recover a C field whose name is wrapped in the function-like
9787/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
9788/// function declarator, while retaining its argument as a structured child.
9789/// The exact macro name and one-argument shape keep ordinary function-pointer
9790/// fields and unknown malformed declarators fail-closed.
9791#[derive(Clone, Copy)]
9792pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
9793    pub(crate) name: Node<'tree>,
9794    pub(crate) declarator: Node<'tree>,
9795}
9796
9797impl RecoveredFunctionLikeFieldDeclarator<'_> {
9798    pub(crate) fn pointer_depth(self) -> i32 {
9799        let mut depth = 0;
9800        let mut current = self.declarator;
9801        while current.kind() != "function_declarator" {
9802            if current.kind() == "pointer_declarator" {
9803                depth += 1;
9804            }
9805            current = current
9806                .child_by_field_name("declarator")
9807                .expect("recovered field wrapper has an inner declarator");
9808        }
9809        depth
9810    }
9811}
9812
9813pub(crate) fn recovered_function_like_field_declarator<'tree>(
9814    node: Node<'tree>,
9815    source: &str,
9816) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
9817    if node.kind() != "field_declaration" {
9818        return None;
9819    }
9820    let outer_declarator = node.child_by_field_name("declarator")?;
9821    let mut declarator = outer_declarator;
9822    while matches!(
9823        declarator.kind(),
9824        "pointer_declarator"
9825            | "reference_declarator"
9826            | "array_declarator"
9827            | "parenthesized_declarator"
9828    ) {
9829        declarator = declarator.child_by_field_name("declarator")?;
9830    }
9831    if declarator.kind() != "function_declarator" {
9832        return None;
9833    }
9834    let macro_name = declarator.child_by_field_name("declarator")?;
9835    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
9836    {
9837        return None;
9838    }
9839    let parameters = declarator.child_by_field_name("parameters")?;
9840    let mut cursor = parameters.walk();
9841    let mut arguments = parameters.named_children(&mut cursor);
9842    let parameter = arguments.next()?;
9843    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
9844        return None;
9845    }
9846    let name = parameter.child_by_field_name("type").filter(|argument| {
9847        matches!(
9848            argument.kind(),
9849            "identifier" | "field_identifier" | "type_identifier"
9850        )
9851    })?;
9852    Some(RecoveredFunctionLikeFieldDeclarator {
9853        name,
9854        declarator: outer_declarator,
9855    })
9856}
9857
9858fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
9859    let count = node.named_child_count();
9860    if count == 0 {
9861        None
9862    } else {
9863        node.named_child(count - 1)
9864    }
9865}
9866
9867fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
9868    let name_node = node.child_by_field_name("name")?;
9869    let name = normalize_cpp_whitespace(node_text(name_node, source));
9870    (!name.is_empty()).then_some(name)
9871}
9872
9873fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
9874    if node.kind() != "declaration" {
9875        return Vec::new();
9876    }
9877    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
9878        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
9879    }) else {
9880        return Vec::new();
9881    };
9882    let Some(declarator) = node.child_by_field_name("declarator") else {
9883        return Vec::new();
9884    };
9885    if node_text(keyword, source) == "using"
9886        && (declarator.kind() != "init_declarator"
9887            || declarator.child_by_field_name("value").is_none())
9888    {
9889        return Vec::new();
9890    }
9891    if node_text(keyword, source) == "typedef"
9892        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
9893    {
9894        return vec![alias_name];
9895    }
9896    extract_typedef_declarator_name(declarator, source)
9897        .into_iter()
9898        .collect()
9899}
9900
9901fn recovered_typedef_error_alias_name(
9902    declaration: Node<'_>,
9903    declarator: Node<'_>,
9904    source: &str,
9905) -> Option<String> {
9906    // An export macro between `class` and its name can make tree-sitter parse
9907    // the recovered class body as a function body. In that shape,
9908    //
9909    //     typedef spi::Filter BASE_CLASS;
9910    //
9911    // becomes a declaration whose `declarator` is the underlying qualified
9912    // type (`spi::Filter`) and whose actual alias name is displaced into the
9913    // following ERROR node. Do not publish the terminal underlying type
9914    // (`Filter`) as a false class-owned alias.
9915    if declarator.kind() != "qualified_identifier" {
9916        return None;
9917    }
9918    let mut cursor = declaration.walk();
9919    let mut errors = declaration
9920        .named_children(&mut cursor)
9921        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
9922    let error = errors.next()?;
9923    if errors.next().is_some() || error.named_child_count() != 1 {
9924        return None;
9925    }
9926    let name = error.named_child(0)?;
9927    if !matches!(
9928        name.kind(),
9929        "identifier" | "field_identifier" | "type_identifier"
9930    ) {
9931        return None;
9932    }
9933    let name = normalize_cpp_whitespace(node_text(name, source));
9934    (!name.is_empty()).then_some(name)
9935}
9936
9937fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
9938    // A function-like token in the type position can make tree-sitter expose
9939    // its argument as a parenthesized declarator. Do not publish that argument
9940    // as an alias. The macro-specific recovery below handles the proven shape.
9941    if fragmented_parenthesized_typedef_type(node).is_some() {
9942        return Vec::new();
9943    }
9944    let has_function_like_macro_type = node
9945        .child_by_field_name("type")
9946        .filter(|type_node| type_node.kind() == "type_identifier")
9947        .is_some_and(|type_node| {
9948            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
9949        });
9950    let mut names = Vec::new();
9951    let mut cursor = node.walk();
9952    for declarator in node.children_by_field_name("declarator", &mut cursor) {
9953        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
9954            continue;
9955        }
9956        if let Some(name) = extract_typedef_declarator_name(declarator, source)
9957            && !names.contains(&name)
9958        {
9959            names.push(name);
9960        }
9961    }
9962    names
9963}
9964
9965struct RecoveredMacroTypedefAlias<'tree> {
9966    name: String,
9967    end_node: Node<'tree>,
9968}
9969
9970/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
9971/// into an identifier expression statement. The uppercase macro token, missing
9972/// typedef terminator, and complete sibling terminator prove this exact shape.
9973fn recovered_macro_typedef_alias<'tree>(
9974    node: Node<'tree>,
9975    source: &str,
9976) -> Option<RecoveredMacroTypedefAlias<'tree>> {
9977    let type_node = fragmented_parenthesized_typedef_type(node)?;
9978    if type_node.kind() != "type_identifier"
9979        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
9980    {
9981        return None;
9982    }
9983
9984    let end_node = node.next_named_sibling()?;
9985    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
9986        return None;
9987    }
9988    let name_node = end_node.named_child(0)?;
9989    if name_node.kind() != "identifier" {
9990        return None;
9991    }
9992    let has_terminator = (0..end_node.child_count()).any(|index| {
9993        end_node
9994            .child(index)
9995            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
9996    });
9997    if !has_terminator {
9998        return None;
9999    }
10000    let name = normalize_cpp_whitespace(node_text(name_node, source));
10001    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
10002}
10003
10004fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
10005    if node.kind() != "type_definition" {
10006        return None;
10007    }
10008    let mut declarator_cursor = node.walk();
10009    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
10010    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
10011        return None;
10012    }
10013    let has_missing_terminator = (0..node.child_count()).any(|index| {
10014        node.child(index)
10015            .is_some_and(|child| child.kind() == ";" && child.is_missing())
10016    });
10017    if !has_missing_terminator {
10018        return None;
10019    }
10020    node.child_by_field_name("type")
10021}
10022
10023fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
10024    match node.kind() {
10025        "identifier" | "field_identifier" | "type_identifier" => {
10026            let name = normalize_cpp_whitespace(node_text(node, source));
10027            (!name.is_empty()).then_some(name)
10028        }
10029        "qualified_identifier" => node
10030            .child_by_field_name("name")
10031            .and_then(|name| extract_typedef_declarator_name(name, source)),
10032        _ => node
10033            .child_by_field_name("declarator")
10034            .or_else(|| node.child_by_field_name("name"))
10035            .or_else(|| last_named_child(node))
10036            .and_then(|child| extract_typedef_declarator_name(child, source)),
10037    }
10038}
10039
10040fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
10041    let name = node
10042        .child_by_field_name("name")
10043        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10044        .or_else(|| {
10045            let mut cursor = node.walk();
10046            node.named_children(&mut cursor)
10047                .find(|child| {
10048                    matches!(
10049                        child.kind(),
10050                        "identifier" | "field_identifier" | "type_identifier"
10051                    )
10052                })
10053                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10054        })?;
10055    (!name.is_empty()).then_some(name)
10056}
10057
10058fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
10059    left.id() == right.id()
10060}
10061
10062fn render_cpp_type_signature(
10063    node: Node<'_>,
10064    source: &str,
10065    template_signature: Option<&str>,
10066) -> String {
10067    let text = normalize_cpp_whitespace(node_text(node, source));
10068    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
10069    let rendered = if head.ends_with(';') {
10070        head.to_string()
10071    } else {
10072        format!("{head} {{")
10073    };
10074    if let Some(template_signature) = template_signature {
10075        format!("template {template_signature} {rendered}")
10076    } else {
10077        rendered
10078    }
10079}
10080
10081fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
10082    if let Some(recovered) = recovered_pyobject_head_field(node, source)
10083        && recovered.declarator == declarator
10084    {
10085        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
10086        let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
10087        return format!("{type_text} {declarator};");
10088    }
10089    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
10090        && recovered.name == declarator
10091    {
10092        let type_text = node
10093            .child_by_field_name("type")
10094            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
10095            .unwrap_or_default();
10096        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
10097        let mut prefix = String::new();
10098        let mut suffix = String::new();
10099        let mut current = recovered.declarator;
10100        while current.kind() != "function_declarator" {
10101            match current.kind() {
10102                "pointer_declarator" => prefix.push('*'),
10103                "reference_declarator" => prefix.push('&'),
10104                "array_declarator" => {
10105                    let size = current
10106                        .child_by_field_name("size")
10107                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
10108                        .unwrap_or_default();
10109                    suffix.push('[');
10110                    suffix.push_str(&size);
10111                    suffix.push(']');
10112                }
10113                "parenthesized_declarator" => {}
10114                _ => unreachable!("validated recovered field declarator wrapper"),
10115            }
10116            current = current
10117                .child_by_field_name("declarator")
10118                .expect("recovered field wrapper has an inner declarator");
10119        }
10120        let separator = if prefix.is_empty() { "" } else { " " };
10121        return format!("{type_text} {prefix}{separator}{name}{suffix};");
10122    }
10123    if let Some(signature) =
10124        render_recovered_macro_qualified_field_signature(node, declarator, source)
10125    {
10126        return signature;
10127    }
10128    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
10129    let prefix = cpp_declaration_prefix(node, source);
10130    let name = extract_variable_name(declarator, source).unwrap_or_default();
10131    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
10132    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
10133        || (prefix.ends_with('&') && raw_suffix == "&")
10134    {
10135        String::new()
10136    } else {
10137        raw_suffix
10138    };
10139
10140    let mut rendered = if suffix.is_empty() {
10141        format!("{prefix} {name}")
10142    } else if suffix.starts_with('*') || suffix.starts_with('&') {
10143        format!("{prefix}{suffix} {name}")
10144    } else if suffix.starts_with('[') || suffix.starts_with('(') {
10145        format!("{prefix} {name}{suffix}")
10146    } else {
10147        format!("{prefix} {suffix}{name}")
10148    };
10149    rendered = collapse_cpp_whitespace(&rendered);
10150
10151    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10152        format!("{rendered} = {initializer};")
10153    } else if declaration_text.ends_with(';') {
10154        format!("{rendered};")
10155    } else {
10156        rendered
10157    }
10158}
10159
10160fn render_recovered_macro_qualified_field_signature(
10161    node: Node<'_>,
10162    declarator: Node<'_>,
10163    source: &str,
10164) -> Option<String> {
10165    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
10166    if !recovered
10167        .iter()
10168        .any(|candidate| same_node(*candidate, declarator))
10169    {
10170        return None;
10171    }
10172    let pseudo_declarator = node.child_by_field_name("declarator")?;
10173    let mut cursor = node.walk();
10174    let clause = node
10175        .named_children(&mut cursor)
10176        .find(|child| child.kind() == "bitfield_clause")?;
10177    let mut cursor = clause.walk();
10178    let error = clause
10179        .named_children(&mut cursor)
10180        .find(|child| child.kind() == "ERROR")?;
10181    let qualified_type =
10182        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
10183    let prefix = cpp_declaration_prefix(node, source);
10184    let name = extract_variable_name(declarator, source)?;
10185    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
10186    let mut rendered = if suffix.is_empty() {
10187        format!("{prefix} {qualified_type} {name}")
10188    } else {
10189        format!("{prefix} {qualified_type} {suffix} {name}")
10190    };
10191    rendered = collapse_cpp_whitespace(&rendered);
10192
10193    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
10194        Some(format!(
10195            "{rendered} = {};",
10196            normalize_cpp_whitespace(node_text(initializer, source))
10197        ))
10198    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10199        Some(format!("{rendered} = {initializer};"))
10200    } else {
10201        Some(format!("{rendered};"))
10202    }
10203}
10204
10205fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
10206    match node.kind() {
10207        "pointer_expression" => {
10208            let operator = node
10209                .child_by_field_name("operator")
10210                .or_else(|| node.child(0))
10211                .map(|operator| node_text(operator, source))
10212                .unwrap_or("*");
10213            let argument = node
10214                .child_by_field_name("argument")
10215                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10216                .unwrap_or_default();
10217            format!("{operator}{argument}")
10218        }
10219        "unary_expression" => {
10220            let operator = node
10221                .child_by_field_name("operator")
10222                .or_else(|| node.child(0))
10223                .map(|operator| node_text(operator, source))
10224                .unwrap_or_default();
10225            let argument = node
10226                .child_by_field_name("argument")
10227                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10228                .unwrap_or_default();
10229            format!("{operator}{argument}")
10230        }
10231        "identifier" | "field_identifier" => String::new(),
10232        _ => cpp_declarator_suffix_without_name(node, source),
10233    }
10234}
10235
10236fn recovered_macro_qualified_field_initializer<'tree>(
10237    clause: Node<'tree>,
10238    declarator: Node<'tree>,
10239) -> Option<Node<'tree>> {
10240    let mut stack = vec![clause];
10241    while let Some(current) = stack.pop() {
10242        if current.kind() == "assignment_expression"
10243            && current
10244                .child_by_field_name("left")
10245                .is_some_and(|left| same_node(left, declarator))
10246        {
10247            return current.child_by_field_name("right");
10248        }
10249        let mut cursor = current.walk();
10250        stack.extend(current.named_children(&mut cursor));
10251    }
10252    None
10253}
10254
10255fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
10256    let text = node_text(node, source);
10257    let mut cursor = node.walk();
10258    let first_declarator = node.named_children(&mut cursor).find(|child| {
10259        matches!(
10260            child.kind(),
10261            "init_declarator"
10262                | "identifier"
10263                | "field_identifier"
10264                | "pointer_declarator"
10265                | "reference_declarator"
10266                | "array_declarator"
10267                | "function_declarator"
10268        )
10269    });
10270    let prefix = if let Some(first_declarator) = first_declarator {
10271        let end = first_declarator
10272            .start_byte()
10273            .saturating_sub(node.start_byte());
10274        let mut prefix = text.get(..end).unwrap_or(text).to_string();
10275        let declarator_suffix = match first_declarator.kind() {
10276            "init_declarator" => first_declarator
10277                .child_by_field_name("declarator")
10278                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
10279                .unwrap_or_default(),
10280            _ => cpp_declarator_suffix_without_name(first_declarator, source),
10281        };
10282        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
10283            prefix.push_str(&declarator_suffix);
10284        }
10285        return collapse_cpp_whitespace(&prefix)
10286            .trim_end_matches(',')
10287            .trim_end_matches(';')
10288            .trim()
10289            .to_string();
10290    } else {
10291        text
10292    };
10293    collapse_cpp_whitespace(prefix)
10294        .trim_end_matches(',')
10295        .trim_end_matches(';')
10296        .trim()
10297        .to_string()
10298}
10299
10300fn cpp_preserved_initializer(
10301    declaration_node: Node<'_>,
10302    declarator: Node<'_>,
10303    source: &str,
10304) -> Option<String> {
10305    let name = extract_variable_name(declarator, source)?;
10306    let mut cursor = declaration_node.walk();
10307    for child in declaration_node.named_children(&mut cursor) {
10308        if child.kind() != "init_declarator" {
10309            continue;
10310        }
10311        let Some(inner) = child.child_by_field_name("declarator") else {
10312            continue;
10313        };
10314        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
10315            continue;
10316        }
10317        let value = child.child_by_field_name("value")?;
10318        let kind = value.kind();
10319        if matches!(
10320            kind,
10321            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
10322        ) {
10323            return Some(normalize_cpp_whitespace(node_text(value, source)));
10324        }
10325        break;
10326    }
10327    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
10328    let pattern = format!(
10329        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
10330        regex::escape(&name)
10331    );
10332    Regex::new(&pattern)
10333        .ok()
10334        .and_then(|regex| regex.captures(&declaration_text))
10335        .and_then(|captures| captures.get(1))
10336        .map(|value| value.as_str().to_string())
10337}
10338
10339fn render_cpp_function_display_signature_from_node<'tree>(
10340    node: Node<'tree>,
10341    source: &str,
10342    template_signature: Option<&str>,
10343    has_body: bool,
10344    ancestry: &ParentIndex<'tree>,
10345) -> String {
10346    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
10347    let parent_text = node_text(root, source);
10348    let body_local_start = root
10349        .child_by_field_name("body")
10350        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
10351        .unwrap_or(parent_text.len());
10352    let display = parent_text
10353        .get(..body_local_start)
10354        .unwrap_or(parent_text)
10355        .trim()
10356        .trim();
10357    let display = if let Some(template_signature) = template_signature {
10358        if display.starts_with("template ") {
10359            display.to_string()
10360        } else {
10361            format!("template {template_signature} {display}")
10362        }
10363    } else {
10364        display.to_string()
10365    };
10366    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
10367    if has_body {
10368        format!("{display} {{...}}")
10369    } else {
10370        format!("{display};")
10371    }
10372}
10373
10374fn cpp_template_signature(
10375    template_node: Node<'_>,
10376    declaration_child: Node<'_>,
10377    source: &str,
10378) -> Option<String> {
10379    let text = source
10380        .get(template_node.start_byte()..declaration_child.start_byte())
10381        .unwrap_or("");
10382    let text = normalize_cpp_whitespace(text);
10383    let start = text.find('<')?;
10384    let end = text.rfind('>')?;
10385    if end < start {
10386        return None;
10387    }
10388    Some(text[start..=end].to_string())
10389}
10390
10391struct RecoveredFragmentedPartialSpecialization<'tree> {
10392    declaration_node: Node<'tree>,
10393    name: String,
10394    range: Range,
10395    prefix_members: Vec<Node<'tree>>,
10396    member_siblings: Vec<Node<'tree>>,
10397    following_declarations: Vec<Node<'tree>>,
10398}
10399
10400struct RecoveredFragmentedPreprocessorClass<'tree> {
10401    declaration_node: Node<'tree>,
10402    class_node: Node<'tree>,
10403    body: Node<'tree>,
10404    name: String,
10405    range: Range,
10406    tail_members: Vec<Node<'tree>>,
10407    member_siblings: Vec<Node<'tree>>,
10408}
10409
10410/// Recover a class whose preprocessor-fragmented parse closes at an early
10411/// member body and publishes the remaining in-class declarations as siblings
10412/// of the surrounding alternative. Primary classes are admitted only when an
10413/// earlier branch contains the matching bodyless declaration and the class
10414/// node retains the displaced `#endif`. Partial specializations instead carry
10415/// their identity structurally in the `template_type` name and template
10416/// metadata. Retain the original AST nodes and re-own only the siblings through
10417/// the displaced structural `};` terminator.
10418fn recover_fragmented_preprocessor_class<'tree>(
10419    template_node: Node<'tree>,
10420    source: &str,
10421    ancestry: &ParentIndex<'tree>,
10422) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
10423    let alternative = ancestry.parent(template_node)?;
10424    if alternative.kind() != "preproc_else" {
10425        return None;
10426    }
10427    let conditional = alternative.parent()?;
10428    if conditional.kind() != "preproc_if" {
10429        return None;
10430    }
10431    let declaration_node = template_node
10432        .named_children(&mut template_node.walk())
10433        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
10434    let class_node = declaration_node
10435        .named_children(&mut declaration_node.walk())
10436        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
10437    let body = cpp_body_node(class_node)?;
10438    if class_node.end_byte() >= declaration_node.end_byte() {
10439        return None;
10440    }
10441    let name = class_like_name(class_node, source, ancestry)?;
10442    let is_partial_specialization = class_node
10443        .child_by_field_name("name")
10444        .is_some_and(|class_name| class_name.kind() == "template_type");
10445    if is_partial_specialization {
10446        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
10447        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
10448            return None;
10449        }
10450    } else {
10451        if !class_has_displaced_preprocessor_terminator(class_node) {
10452            return None;
10453        }
10454        let matching_other_branch = conditional
10455            .named_children(&mut conditional.walk())
10456            .take_while(|child| !same_node(*child, alternative))
10457            .filter(|child| child.kind() == "template_declaration")
10458            .filter_map(first_class_like_child)
10459            .any(|candidate| {
10460                cpp_body_node(candidate).is_none()
10461                    && class_like_name(candidate, source, ancestry).as_deref()
10462                        == Some(name.as_str())
10463            });
10464        if !matching_other_branch {
10465            return None;
10466        }
10467    }
10468
10469    let mut tail_members = Vec::new();
10470    let mut saw_class = false;
10471    let mut declaration_cursor = declaration_node.walk();
10472    for child in declaration_node.named_children(&mut declaration_cursor) {
10473        if same_node(child, class_node) {
10474            saw_class = true;
10475        } else if saw_class {
10476            tail_members.push(child);
10477        }
10478    }
10479
10480    let mut member_siblings = Vec::new();
10481    let mut saw_template = false;
10482    let mut terminator = None;
10483    for index in 0..alternative.child_count() {
10484        let Some(child) = alternative.child(index) else {
10485            continue;
10486        };
10487        if same_node(child, template_node) {
10488            saw_template = true;
10489            continue;
10490        }
10491        if !saw_template {
10492            continue;
10493        }
10494        if displaced_fragmented_class_terminator(alternative, index) {
10495            terminator = alternative.child(index + 1);
10496            break;
10497        }
10498        if child.is_named() {
10499            member_siblings.push(child);
10500        }
10501    }
10502    let terminator = terminator?;
10503    Some(RecoveredFragmentedPreprocessorClass {
10504        declaration_node,
10505        class_node,
10506        body,
10507        name,
10508        range: Range {
10509            start_byte: class_node.start_byte(),
10510            end_byte: terminator.end_byte(),
10511            start_line: class_node.start_position().row + 1,
10512            end_line: terminator.end_position().row + 1,
10513        },
10514        tail_members,
10515        member_siblings,
10516    })
10517}
10518
10519fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
10520    (0..class_node.child_count()).any(|index| {
10521        class_node.child(index).is_some_and(|child| {
10522            child.kind() == "ERROR"
10523                && (0..child.child_count()).any(|error_index| {
10524                    child
10525                        .child(error_index)
10526                        .is_some_and(|token| token.kind() == "#endif")
10527                })
10528        })
10529    })
10530}
10531
10532/// The real `#endif` that tree-sitter consumed inside an error subtree.
10533///
10534/// A preprocessor directive inside a malformed array bound can cause later
10535/// declarations to remain children of the conditional. The non-missing token
10536/// still gives the exact structured boundary. Ignore nested conditionals and
10537/// select the last error-owned token. Tree-sitter can pair a later outer
10538/// `#endif` with this conditional, so the direct terminator is not necessarily
10539/// missing.
10540pub fn cpp_displaced_preprocessor_terminator<'tree>(
10541    conditional: Node<'tree>,
10542) -> Option<Node<'tree>> {
10543    if !conditional.has_error() {
10544        return None;
10545    }
10546    let has_concrete_direct_terminator = conditional
10547        .child_count()
10548        .checked_sub(1)
10549        .and_then(|index| conditional.child(index))
10550        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
10551    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
10552        // A structured alternative proves that the direct `#endif` closes
10553        // this family. An error-owned terminator inside either branch belongs
10554        // to a damaged nested conditional, not to this one.
10555        return None;
10556    }
10557    let mut displaced = None;
10558    let mut stack = (0..conditional.child_count())
10559        .filter_map(|index| conditional.child(index))
10560        .map(|child| (child, false))
10561        .collect::<Vec<_>>();
10562    while let Some((node, inside_error)) = stack.pop() {
10563        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
10564            continue;
10565        }
10566        if node.kind() == "#endif" && !node.is_missing() && inside_error {
10567            if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
10568                displaced = Some(node);
10569            }
10570            continue;
10571        }
10572        if node != conditional
10573            && matches!(
10574                node.kind(),
10575                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
10576            )
10577        {
10578            continue;
10579        }
10580        let inside_error = inside_error || node.kind() == "ERROR";
10581        for child in children_iter(node) {
10582            stack.push((child, inside_error));
10583        }
10584    }
10585    displaced
10586}
10587
10588/// The effective end of a conditional whose real terminator tree-sitter
10589/// displaced into declaration recovery.
10590///
10591/// Most damaged conditionals retain a concrete `#endif` token below an
10592/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
10593/// boundary. A preprocessor family that selects the middle of a declaration
10594/// can lose the directive tokens entirely. In that shape tree-sitter leaves
10595/// the declaration's `typedef` token as the sole child of the immediately
10596/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
10597/// declarator name inside the conditional's first declaration. The declaration
10598/// end is then the smallest structured boundary that contains the whole split
10599/// declaration.
10600#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10601pub struct CppDisplacedPreprocessorBoundary {
10602    pub end_byte: usize,
10603    pub end_line: usize,
10604}
10605
10606pub fn cpp_displaced_preprocessor_boundary(
10607    conditional: Node<'_>,
10608) -> Option<CppDisplacedPreprocessorBoundary> {
10609    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
10610        return Some(CppDisplacedPreprocessorBoundary {
10611            end_byte: terminator.end_byte(),
10612            end_line: terminator.end_position().row + 1,
10613        });
10614    }
10615    if let Some(declaration) = displaced_split_declaration(conditional) {
10616        return Some(CppDisplacedPreprocessorBoundary {
10617            end_byte: declaration.end_byte(),
10618            end_line: declaration.end_position().row + 1,
10619        });
10620    }
10621    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
10622        return Some(CppDisplacedPreprocessorBoundary {
10623            end_byte: terminator.end_byte(),
10624            end_line: terminator.end_position().row + 1,
10625        });
10626    }
10627    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
10628        return Some(CppDisplacedPreprocessorBoundary {
10629            end_byte: terminator.end_byte(),
10630            end_line: terminator.end_position().row + 1,
10631        });
10632    }
10633    None
10634}
10635
10636/// Recover an outer terminator that tree-sitter assigned to a damaged nested
10637/// conditional. This occurs when a split construct such as `extern "C"`
10638/// consumes the nested `#endif` inside an error node: the nested conditional's
10639/// direct terminator is then the outer conditional's real terminator, while
10640/// the outer node ends with a missing token and absorbs later declarations.
10641fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10642    if !conditional.has_error()
10643        || conditional.child_by_field_name("alternative").is_some()
10644        || conditional
10645            .child(conditional.child_count().saturating_sub(1))
10646            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
10647    {
10648        return None;
10649    }
10650    let mut recovered = None;
10651    for nested in named_children_iter(conditional) {
10652        if !matches!(
10653            nested.kind(),
10654            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
10655        ) || nested.child_by_field_name("alternative").is_some()
10656        {
10657            continue;
10658        }
10659        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
10660            continue;
10661        };
10662        if direct.kind() != "#endif" || direct.is_missing() {
10663            continue;
10664        }
10665        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
10666            continue;
10667        };
10668        if displaced.end_byte() >= direct.start_byte() {
10669            continue;
10670        }
10671        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
10672            recovered = Some(direct);
10673        }
10674    }
10675    recovered
10676}
10677
10678fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10679    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
10680        return None;
10681    }
10682    let mut cursor = conditional.walk();
10683    let declarations = conditional
10684        .named_children(&mut cursor)
10685        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
10686        .collect::<Vec<_>>();
10687    let declaration = *declarations.first()?;
10688    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
10689        return None;
10690    }
10691    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
10692    let mut terminator = None;
10693    let mut stack = (0..declaration.child_count())
10694        .filter_map(|index| declaration.child(index))
10695        .filter(|child| child.start_byte() < declarator_start)
10696        .map(|child| (child, false))
10697        .collect::<Vec<_>>();
10698    while let Some((node, inside_error)) = stack.pop() {
10699        let inside_error = inside_error || node.kind() == "ERROR";
10700        if inside_error && node.kind() == "#endif" && !node.is_missing() {
10701            terminator = Some(node);
10702            continue;
10703        }
10704        for index in 0..node.child_count() {
10705            if let Some(child) = node.child(index)
10706                && child.start_byte() < declarator_start
10707            {
10708                stack.push((child, inside_error));
10709            }
10710        }
10711    }
10712    terminator
10713}
10714
10715fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10716    if !conditional.has_error()
10717        || conditional.child_by_field_name("alternative").is_some()
10718        || conditional
10719            .prev_named_sibling()
10720            .filter(|sibling| {
10721                sibling.kind() == "ERROR"
10722                    && sibling.child_count() == 1
10723                    && sibling
10724                        .child(0)
10725                        .is_some_and(|child| child.kind() == "typedef")
10726            })
10727            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
10728            .is_none()
10729    {
10730        return None;
10731    }
10732    let mut cursor = conditional.walk();
10733    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
10734    let declaration_index = children
10735        .iter()
10736        .position(|child| child.kind() == "declaration" && child.has_error())?;
10737    let declaration = children[declaration_index];
10738    if !children
10739        .iter()
10740        .skip(declaration_index + 1)
10741        .any(|child| child.end_byte() > declaration.end_byte())
10742    {
10743        return None;
10744    }
10745    let declarator = declaration.child_by_field_name("declarator")?;
10746    let mut error_end = None;
10747    let mut names = Vec::new();
10748    let mut stack = vec![declarator];
10749    while let Some(node) = stack.pop() {
10750        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
10751            error_end =
10752                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
10753            continue;
10754        }
10755        if matches!(node.kind(), "identifier" | "type_identifier") {
10756            names.push(node.start_byte());
10757        }
10758        push_named_children_reversed(node, &mut stack);
10759    }
10760    let error_end = error_end?;
10761    names
10762        .into_iter()
10763        .any(|start| start >= error_end)
10764        .then_some(declaration)
10765}
10766
10767fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
10768    let Some(error) = parent.child(error_index) else {
10769        return false;
10770    };
10771    if error.kind() != "ERROR"
10772        || error.child_count() != 1
10773        || error.child(0).is_none_or(|child| child.kind() != "}")
10774    {
10775        return false;
10776    }
10777    let Some(semicolon) = parent.child(error_index + 1) else {
10778        return false;
10779    };
10780    semicolon.kind() == "expression_statement"
10781        && semicolon.child_count() == 1
10782        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
10783}
10784
10785/// Locate the real end of a class-like declaration when a macro invocation
10786/// without a source semicolon absorbs the class's `};` into its parsed field.
10787/// The grammar then keeps following namespace declarations as later children
10788/// of the same field list. The direct ERROR-plus-semicolon pair proves the
10789/// boundary structurally; no source-text delimiter scan is needed.
10790fn displaced_macro_class_tail(
10791    declaration_node: Node<'_>,
10792    body: Node<'_>,
10793    source: &str,
10794) -> Option<DisplacedMacroClassTail> {
10795    if !matches!(
10796        declaration_node.kind(),
10797        "class_specifier" | "struct_specifier" | "union_specifier"
10798    ) || body.kind() != "field_declaration_list"
10799    {
10800        return None;
10801    }
10802
10803    let child_count = body.named_child_count();
10804    for index in 0..child_count {
10805        let child = body.named_child(index)?;
10806        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
10807            continue;
10808        };
10809        let split_index = index + 1;
10810        if split_index >= child_count {
10811            return None;
10812        }
10813        let mut cursor = body.walk();
10814        if !body
10815            .named_children(&mut cursor)
10816            .skip(split_index)
10817            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
10818        {
10819            return None;
10820        }
10821        return Some(DisplacedMacroClassTail {
10822            split_index,
10823            class_range: Range {
10824                start_byte: declaration_node.start_byte(),
10825                end_byte: terminator.end_byte(),
10826                start_line: declaration_node.start_position().row + 1,
10827                end_line: terminator.end_position().row + 1,
10828            },
10829        });
10830    }
10831    None
10832}
10833
10834fn displaced_macro_field_terminator<'tree>(
10835    field: Node<'tree>,
10836    source: &str,
10837) -> Option<Node<'tree>> {
10838    if field.kind() != "field_declaration" {
10839        return None;
10840    }
10841    let macro_type = field.child_by_field_name("type")?;
10842    if macro_type.kind() != "type_identifier"
10843        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10844        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
10845    {
10846        return None;
10847    }
10848    for index in 0..field.child_count() {
10849        let error = field.child(index)?;
10850        if error.kind() != "ERROR"
10851            || error.child_count() != 1
10852            || error.child(0).is_none_or(|child| child.kind() != "}")
10853        {
10854            continue;
10855        }
10856        let semicolon = field.child(index + 1)?;
10857        if semicolon.kind() == ";" {
10858            return Some(semicolon);
10859        }
10860    }
10861    None
10862}
10863
10864fn recover_fragmented_partial_specialization<'tree>(
10865    template_node: Node<'tree>,
10866    declaration_child: Node<'tree>,
10867    source: &str,
10868    ancestry: &ParentIndex<'tree>,
10869) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
10870    if declaration_child.kind() != "function_definition" {
10871        return None;
10872    }
10873    let class_node = declaration_child.child_by_field_name("type")?;
10874    if !matches!(
10875        class_node.kind(),
10876        "class_specifier" | "struct_specifier" | "union_specifier"
10877    ) || !class_node
10878        .child_by_field_name("name")
10879        .and_then(|name| direct_identifier_name(name, source))
10880        .is_some_and(|name| cpp_export_macro_token(&name))
10881    {
10882        return None;
10883    }
10884    let declarator = declaration_child.child_by_field_name("declarator")?;
10885    if declarator.kind() != "template_function" {
10886        return None;
10887    }
10888    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
10889    if metadata.specialization_arguments.is_empty() {
10890        return None;
10891    }
10892    let body = declaration_child.child_by_field_name("body")?;
10893    if body.kind() != "compound_statement" {
10894        return None;
10895    }
10896    let complete_prefix = body.named_child(0).filter(|first| {
10897        first.kind() == "labeled_statement"
10898            && first.has_error()
10899            && first
10900                .named_child(first.named_child_count().saturating_sub(1))
10901                .is_some_and(recovered_declaration_has_class_terminator)
10902    });
10903    let complete_body = complete_prefix.is_some();
10904    let mut prefix_members = Vec::new();
10905    if let Some(prefix) = complete_prefix {
10906        prefix_members.push(prefix);
10907    } else {
10908        let mut body_cursor = body.walk();
10909        for child in body.named_children(&mut body_cursor) {
10910            if !is_structurally_valid_fragmented_class_prefix_member(child) {
10911                break;
10912            }
10913            prefix_members.push(child);
10914        }
10915    }
10916    let containing_declarations = template_node.parent()?;
10917    if !matches!(
10918        containing_declarations.kind(),
10919        "declaration_list" | "compound_statement"
10920    ) {
10921        return None;
10922    }
10923    let mut member_siblings = Vec::new();
10924    let mut following_declarations = Vec::new();
10925    let terminator;
10926    if complete_body {
10927        terminator = complete_prefix?;
10928        let mut cursor = body.walk();
10929        let mut after_prefix = false;
10930        for child in body.named_children(&mut cursor) {
10931            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
10932                after_prefix = true;
10933            } else if after_prefix {
10934                following_declarations.push(child);
10935            }
10936        }
10937    } else {
10938        let mut found_template = false;
10939        let mut cursor = containing_declarations.walk();
10940        let mut class_terminator = None;
10941        for child in containing_declarations.children(&mut cursor) {
10942            if same_node(child, template_node) {
10943                found_template = true;
10944                continue;
10945            }
10946            if found_template && child.kind() == "}" {
10947                class_terminator = Some(child);
10948                break;
10949            }
10950            // A namespace can never be a class member: reaching one before the
10951            // terminator proves the class's true close was swallowed upstream
10952            // and this scan has crossed into the enclosing scope, so the
10953            // recovery cannot be bounded -- continuing re-owns the namespace
10954            // block (and its template specializations) as class members under
10955            // a re-appended package, desyncing the fq boundary (#2306).
10956            if found_template && child.kind() == "namespace_definition" {
10957                return None;
10958            }
10959            if found_template && child.is_named() {
10960                member_siblings.push(child);
10961            }
10962        }
10963        terminator = class_terminator?;
10964    }
10965    let name = format!(
10966        "{}<{}>",
10967        metadata.primary_name,
10968        metadata
10969            .specialization_arguments
10970            .iter()
10971            .map(|argument| argument.text.as_str())
10972            .collect::<Vec<_>>()
10973            .join(", ")
10974    );
10975    Some(RecoveredFragmentedPartialSpecialization {
10976        declaration_node: declaration_child,
10977        name,
10978        range: Range {
10979            start_byte: declaration_child.start_byte(),
10980            end_byte: terminator.end_byte(),
10981            start_line: declaration_child.start_position().row + 1,
10982            end_line: terminator.end_position().row + 1,
10983        },
10984        prefix_members,
10985        member_siblings,
10986        following_declarations,
10987    })
10988}
10989
10990/// Whether `node` is the function-shaped parser wrapper whose body was
10991/// recovered as a fragmented partial-specialization class body.
10992///
10993/// Consumers that walk lexical function scopes must use the same complete
10994/// structural proof as declaration extraction. Otherwise member callables in
10995/// the recovered body are mistaken for function-local declarations.
10996pub fn is_recovered_fragmented_partial_specialization_container(
10997    node: Node<'_>,
10998    source: &str,
10999) -> bool {
11000    let Some(template) = node
11001        .parent()
11002        .filter(|parent| parent.kind() == "template_declaration")
11003    else {
11004        return false;
11005    };
11006    let mut root = template;
11007    while let Some(parent) = root.parent() {
11008        root = parent;
11009    }
11010    recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
11011        .is_some()
11012}
11013
11014fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
11015    if declaration.kind() != "declaration" {
11016        return false;
11017    }
11018    // With an export macro between `class` and its name, tree-sitter folds a
11019    // complete class body into a function-shaped declaration. The class's own
11020    // `};` remains structurally identifiable as a direct ERROR child holding
11021    // `}`, immediately followed by the declaration's direct `;` child.
11022    (0..declaration.child_count().saturating_sub(1)).any(|index| {
11023        let Some(error) = declaration.child(index) else {
11024            return false;
11025        };
11026        error.kind() == "ERROR"
11027            && error.child_count() == 1
11028            && error.child(0).is_some_and(|child| child.kind() == "}")
11029            && declaration
11030                .child(index + 1)
11031                .is_some_and(|child| child.kind() == ";")
11032    })
11033}
11034
11035fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
11036    if node.has_error() {
11037        return false;
11038    }
11039    match node.kind() {
11040        "declaration"
11041        | "field_declaration"
11042        | "alias_declaration"
11043        | "type_definition"
11044        | "static_assert_declaration" => true,
11045        "labeled_statement" => node
11046            .named_child(node.named_child_count().saturating_sub(1))
11047            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
11048        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
11049            matches!(
11050                child.kind(),
11051                "declaration"
11052                    | "field_declaration"
11053                    | "alias_declaration"
11054                    | "type_definition"
11055                    | "function_definition"
11056            )
11057        }),
11058        _ => false,
11059    }
11060}
11061
11062fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
11063    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
11064        .then(|| node.child_by_field_name("declarator"))
11065        .flatten()
11066        .and_then(|declarator| extract_variable_name(declarator, source))
11067}
11068
11069fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
11070    while let Some(parent) = node.parent() {
11071        if matches!(parent.kind(), "function_definition" | "lambda_expression") {
11072            return true;
11073        }
11074        node = parent;
11075    }
11076    false
11077}
11078
11079fn cpp_template_metadata<'tree>(
11080    template_node: Node<'tree>,
11081    declaration_child: Node<'tree>,
11082    source: &str,
11083    ancestry: &ParentIndex<'tree>,
11084) -> Option<CppTemplateMetadata> {
11085    let parameters_node = template_node.child_by_field_name("parameters")?;
11086    let name_node = cpp_templated_class_name_node(declaration_child)?;
11087    let primary_node = match name_node.kind() {
11088        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
11089        _ => name_node,
11090    };
11091    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
11092    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
11093        return None;
11094    }
11095
11096    let mut parameter_nodes = Vec::new();
11097    let mut parameter_names = Vec::new();
11098    let mut cursor = parameters_node.walk();
11099    for parameter in parameters_node.named_children(&mut cursor) {
11100        if !matches!(
11101            parameter.kind(),
11102            "type_parameter_declaration"
11103                | "optional_type_parameter_declaration"
11104                | "variadic_type_parameter_declaration"
11105                | "template_template_parameter_declaration"
11106                | "parameter_declaration"
11107                | "optional_parameter_declaration"
11108                | "variadic_parameter_declaration"
11109        ) {
11110            continue;
11111        }
11112        let index = parameter_nodes.len();
11113        // An unnamed parameter still contributes template arity and kind. Use
11114        // an impossible C++ identifier so positional reconciliation can bind
11115        // it without making source expressions refer to a name that was not
11116        // written.
11117        let name = cpp_template_parameter_name(parameter, source)
11118            .unwrap_or_else(|| format!("<anonymous:{index}>"));
11119        parameter_names.push(name);
11120        parameter_nodes.push(parameter);
11121    }
11122    let parameters = parameter_nodes
11123        .into_iter()
11124        .zip(parameter_names.iter().cloned())
11125        .map(|(parameter, name)| CppTemplateParameterMetadata {
11126            name,
11127            kind: cpp_template_parameter_kind(parameter),
11128            variadic: matches!(
11129                parameter.kind(),
11130                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
11131            ),
11132            default: cpp_template_parameter_default_expression(
11133                parameter,
11134                source,
11135                &parameter_names,
11136                ancestry,
11137            ),
11138        })
11139        .collect();
11140    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
11141        Vec::new()
11142    } else {
11143        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
11144            .unwrap_or_default()
11145    };
11146    let alias_target = (declaration_child.kind() == "alias_declaration")
11147        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
11148        .flatten();
11149    Some(CppTemplateMetadata {
11150        primary_name,
11151        primary_fq_name: String::new(),
11152        parameters,
11153        specialization_arguments,
11154        alias_target,
11155    })
11156}
11157
11158fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
11159    match node.kind() {
11160        "class_specifier" | "struct_specifier" | "union_specifier" => {
11161            node.child_by_field_name("name")
11162        }
11163        "function_definition" => {
11164            let declarator = node.child_by_field_name("declarator")?;
11165            if matches!(declarator.kind(), "identifier" | "template_function") {
11166                Some(declarator)
11167            } else {
11168                None
11169            }
11170        }
11171        "alias_declaration" => node.child_by_field_name("name"),
11172        _ => None,
11173    }
11174}
11175
11176fn cpp_template_alias_target<'tree>(
11177    alias: Node<'tree>,
11178    source: &str,
11179    parameter_names: &[String],
11180    ancestry: &ParentIndex<'tree>,
11181) -> Option<CppTemplateAliasTargetMetadata> {
11182    let mut type_node = alias.child_by_field_name("type")?;
11183    while type_node.kind() == "type_descriptor" {
11184        type_node = type_node.child_by_field_name("type")?;
11185    }
11186    let global = type_node.child_by_field_name("scope").is_none()
11187        && type_node.child(0).is_some_and(|child| child.kind() == "::");
11188    let mut components = Vec::new();
11189    cpp_template_target_components(type_node, source, &mut components)?;
11190    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
11191    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
11192        components,
11193        global,
11194        arguments,
11195    })
11196}
11197
11198fn cpp_template_target_components(
11199    node: Node<'_>,
11200    source: &str,
11201    out: &mut Vec<String>,
11202) -> Option<()> {
11203    match node.kind() {
11204        "identifier" | "namespace_identifier" | "type_identifier" => {
11205            out.push(node_text(node, source).to_string());
11206            Some(())
11207        }
11208        "template_type" => {
11209            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11210        }
11211        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11212            if let Some(scope) = node.child_by_field_name("scope") {
11213                cpp_template_target_components(scope, source, out)?;
11214            }
11215            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11216        }
11217        _ => None,
11218    }
11219}
11220
11221fn cpp_template_argument_expressions<'tree>(
11222    mut node: Node<'tree>,
11223    source: &str,
11224    parameter_names: &[String],
11225    ancestry: &ParentIndex<'tree>,
11226) -> Option<Vec<CppTemplateExpression>> {
11227    loop {
11228        match node.kind() {
11229            "template_type" | "template_function" => {
11230                let arguments = node.child_by_field_name("arguments")?;
11231                let mut cursor = arguments.walk();
11232                return Some(
11233                    arguments
11234                        .named_children(&mut cursor)
11235                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
11236                        .map(|argument| {
11237                            cpp_template_expression(argument, source, parameter_names, ancestry)
11238                        })
11239                        .collect(),
11240                );
11241            }
11242            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
11243                node = node
11244                    .child_by_field_name("name")
11245                    .or_else(|| node.child_by_field_name("type"))?;
11246            }
11247            _ => return None,
11248        }
11249    }
11250}
11251
11252fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
11253    let candidate = node
11254        .child_by_field_name("name")
11255        .or_else(|| node.child_by_field_name("declarator"))
11256        .or_else(|| {
11257            let mut cursor = node.walk();
11258            node.named_children(&mut cursor).find(|child| {
11259                matches!(
11260                    child.kind(),
11261                    "identifier" | "type_identifier" | "field_identifier"
11262                )
11263            })
11264        })?;
11265    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
11266    (!name.is_empty()).then_some(name)
11267}
11268
11269fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
11270    match node.kind() {
11271        "type_parameter_declaration"
11272        | "optional_type_parameter_declaration"
11273        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
11274        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
11275        _ => CppTemplateParameterKind::Value,
11276    }
11277}
11278
11279fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
11280    node.child_by_field_name("default_type")
11281        .or_else(|| node.child_by_field_name("default_value"))
11282}
11283
11284fn cpp_template_parameter_default_expression<'tree>(
11285    parameter: Node<'tree>,
11286    source: &str,
11287    parameter_names: &[String],
11288    ancestry: &ParentIndex<'tree>,
11289) -> Option<CppTemplateExpression> {
11290    let default = cpp_template_parameter_default(parameter)?;
11291    let base = cpp_template_expression(default, source, parameter_names, ancestry);
11292    let Some(pointer_error) = parameter.next_named_sibling() else {
11293        return Some(base);
11294    };
11295    let Some(pointer_declarator) =
11296        recovered_abstract_pointer_declarator_term(pointer_error, source)
11297    else {
11298        return Some(base);
11299    };
11300    Some(CppTemplateExpression {
11301        text: format!(
11302            "{}{}",
11303            base.text,
11304            normalize_cpp_whitespace(node_text(pointer_error, source))
11305        ),
11306        term: CppTemplateTerm::Node {
11307            kind: "type_descriptor".to_string(),
11308            children: vec![base.term, pointer_declarator],
11309        },
11310    })
11311}
11312
11313fn recovered_abstract_pointer_declarator_term(
11314    node: Node<'_>,
11315    source: &str,
11316) -> Option<CppTemplateTerm> {
11317    if node.kind() != "ERROR" || node.child_count() == 0 {
11318        return None;
11319    }
11320    let mut children = Vec::new();
11321    for index in 0..node.child_count() {
11322        let child = node.child(index)?;
11323        if child.kind() != "*" {
11324            return None;
11325        }
11326        children.push(CppTemplateTerm::Atom {
11327            kind: "*".to_string(),
11328            text: normalize_cpp_whitespace(node_text(child, source)),
11329        });
11330    }
11331    Some(CppTemplateTerm::Node {
11332        kind: "abstract_pointer_declarator".to_string(),
11333        children,
11334    })
11335}
11336
11337fn cpp_template_expression<'tree>(
11338    node: Node<'tree>,
11339    source: &str,
11340    parameter_names: &[String],
11341    ancestry: &ParentIndex<'tree>,
11342) -> CppTemplateExpression {
11343    let text = normalize_cpp_whitespace(node_text(node, source));
11344    CppTemplateExpression {
11345        text,
11346        term: cpp_template_term(node, source, parameter_names, ancestry),
11347    }
11348}
11349
11350pub fn cpp_template_term<'tree>(
11351    node: Node<'tree>,
11352    source: &str,
11353    parameter_names: &[String],
11354    ancestry: &ParentIndex<'tree>,
11355) -> CppTemplateTerm {
11356    enum Work<'tree> {
11357        Visit(Node<'tree>),
11358        Build { kind: String, child_count: usize },
11359    }
11360
11361    let mut work = vec![Work::Visit(node)];
11362    let mut terms = Vec::new();
11363    while let Some(next) = work.pop() {
11364        match next {
11365            Work::Visit(current) => {
11366                let text = normalize_cpp_whitespace(node_text(current, source));
11367                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
11368                    terms.push(CppTemplateTerm::Parameter(text));
11369                    continue;
11370                }
11371                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
11372                    let mut cursor = current.walk();
11373                    let named = current
11374                        .named_children(&mut cursor)
11375                        .filter(|child| !child.is_extra() && child.kind() != "comment")
11376                        .collect::<Vec<_>>();
11377                    if let [child] = named.as_slice() {
11378                        work.push(Work::Visit(*child));
11379                        continue;
11380                    }
11381                }
11382                if current.child_count() == 0 {
11383                    terms.push(CppTemplateTerm::Atom {
11384                        kind: if matches!(
11385                            current.kind(),
11386                            "identifier"
11387                                | "type_identifier"
11388                                | "field_identifier"
11389                                | "namespace_identifier"
11390                        ) {
11391                            "identifier".to_string()
11392                        } else {
11393                            current.kind().to_string()
11394                        },
11395                        text,
11396                    });
11397                    continue;
11398                }
11399                let children = (0..current.child_count())
11400                    .filter_map(|index| current.child(index))
11401                    .filter(|child| !child.is_extra() && child.kind() != "comment")
11402                    .collect::<Vec<_>>();
11403                work.push(Work::Build {
11404                    kind: current.kind().to_string(),
11405                    child_count: children.len(),
11406                });
11407                work.extend(children.into_iter().rev().map(Work::Visit));
11408            }
11409            Work::Build { kind, child_count } => {
11410                let children = terms.split_off(terms.len() - child_count);
11411                terms.push(CppTemplateTerm::Node { kind, children });
11412            }
11413        }
11414    }
11415    terms.pop().expect("template term traversal emits one root")
11416}
11417
11418fn cpp_template_term_leaf_is_parameter<'tree>(
11419    node: Node<'tree>,
11420    text: &str,
11421    parameter_names: &[String],
11422    ancestry: &ParentIndex<'tree>,
11423) -> bool {
11424    if !parameter_names.iter().any(|parameter| parameter == text) {
11425        return false;
11426    }
11427    !ancestry.parent(node).is_some_and(|parent| {
11428        matches!(
11429            parent.kind(),
11430            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
11431        ) && parent.child_by_field_name("scope").is_some()
11432            && parent.child_by_field_name("name") == Some(node)
11433    })
11434}
11435
11436fn enclosing_cpp_declaration_node<'tree>(
11437    mut node: Node<'tree>,
11438    ancestry: &ParentIndex<'tree>,
11439) -> Option<Node<'tree>> {
11440    loop {
11441        match node.kind() {
11442            "declaration"
11443            | "function_declaration"
11444            | "field_declaration"
11445            | "function_definition" => return Some(node),
11446            _ => node = ancestry.parent(node)?,
11447        }
11448    }
11449}
11450
11451fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
11452    let mut params = Vec::new();
11453    let mut cursor = parameters_node.walk();
11454    for child in parameters_node.children(&mut cursor) {
11455        match child.kind() {
11456            "parameter_declaration" | "optional_parameter_declaration" => {
11457                params.push(cpp_parameter_type(child, source));
11458            }
11459            "variadic_parameter_declaration" => {
11460                params.push(cpp_parameter_type(child, source));
11461            }
11462            "variadic_parameter" | "..." => params.push("...".to_string()),
11463            _ => {}
11464        }
11465    }
11466
11467    if params.is_empty() {
11468        "()".to_string()
11469    } else {
11470        format!("({})", params.join(", "))
11471    }
11472}
11473
11474fn cpp_signature_metadata<'tree>(
11475    signature: String,
11476    function_declarator: Node<'tree>,
11477    source: &str,
11478    ancestry: &ParentIndex<'tree>,
11479) -> SignatureMetadata {
11480    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
11481    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
11482    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
11483    let return_type_identity =
11484        cpp_callable_return_type_identity(function_declarator, source, ancestry);
11485    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
11486        return enrich(
11487            SignatureMetadata::new(signature, Vec::new())
11488                .with_return_type_text(return_type_text)
11489                .with_return_type_identity(return_type_identity),
11490        );
11491    };
11492    let callable_arity = cpp_callable_arity(parameters_node, source);
11493    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
11494    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
11495    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
11496    let Some(relative_start) = signature
11497        .get(search_from..)
11498        .and_then(|suffix| suffix.find(&parameter_text))
11499    else {
11500        return enrich(
11501            SignatureMetadata::new(signature, Vec::new())
11502                .with_callable_arity(callable_arity)
11503                .with_callable_parameter_types(callable_parameter_types)
11504                .with_return_type_text(return_type_text)
11505                .with_return_type_identity(return_type_identity),
11506        );
11507    };
11508    let parameters_start = search_from + relative_start;
11509    let parameters_end = parameters_start + parameter_text.len();
11510    let mut search_start = parameters_start;
11511    let parameters = cpp_parameter_label_nodes(parameters_node)
11512        .into_iter()
11513        .filter_map(|label_node| {
11514            let label = normalize_cpp_whitespace(node_text(label_node, source));
11515            if label.is_empty() || search_start > parameters_end {
11516                return None;
11517            }
11518            let haystack = signature.get(search_start..parameters_end)?;
11519            let relative_start = haystack.find(&label)?;
11520            let start_byte = search_start + relative_start;
11521            let end_byte = start_byte + label.len();
11522            search_start = end_byte;
11523            Some(ParameterMetadata::new(label, start_byte, end_byte))
11524        })
11525        .collect();
11526    enrich(
11527        SignatureMetadata::new(signature, parameters)
11528            .with_callable_arity(callable_arity)
11529            .with_callable_parameter_types(callable_parameter_types)
11530            .with_return_type_text(return_type_text)
11531            .with_return_type_identity(return_type_identity),
11532    )
11533}
11534
11535fn cpp_callable_is_structural_constructor<'tree>(
11536    function_declarator: Node<'tree>,
11537    source: &str,
11538    ancestry: &ParentIndex<'tree>,
11539) -> bool {
11540    let Some(name_node) = function_declarator
11541        .child_by_field_name("declarator")
11542        .or_else(|| function_declarator.child_by_field_name("name"))
11543        .or_else(|| last_named_child(function_declarator))
11544    else {
11545        return false;
11546    };
11547    let Some(callable_name) = direct_identifier_name(name_node, source) else {
11548        return false;
11549    };
11550
11551    let mut current = ancestry.parent(function_declarator);
11552    while let Some(ancestor) = current {
11553        let owner_name = match ancestor.kind() {
11554            "class_specifier" | "struct_specifier" | "union_specifier" => {
11555                class_like_name(ancestor, source, ancestry)
11556            }
11557            "ERROR" => malformed_class_error_owner_name(ancestor, source),
11558            _ => None,
11559        };
11560        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
11561            return true;
11562        }
11563        current = ancestry.parent(ancestor);
11564    }
11565    false
11566}
11567
11568/// Recover the owner name from the direct grammar shape retained when a later
11569/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
11570/// `ERROR` node:
11571///
11572/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
11573///
11574/// Direct-child checks keep this distinct from an unrelated nested class inside
11575/// a broader error region. The closing brace may be displaced past the error
11576/// node, so the opening body token is the available structural boundary.
11577fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
11578    if node.kind() != "ERROR" {
11579        return None;
11580    }
11581    let keyword = node.child(0)?;
11582    if !matches!(keyword.kind(), "class" | "struct" | "union") {
11583        return None;
11584    }
11585    let name_node = node.child(1)?;
11586    let name = direct_identifier_name(name_node, source)?;
11587    let has_body = (2..node.child_count())
11588        .filter_map(|index| node.child(index))
11589        .any(|child| child.kind() == "{");
11590    has_body.then_some(name)
11591}
11592
11593/// The parser-derived return type of one callable declaration.
11594///
11595/// This accepts the enclosing declaration node so consumers do not need to
11596/// duplicate the declarator-unwrapping rules before asking for the structured
11597/// identity.
11598pub fn cpp_callable_declaration_return_type_identity<'tree>(
11599    callable: Node<'tree>,
11600    source: &str,
11601    ancestry: &ParentIndex<'tree>,
11602) -> Option<StructuredTypeIdentity> {
11603    let declarator = callable
11604        .child_by_field_name("declarator")
11605        .and_then(extract_function_declarator)?;
11606    cpp_callable_return_type_identity(declarator, source, ancestry)
11607}
11608
11609pub(crate) fn cpp_callable_return_type_identity<'tree>(
11610    function_declarator: Node<'tree>,
11611    source: &str,
11612    ancestry: &ParentIndex<'tree>,
11613) -> Option<StructuredTypeIdentity> {
11614    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
11615        return None;
11616    }
11617    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
11618    if let Some((return_type, _)) =
11619        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
11620    {
11621        return cpp_structured_type_identity(return_type, source, &lexical_scope);
11622    }
11623    let mut cursor = function_declarator.walk();
11624    if let Some(trailing) = function_declarator
11625        .named_children(&mut cursor)
11626        .find(|child| child.kind() == "trailing_return_type")
11627        && let Some(type_descriptor) = trailing.named_child(0)
11628    {
11629        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
11630    }
11631
11632    let mut current = function_declarator;
11633    let mut wrappers = Vec::new();
11634    while let Some(parent) = ancestry.parent(current) {
11635        if matches!(
11636            parent.kind(),
11637            "function_definition" | "declaration" | "field_declaration"
11638        ) {
11639            let type_node = parent.child_by_field_name("type")?;
11640            if cpp_export_macro_token(node_text(type_node, source))
11641                && (0..parent.named_child_count()).any(|index| {
11642                    parent
11643                        .named_child(index)
11644                        .is_some_and(|child| child.kind() == "ERROR")
11645                })
11646            {
11647                return None;
11648            }
11649            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
11650            for wrapper in wrappers.into_iter().rev() {
11651                identity = cpp_wrap_structured_type(identity, wrapper)?;
11652            }
11653            return Some(identity);
11654        }
11655        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
11656            || (matches!(
11657                parent.kind(),
11658                "pointer_declarator"
11659                    | "reference_declarator"
11660                    | "array_declarator"
11661                    | "parenthesized_declarator"
11662            ) && parent.named_child_count() == 1
11663                && parent.named_child(0) == Some(current));
11664        if !wraps_current_declarator {
11665            return None;
11666        }
11667        match parent.kind() {
11668            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
11669            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
11670            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
11671            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
11672            _ => return None,
11673        }
11674        current = parent;
11675    }
11676    None
11677}
11678
11679fn cpp_structured_type_identity(
11680    node: Node<'_>,
11681    source: &str,
11682    lexical_scope: &[String],
11683) -> Option<StructuredTypeIdentity> {
11684    enum Work<'tree> {
11685        Visit(Node<'tree>),
11686        Wrap(CppStructuredTypeWrapper),
11687        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
11688        BuildGeneric { argument_count: usize },
11689    }
11690
11691    let mut work = vec![Work::Visit(node)];
11692    let mut values = Vec::new();
11693    let mut builder = StructuredTypeIdentityBuilder::default();
11694    while let Some(next) = work.pop() {
11695        match next {
11696            Work::Visit(current) => match current.kind() {
11697                "type_descriptor" => {
11698                    let type_node = current
11699                        .child_by_field_name("type")
11700                        .or_else(|| current.named_child(0))?;
11701                    let mut wrappers = Vec::new();
11702                    let mut cursor = current.walk();
11703                    for child in current.named_children(&mut cursor) {
11704                        if child.id() != type_node.id() {
11705                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
11706                        }
11707                    }
11708                    work.push(Work::ApplyWrappers(wrappers));
11709                    work.push(Work::Visit(type_node));
11710                }
11711                "pointer_declarator" | "abstract_pointer_declarator" => {
11712                    let child = current
11713                        .child_by_field_name("declarator")
11714                        .or_else(|| current.named_child(0))?;
11715                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
11716                    work.push(Work::Visit(child));
11717                }
11718                "reference_declarator" => {
11719                    let child = current
11720                        .child_by_field_name("declarator")
11721                        .or_else(|| current.named_child(0))?;
11722                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
11723                    work.push(Work::Visit(child));
11724                }
11725                "array_declarator" | "abstract_array_declarator" => {
11726                    let child = current
11727                        .child_by_field_name("declarator")
11728                        .or_else(|| current.named_child(0))?;
11729                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
11730                    work.push(Work::Visit(child));
11731                }
11732                "template_type" => {
11733                    let name_node = current.child_by_field_name("name")?;
11734                    let arguments = current
11735                        .child_by_field_name("arguments")
11736                        .map(|arguments_node| {
11737                            let mut cursor = arguments_node.walk();
11738                            arguments_node
11739                                .named_children(&mut cursor)
11740                                .filter(|child| !child.is_extra() && child.kind() != "comment")
11741                                .collect::<Vec<_>>()
11742                        })
11743                        .unwrap_or_default();
11744                    work.push(Work::BuildGeneric {
11745                        argument_count: arguments.len(),
11746                    });
11747                    work.extend(arguments.into_iter().rev().map(Work::Visit));
11748                    work.push(Work::Visit(name_node));
11749                }
11750                "qualified_identifier"
11751                | "scoped_identifier"
11752                | "scoped_type_identifier"
11753                | "type_identifier"
11754                | "field_identifier"
11755                | "identifier"
11756                | "namespace_identifier"
11757                | "primitive_type" => {
11758                    values.push(builder.named(cpp_structured_named_type(
11759                        current,
11760                        source,
11761                        lexical_scope,
11762                    )?)?);
11763                }
11764                _ => {
11765                    let child = current.child_by_field_name("type").or_else(|| {
11766                        (current.named_child_count() == 1)
11767                            .then(|| current.named_child(0))
11768                            .flatten()
11769                    })?;
11770                    work.push(Work::Visit(child));
11771                }
11772            },
11773            Work::Wrap(wrapper) => {
11774                let root = values.pop()?;
11775                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
11776            }
11777            Work::ApplyWrappers(wrappers) => {
11778                let mut root = values.pop()?;
11779                for wrapper in wrappers.into_iter().rev() {
11780                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
11781                }
11782                values.push(root);
11783            }
11784            Work::BuildGeneric { argument_count } => {
11785                let value_count = argument_count.checked_add(1)?;
11786                let start = values.len().checked_sub(value_count)?;
11787                let mut built = values.split_off(start);
11788                let base = built.remove(0);
11789                values.push(builder.generic(base, built)?);
11790            }
11791        }
11792    }
11793    (values.len() == 1)
11794        .then(|| values.pop())
11795        .flatten()
11796        .and_then(|root| builder.finish(root))
11797}
11798
11799fn cpp_structured_named_type(
11800    node: Node<'_>,
11801    source: &str,
11802    lexical_scope: &[String],
11803) -> Option<StructuredTypeName> {
11804    let path = cpp_structured_type_path(node, source)?;
11805    let absolute = node.child_by_field_name("scope").is_none()
11806        && node.child(0).is_some_and(|child| child.kind() == "::");
11807    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
11808}
11809
11810#[derive(Clone, Copy)]
11811enum CppStructuredTypeWrapper {
11812    Pointer,
11813    LvalueReference,
11814    RvalueReference,
11815    Array,
11816}
11817
11818fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
11819    let mut wrappers = Vec::new();
11820    let mut current = node;
11821    loop {
11822        match current.kind() {
11823            "pointer_declarator" | "abstract_pointer_declarator" => {
11824                wrappers.push(CppStructuredTypeWrapper::Pointer)
11825            }
11826            "reference_declarator" | "abstract_reference_declarator" => {
11827                wrappers.push(cpp_reference_wrapper(current)?);
11828            }
11829            "array_declarator" | "abstract_array_declarator" => {
11830                wrappers.push(CppStructuredTypeWrapper::Array)
11831            }
11832            _ => break,
11833        }
11834        let Some(child) = current
11835            .child_by_field_name("declarator")
11836            .or_else(|| current.named_child(0))
11837        else {
11838            break;
11839        };
11840        current = child;
11841    }
11842    Some(wrappers)
11843}
11844
11845fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
11846    node.children(&mut node.walk())
11847        .find_map(|child| match child.kind() {
11848            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
11849            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
11850            _ => None,
11851        })
11852}
11853
11854fn cpp_wrap_structured_type(
11855    identity: StructuredTypeIdentity,
11856    wrapper: CppStructuredTypeWrapper,
11857) -> Option<StructuredTypeIdentity> {
11858    match wrapper {
11859        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
11860        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
11861        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
11862        CppStructuredTypeWrapper::Array => identity.wrap_array(),
11863    }
11864}
11865
11866fn cpp_wrap_structured_type_node(
11867    builder: &mut StructuredTypeIdentityBuilder,
11868    inner: StructuredTypeNodeId,
11869    wrapper: CppStructuredTypeWrapper,
11870) -> Option<StructuredTypeNodeId> {
11871    match wrapper {
11872        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
11873        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
11874        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
11875        CppStructuredTypeWrapper::Array => builder.array(inner),
11876    }
11877}
11878
11879fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
11880    let mut path = Vec::new();
11881    let mut stack = vec![node];
11882    while let Some(current) = stack.pop() {
11883        match current.kind() {
11884            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
11885                let component = node_text(current, source).to_string();
11886                if component.is_empty() {
11887                    return None;
11888                }
11889                path.push(component);
11890            }
11891            "template_type" | "dependent_type" => {
11892                stack.push(current.child_by_field_name("name")?);
11893            }
11894            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11895                stack.push(current.child_by_field_name("name")?);
11896                if let Some(scope) = current.child_by_field_name("scope") {
11897                    stack.push(scope);
11898                }
11899            }
11900            _ => return None,
11901        }
11902    }
11903    (!path.is_empty()).then_some(path)
11904}
11905
11906fn cpp_callable_lexical_scope<'tree>(
11907    node: Node<'tree>,
11908    source: &str,
11909    ancestry: &ParentIndex<'tree>,
11910) -> Vec<String> {
11911    let mut groups = Vec::new();
11912    let mut current = ancestry.parent(node);
11913    while let Some(parent) = current {
11914        if matches!(
11915            parent.kind(),
11916            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
11917        ) && let Some(name_node) = parent.child_by_field_name("name")
11918            && let Some(components) = cpp_structured_type_path(name_node, source)
11919            && !components.is_empty()
11920        {
11921            groups.push(components);
11922        }
11923        current = ancestry.parent(parent);
11924    }
11925    groups.reverse();
11926    groups.into_iter().flatten().collect()
11927}
11928
11929fn cpp_callable_dispatch_extensibility<'tree>(
11930    function_declarator: Node<'tree>,
11931    ancestry: &ParentIndex<'tree>,
11932) -> DispatchExtensibility {
11933    let mut declaration = None;
11934    let mut current = Some(function_declarator);
11935    while let Some(node) = current {
11936        match node.kind() {
11937            "template_declaration"
11938            | "preproc_if"
11939            | "preproc_ifdef"
11940            | "preproc_else"
11941            | "preproc_elif"
11942            | "preproc_call"
11943            | "ERROR" => return DispatchExtensibility::Open,
11944            "declaration" | "field_declaration" | "function_definition" => {
11945                declaration.get_or_insert(node);
11946            }
11947            "translation_unit" => break,
11948            _ => {}
11949        }
11950        current = ancestry.parent(node);
11951    }
11952    let Some(declaration) = declaration else {
11953        return DispatchExtensibility::Open;
11954    };
11955
11956    let mut saw_virtual_boundary = false;
11957    let mut stack = vec![declaration];
11958    while let Some(node) = stack.pop() {
11959        match node.kind() {
11960            "compound_statement" | "field_declaration_list" => continue,
11961            "final" | "final_specifier" => return DispatchExtensibility::Closed,
11962            "virtual"
11963            | "override"
11964            | "virtual_specifier"
11965            | "pure_virtual_clause"
11966            | "template_parameter_list"
11967            | "template_method"
11968            | "template_function"
11969            | "ERROR" => saw_virtual_boundary = true,
11970            _ => {}
11971        }
11972        let mut cursor = node.walk();
11973        stack.extend(node.children(&mut cursor));
11974    }
11975
11976    if saw_virtual_boundary {
11977        DispatchExtensibility::Open
11978    } else {
11979        DispatchExtensibility::Closed
11980    }
11981}
11982
11983fn cpp_callable_linkage<'tree>(
11984    declaration: Node<'tree>,
11985    source: &str,
11986    ancestry: &ParentIndex<'tree>,
11987) -> CallableLinkage {
11988    let mut enclosed_by_class = false;
11989    let mut current = ancestry.parent(declaration);
11990    while let Some(node) = current {
11991        if node.kind() == "namespace_definition"
11992            && node
11993                .child_by_field_name("name")
11994                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11995        {
11996            return CallableLinkage::Internal;
11997        }
11998        if matches!(
11999            node.kind(),
12000            "class_specifier" | "struct_specifier" | "union_specifier"
12001        ) {
12002            if node
12003                .child_by_field_name("name")
12004                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12005            {
12006                return CallableLinkage::Internal;
12007            }
12008            enclosed_by_class = true;
12009        }
12010        // An export macro between `class` and the class name can make
12011        // tree-sitter parse the entire class as a function definition. The
12012        // structured recovery proves that this container is a named class
12013        // scope, not a local callable scope.
12014        if node.kind() == "lambda_expression"
12015            || node.kind() == "function_definition"
12016                && !is_recovered_exported_class_container(node, source)
12017        {
12018            return CallableLinkage::Internal;
12019        }
12020        current = ancestry.parent(node);
12021    }
12022
12023    if enclosed_by_class {
12024        return CallableLinkage::External;
12025    }
12026
12027    let mut cursor = declaration.walk();
12028    if declaration.named_children(&mut cursor).any(|child| {
12029        child.kind() == "storage_class_specifier"
12030            && normalize_cpp_whitespace(node_text(child, source)) == "static"
12031    }) {
12032        CallableLinkage::Internal
12033    } else {
12034        CallableLinkage::External
12035    }
12036}
12037
12038fn cpp_callable_return_type_text<'tree>(
12039    function_declarator: Node<'tree>,
12040    source: &str,
12041    ancestry: &ParentIndex<'tree>,
12042) -> Option<String> {
12043    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
12044        return None;
12045    }
12046    if let Some((return_type, _)) =
12047        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
12048    {
12049        let text = normalize_cpp_whitespace(node_text(return_type, source));
12050        return (!text.is_empty()).then_some(text);
12051    }
12052    let mut cursor = function_declarator.walk();
12053    if let Some(trailing) = function_declarator
12054        .named_children(&mut cursor)
12055        .find(|child| child.kind() == "trailing_return_type")
12056        && let Some(type_descriptor) = trailing.named_child(0)
12057    {
12058        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
12059        if !text.is_empty() {
12060            return Some(text);
12061        }
12062    }
12063
12064    let mut current = function_declarator;
12065    let mut indirection = String::new();
12066    while let Some(parent) = ancestry.parent(current) {
12067        if matches!(
12068            parent.kind(),
12069            "function_definition" | "declaration" | "field_declaration"
12070        ) {
12071            let type_node = parent.child_by_field_name("type")?;
12072            if cpp_export_macro_token(node_text(type_node, source))
12073                && (0..parent.named_child_count()).any(|index| {
12074                    parent
12075                        .named_child(index)
12076                        .is_some_and(|child| child.kind() == "ERROR")
12077                })
12078            {
12079                // Export/decorator macros commonly occupy the grammar's `type`
12080                // field and leave the semantic return type in an ERROR sibling.
12081                // Do not persist the macro token as a return type. The malformed
12082                // declaration does not carry enough structured evidence here.
12083                return None;
12084            }
12085            let base = normalize_cpp_whitespace(node_text(type_node, source));
12086            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
12087        }
12088        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
12089            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
12090                && parent.named_child_count() == 1
12091                && parent.named_child(0) == Some(current));
12092        if wraps_current_declarator {
12093            match parent.kind() {
12094                "pointer_declarator" => indirection.push('*'),
12095                "reference_declarator" => {
12096                    let reference = parent
12097                        .children(&mut parent.walk())
12098                        .find(|child| !child.is_named())
12099                        .map(|child| node_text(child, source))
12100                        .unwrap_or("&");
12101                    indirection.push_str(reference);
12102                }
12103                "init_declarator" | "parenthesized_declarator" => {}
12104                _ => return None,
12105            }
12106            current = parent;
12107            continue;
12108        }
12109        return None;
12110    }
12111    None
12112}
12113
12114fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
12115    let mut required = 0;
12116    let mut total = 0;
12117    let mut repeated = false;
12118    let mut cursor = parameters_node.walk();
12119    for child in parameters_node.children(&mut cursor) {
12120        match child.kind() {
12121            "parameter_declaration" => {
12122                if cpp_parameter_is_explicit_object(child, source) {
12123                    continue;
12124                }
12125                if child.child_by_field_name("declarator").is_none()
12126                    && child
12127                        .child_by_field_name("type")
12128                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
12129                {
12130                    continue;
12131                }
12132                required += 1;
12133                total += 1;
12134            }
12135            "optional_parameter_declaration" => total += 1,
12136            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12137                repeated = true;
12138            }
12139            _ => {}
12140        }
12141    }
12142    CallableArity::new(required, total, repeated)
12143}
12144
12145fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
12146    parameter
12147        .child_by_field_name("type")
12148        .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
12149        .and_then(|type_node| type_node.child_by_field_name("constraint"))
12150        .is_some_and(|constraint| {
12151            constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
12152        })
12153}
12154
12155/// One entry of a callable's invocation parameter list.
12156///
12157/// The list excludes an explicit object parameter and a lone `void`, so its
12158/// length is the callable's invocation arity. Every derivation of a parameter
12159/// type - the rendered spelling used for overload discrimination and the
12160/// structured identity used by dependency-pack production - starts from this
12161/// same sequence, so the two can never disagree about which parameters exist.
12162#[derive(Clone, Copy)]
12163enum CppParameterSlot<'tree> {
12164    Declared(Node<'tree>),
12165    Ellipsis,
12166}
12167
12168fn cpp_callable_parameter_slots<'tree>(
12169    parameters_node: Node<'tree>,
12170    source: &str,
12171) -> Vec<CppParameterSlot<'tree>> {
12172    let mut slots = Vec::new();
12173    let mut cursor = parameters_node.walk();
12174    for parameter in parameters_node.children(&mut cursor) {
12175        match parameter.kind() {
12176            "parameter_declaration" | "optional_parameter_declaration" => {
12177                if cpp_parameter_is_explicit_object(parameter, source)
12178                    || (parameter.child_by_field_name("declarator").is_none()
12179                        && parameter
12180                            .child_by_field_name("type")
12181                            .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
12182                {
12183                    continue;
12184                }
12185                slots.push(CppParameterSlot::Declared(parameter));
12186            }
12187            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12188                slots.push(CppParameterSlot::Ellipsis);
12189            }
12190            _ => {}
12191        }
12192    }
12193    slots
12194}
12195
12196fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
12197    cpp_callable_parameter_slots(parameters_node, source)
12198        .into_iter()
12199        .map(|slot| match slot {
12200            CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
12201            CppParameterSlot::Ellipsis => "...".to_string(),
12202        })
12203        .collect()
12204}
12205
12206/// One callable parameter's parser-derived type.
12207///
12208/// A rendered spelling such as `const T&` is a source text, not a type name. A
12209/// consumer that must publish a type into a structured model - a semantic-pack
12210/// type reference, for example - reads this instead.
12211#[derive(Debug, Clone, PartialEq, Eq)]
12212pub enum CppParameterType {
12213    /// The written type reduced to a structured identity. C++ cv-qualifiers
12214    /// have no place in that model and are not represented.
12215    Structured(StructuredTypeIdentity),
12216    /// A `...` pack, which declares no parameter type at all.
12217    Ellipsis,
12218    /// A written type with no structured reduction, such as a macro-obscured,
12219    /// `decltype`-computed, or function-pointer parameter.
12220    Unstructured,
12221}
12222
12223/// The structured type of each invocation parameter, in declaration order.
12224///
12225/// The result is index-parallel with the rendered
12226/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
12227/// callable.
12228pub fn cpp_callable_parameter_type_identities<'tree>(
12229    function_declarator: Node<'tree>,
12230    source: &str,
12231    ancestry: &ParentIndex<'tree>,
12232) -> Vec<CppParameterType> {
12233    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12234        return Vec::new();
12235    };
12236    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12237    cpp_callable_parameter_slots(parameters_node, source)
12238        .into_iter()
12239        .map(|slot| match slot {
12240            CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
12241            CppParameterSlot::Declared(parameter) => {
12242                cpp_parameter_type_identity(parameter, source, &lexical_scope)
12243                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
12244            }
12245        })
12246        .collect()
12247}
12248
12249/// The parser-derived type of one declared object or parameter.
12250///
12251/// The declaration owns the base `type` field while the individual declarator
12252/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
12253/// lets callers distinguish several declarators in one declaration without
12254/// reparsing a rendered signature.
12255pub fn cpp_declaration_type_identity<'tree>(
12256    declaration: Node<'tree>,
12257    declarator: Node<'tree>,
12258    source: &str,
12259    ancestry: &ParentIndex<'tree>,
12260) -> Option<StructuredTypeIdentity> {
12261    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
12262    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
12263}
12264
12265fn cpp_parameter_type_identity(
12266    parameter: Node<'_>,
12267    source: &str,
12268    lexical_scope: &[String],
12269) -> Option<StructuredTypeIdentity> {
12270    cpp_declaration_type_identity_in_scope(
12271        parameter,
12272        cpp_parameter_declarator(parameter),
12273        source,
12274        lexical_scope,
12275    )
12276}
12277
12278fn cpp_declaration_type_identity_in_scope(
12279    declaration: Node<'_>,
12280    declarator: Option<Node<'_>>,
12281    source: &str,
12282    lexical_scope: &[String],
12283) -> Option<StructuredTypeIdentity> {
12284    let type_node = declaration.child_by_field_name("type")?;
12285    let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
12286    if let Some(declarator) = declarator {
12287        for wrapper in cpp_structured_declarator_wrappers(declarator)?
12288            .into_iter()
12289            .rev()
12290        {
12291            identity = cpp_wrap_structured_type(identity, wrapper)?;
12292        }
12293    }
12294    Some(identity)
12295}
12296
12297/// One callable parameter's comparable shape.
12298///
12299/// [`CppParameterType`] above answers "which type is written here" for a
12300/// structured model and deliberately records no cv-qualifiers, so it reports
12301/// the same value for `f(char *)` and `f(const char *)`. Deciding whether two
12302/// callable declarations declare one function needs the opposite trade: every
12303/// cv-qualifier that C++ counts as part of the parameter type must survive,
12304/// while the two declarations may spell the same type through different
12305/// qualifications. This slot carries that comparand.
12306///
12307/// The result is index-parallel with [`cpp_callable_parameter_type_identities`]
12308/// and with the rendered parameter spellings of the same callable.
12309#[derive(Debug, Clone, PartialEq, Eq)]
12310pub enum CppComparableSlot {
12311    /// A declared parameter reduced to its comparable shape.
12312    Shape(CppComparableParameter),
12313    /// A `...` pack, which declares no parameter type at all.
12314    Ellipsis,
12315    /// A parameter with no comparable reduction, such as a macro-obscured,
12316    /// `decltype`-computed, or function-pointer parameter.
12317    Unstructured,
12318}
12319
12320/// A parameter type as a flat arena of nodes plus a root index.
12321///
12322/// The arena carries the same rationale as [`StructuredTypeIdentity`]: source
12323/// can nest types very deeply, and cloning, comparing or dropping the value
12324/// must not consume the Rust call stack. Nodes are appended in post-order, so
12325/// every child index is smaller than its parent's and the last appended node is
12326/// the root.
12327///
12328/// That post-order append is also what makes the derived `PartialEq` a correct
12329/// structural equality: the builder below is deterministic, so one type shape
12330/// has exactly one arena layout no matter which spelling produced it. Two
12331/// shapes are equal as values iff they are equal as type trees.
12332#[derive(Debug, Clone, PartialEq, Eq)]
12333pub struct CppComparableParameter {
12334    nodes: Vec<CppComparableNode>,
12335    root: usize,
12336}
12337
12338/// One node of a [`CppComparableParameter`] arena.
12339///
12340/// `Reference` and `Array` carry no qualifiers because the grammar writes none
12341/// on them: a reference cannot be cv-qualified in C++, and an array's
12342/// qualifiers belong to its element type. A cv-qualifier written on a generic
12343/// type (`const std::vector<int>`) is recorded on the generic's base leaf,
12344/// which is the only Named node the whole spelling produces.
12345#[derive(Debug, Clone, PartialEq, Eq)]
12346pub enum CppComparableNode {
12347    Named {
12348        name: StructuredTypeName,
12349        primitive: bool,
12350        konst: bool,
12351        volatil: bool,
12352    },
12353    Pointer {
12354        inner: usize,
12355        konst: bool,
12356        volatil: bool,
12357    },
12358    Reference {
12359        inner: usize,
12360    },
12361    Array {
12362        inner: usize,
12363    },
12364    Generic {
12365        base: usize,
12366        arguments: Vec<usize>,
12367    },
12368}
12369
12370impl CppComparableParameter {
12371    pub fn root(&self) -> usize {
12372        self.root
12373    }
12374
12375    pub fn node(&self, index: usize) -> &CppComparableNode {
12376        &self.nodes[index]
12377    }
12378
12379    /// Apply the [dcl.fct]/5 parameter-type adjustments, which hold at the
12380    /// parameter's top level only.
12381    ///
12382    /// A top-level cv-qualifier is discarded, so `f(const int)` and `f(int)`
12383    /// declare one function, and a top-level array type becomes a pointer to
12384    /// its element type, so `f(int[3])` and `f(int *)` do too. The outermost
12385    /// type constructor is this arena's root, which is why both adjustments
12386    /// are one match on it: cv on an inner pointer level, on a pointee, or on
12387    /// an array element keeps distinguishing the type, and an array behind a
12388    /// pointer or reference is not a top-level array.
12389    fn adjust_parameter_top_level(&mut self) {
12390        let root = self.root;
12391        match &mut self.nodes[root] {
12392            CppComparableNode::Named { konst, volatil, .. }
12393            | CppComparableNode::Pointer { konst, volatil, .. } => {
12394                *konst = false;
12395                *volatil = false;
12396            }
12397            CppComparableNode::Array { inner } => {
12398                let inner = *inner;
12399                self.nodes[root] = CppComparableNode::Pointer {
12400                    inner,
12401                    konst: false,
12402                    volatil: false,
12403                };
12404            }
12405            CppComparableNode::Generic { base, .. } => {
12406                let base = *base;
12407                let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
12408                    unreachable!("a comparable generic's base is always a named leaf");
12409                };
12410                *konst = false;
12411                *volatil = false;
12412            }
12413            CppComparableNode::Reference { .. } => {}
12414        }
12415    }
12416}
12417
12418/// The comparable shape of each invocation parameter, in declaration order.
12419///
12420/// The result is index-parallel with
12421/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
12422/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
12423/// must treat as evidence of nothing rather than as agreement.
12424pub fn cpp_comparable_parameter_shapes<'tree>(
12425    function_declarator: Node<'tree>,
12426    source: &str,
12427    ancestry: &ParentIndex<'tree>,
12428) -> Vec<CppComparableSlot> {
12429    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12430        return Vec::new();
12431    };
12432    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12433    cpp_callable_parameter_slots(parameters_node, source)
12434        .into_iter()
12435        .map(|slot| match slot {
12436            CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
12437            CppParameterSlot::Declared(parameter) => {
12438                cpp_comparable_parameter(parameter, source, &lexical_scope)
12439                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
12440            }
12441        })
12442        .collect()
12443}
12444
12445fn cpp_comparable_parameter(
12446    parameter: Node<'_>,
12447    source: &str,
12448    lexical_scope: &[String],
12449) -> Option<CppComparableParameter> {
12450    let type_node = parameter.child_by_field_name("type")?;
12451    let levels = match cpp_parameter_declarator(parameter) {
12452        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12453        None => Vec::new(),
12454    };
12455    let mut shape = cpp_comparable_type_shape(
12456        type_node,
12457        cpp_cv_qualifiers(parameter, source),
12458        levels,
12459        source,
12460        lexical_scope,
12461    )?;
12462    shape.adjust_parameter_top_level();
12463    Some(shape)
12464}
12465
12466/// The `const` and `volatile` qualifiers written as direct named children of
12467/// `node`.
12468///
12469/// The grammar exposes `type_qualifier` as a non-field named child in exactly
12470/// the three places a parameter's qualifiers can be written: on the
12471/// `parameter_declaration` itself (the base type), on a `type_descriptor`
12472/// (inside a template argument list), and on each `pointer_declarator` level
12473/// (the pointer object). Every other qualifier the grammar admits - `restrict`
12474/// and friends - takes no part in C++ type identity, the same filter
12475/// `cpp_parameter_type` applies to the rendered spelling (#1827).
12476fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
12477    let mut qualifiers = CppCvQualifiers::default();
12478    let mut cursor = node.walk();
12479    for child in node.named_children(&mut cursor) {
12480        if child.kind() != "type_qualifier" {
12481            continue;
12482        }
12483        match node_text(child, source) {
12484            "const" => qualifiers.konst = true,
12485            "volatile" => qualifiers.volatil = true,
12486            _ => {}
12487        }
12488    }
12489    qualifiers
12490}
12491
12492#[derive(Clone, Copy, Default)]
12493struct CppCvQualifiers {
12494    konst: bool,
12495    volatil: bool,
12496}
12497
12498impl CppCvQualifiers {
12499    fn union(self, other: Self) -> Self {
12500        Self {
12501            konst: self.konst || other.konst,
12502            volatil: self.volatil || other.volatil,
12503        }
12504    }
12505}
12506
12507/// One pointer, reference or array level a declarator chain adds.
12508#[derive(Clone, Copy)]
12509enum CppComparableLevel {
12510    Pointer { konst: bool, volatil: bool },
12511    Reference,
12512    Array,
12513}
12514
12515/// The levels `declarator` adds, outermost written level first.
12516///
12517/// C++ declarator syntax binds inside out: the level written closest to the
12518/// declared name is the outermost type constructor, and tree-sitter nests it
12519/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
12520/// applies in order to reach "array of pointer to int", and the qualifier of
12521/// `int * const *p` is read on the level it was written next to, the inner
12522/// pointer of the resulting type.
12523///
12524/// A declarator chain that names a function type - a function-pointer
12525/// parameter - has no comparable shape and reports `None`, matching the
12526/// structured identity channel.
12527fn cpp_comparable_declarator_levels(
12528    declarator: Node<'_>,
12529    source: &str,
12530) -> Option<Vec<CppComparableLevel>> {
12531    let mut levels = Vec::new();
12532    let mut current = declarator;
12533    loop {
12534        match current.kind() {
12535            "pointer_declarator" | "abstract_pointer_declarator" => {
12536                let qualifiers = cpp_cv_qualifiers(current, source);
12537                levels.push(CppComparableLevel::Pointer {
12538                    konst: qualifiers.konst,
12539                    volatil: qualifiers.volatil,
12540                });
12541            }
12542            "reference_declarator" | "abstract_reference_declarator" => {
12543                levels.push(CppComparableLevel::Reference);
12544            }
12545            "array_declarator" | "abstract_array_declarator" => {
12546                levels.push(CppComparableLevel::Array);
12547            }
12548            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
12549            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
12550            _ => return None,
12551        }
12552        let Some(next) = cpp_nested_declarator(current) else {
12553            return Some(levels);
12554        };
12555        current = next;
12556    }
12557}
12558
12559/// Reduce one written type to a comparable arena.
12560///
12561/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
12562/// two additions: each visited type node carries the cv-qualifiers written on
12563/// it, and declarator levels arrive as a prepared list rather than being
12564/// rediscovered inside the walk.
12565fn cpp_comparable_type_shape(
12566    type_node: Node<'_>,
12567    qualifiers: CppCvQualifiers,
12568    levels: Vec<CppComparableLevel>,
12569    source: &str,
12570    lexical_scope: &[String],
12571) -> Option<CppComparableParameter> {
12572    enum Work<'tree> {
12573        Visit {
12574            node: Node<'tree>,
12575            qualifiers: CppCvQualifiers,
12576        },
12577        ApplyLevels(Vec<CppComparableLevel>),
12578        BuildGeneric {
12579            argument_count: usize,
12580        },
12581    }
12582
12583    let mut nodes: Vec<CppComparableNode> = Vec::new();
12584    let mut values: Vec<usize> = Vec::new();
12585    let mut work = vec![
12586        Work::ApplyLevels(levels),
12587        Work::Visit {
12588            node: type_node,
12589            qualifiers,
12590        },
12591    ];
12592    while let Some(next) = work.pop() {
12593        match next {
12594            Work::Visit { node, qualifiers } => match node.kind() {
12595                "type_descriptor" => {
12596                    let inner_type = node
12597                        .child_by_field_name("type")
12598                        .or_else(|| node.named_child(0))?;
12599                    let mut cursor = node.walk();
12600                    let declarator = node.child_by_field_name("declarator").or_else(|| {
12601                        node.named_children(&mut cursor).find(|child| {
12602                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
12603                        })
12604                    });
12605                    let levels = match declarator {
12606                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12607                        None => Vec::new(),
12608                    };
12609                    work.push(Work::ApplyLevels(levels));
12610                    work.push(Work::Visit {
12611                        node: inner_type,
12612                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
12613                    });
12614                }
12615                "sized_type_specifier" => {
12616                    // `unsigned char` is one primitive type whose components are
12617                    // partly unnamed tokens, so the whole specifier is its own
12618                    // name component. Reducing it to the `type` child would make
12619                    // `f(unsigned char)` and `f(char)` compare equal.
12620                    let name = StructuredTypeName::new(
12621                        vec![normalize_cpp_whitespace(node_text(node, source))],
12622                        lexical_scope.to_vec(),
12623                        false,
12624                    )?;
12625                    values.push(cpp_push_comparable_node(
12626                        &mut nodes,
12627                        CppComparableNode::Named {
12628                            name,
12629                            primitive: true,
12630                            konst: qualifiers.konst,
12631                            volatil: qualifiers.volatil,
12632                        },
12633                    ));
12634                }
12635                "qualified_identifier"
12636                | "scoped_identifier"
12637                | "scoped_type_identifier"
12638                | "type_identifier"
12639                | "field_identifier"
12640                | "identifier"
12641                | "namespace_identifier"
12642                | "primitive_type"
12643                | "template_type" => {
12644                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
12645                    values.push(cpp_push_comparable_node(
12646                        &mut nodes,
12647                        CppComparableNode::Named {
12648                            name,
12649                            primitive: node.kind() == "primitive_type",
12650                            konst: qualifiers.konst,
12651                            volatil: qualifiers.volatil,
12652                        },
12653                    ));
12654                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
12655                        let mut cursor = arguments_node.walk();
12656                        let arguments = arguments_node
12657                            .named_children(&mut cursor)
12658                            .filter(|child| !child.is_extra() && child.kind() != "comment")
12659                            .collect::<Vec<_>>();
12660                        work.push(Work::BuildGeneric {
12661                            argument_count: arguments.len(),
12662                        });
12663                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
12664                            node: argument,
12665                            qualifiers: CppCvQualifiers::default(),
12666                        }));
12667                    }
12668                }
12669                _ => {
12670                    let inner = node.child_by_field_name("type").or_else(|| {
12671                        (node.named_child_count() == 1)
12672                            .then(|| node.named_child(0))
12673                            .flatten()
12674                    })?;
12675                    work.push(Work::Visit {
12676                        node: inner,
12677                        qualifiers,
12678                    });
12679                }
12680            },
12681            Work::ApplyLevels(levels) => {
12682                let mut root = values.pop()?;
12683                for level in levels {
12684                    let node = match level {
12685                        CppComparableLevel::Pointer { konst, volatil } => {
12686                            CppComparableNode::Pointer {
12687                                inner: root,
12688                                konst,
12689                                volatil,
12690                            }
12691                        }
12692                        CppComparableLevel::Reference => {
12693                            CppComparableNode::Reference { inner: root }
12694                        }
12695                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
12696                    };
12697                    root = cpp_push_comparable_node(&mut nodes, node);
12698                }
12699                values.push(root);
12700            }
12701            Work::BuildGeneric { argument_count } => {
12702                let value_count = argument_count.checked_add(1)?;
12703                let start = values.len().checked_sub(value_count)?;
12704                let mut built = values.split_off(start);
12705                let base = built.remove(0);
12706                values.push(cpp_push_comparable_node(
12707                    &mut nodes,
12708                    CppComparableNode::Generic {
12709                        base,
12710                        arguments: built,
12711                    },
12712                ));
12713            }
12714        }
12715    }
12716    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
12717    debug_assert_eq!(
12718        root,
12719        nodes.len().saturating_sub(1),
12720        "comparable nodes are appended in post-order, so the root is the last one"
12721    );
12722    Some(CppComparableParameter { nodes, root })
12723}
12724
12725fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
12726    nodes.push(node);
12727    nodes.len() - 1
12728}
12729
12730/// The template argument list of the name `node` terminates in, if any.
12731///
12732/// `std::vector<int>` writes its arguments on the `name` of a qualified
12733/// identifier, so a walk that stopped at the qualified node would reduce
12734/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
12735fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
12736    let mut current = node;
12737    loop {
12738        match current.kind() {
12739            "template_type" => return current.child_by_field_name("arguments"),
12740            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12741                current = current.child_by_field_name("name")?;
12742            }
12743            _ => return None,
12744        }
12745    }
12746}
12747
12748/// The callable declarator of the declaration that covers `start_byte`.
12749///
12750/// A consumer that holds a declaration's recorded byte position rather than its
12751/// syntax node - external header extraction, for instance - uses this to reach
12752/// the same `function_declarator` the declaration walk read.
12753pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
12754    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
12755    loop {
12756        if matches!(
12757            current.kind(),
12758            "declaration" | "field_declaration" | "function_definition"
12759        ) && let Some(declarator) = current
12760            .child_by_field_name("declarator")
12761            .and_then(extract_function_declarator)
12762        {
12763            return Some(declarator);
12764        }
12765        current = current.parent()?;
12766    }
12767}
12768
12769fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
12770    let mut labels = Vec::new();
12771    let mut cursor = parameters_node.walk();
12772    for child in parameters_node.children(&mut cursor) {
12773        match child.kind() {
12774            "parameter_declaration" | "optional_parameter_declaration" => {
12775                if let Some(name_node) = child
12776                    .child_by_field_name("declarator")
12777                    .and_then(cpp_declarator_label_node)
12778                {
12779                    labels.push(name_node);
12780                } else {
12781                    labels.push(child);
12782                }
12783            }
12784            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12785                labels.push(child);
12786            }
12787            _ => {}
12788        }
12789    }
12790    labels
12791}
12792
12793fn cpp_signature_search_start<'tree>(
12794    signature: &str,
12795    function_declarator: Node<'tree>,
12796    source: &str,
12797    ancestry: &ParentIndex<'tree>,
12798) -> usize {
12799    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
12800        return 0;
12801    };
12802    let raw = node_text(enclosing, source);
12803    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
12804    let offset = function_declarator
12805        .start_byte()
12806        .saturating_sub(enclosing.start_byte())
12807        .saturating_sub(leading_trim_bytes);
12808    offset.min(signature.len())
12809}
12810
12811fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
12812    match node.kind() {
12813        "identifier" | "field_identifier" => Some(node),
12814        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
12815            .child_by_field_name("declarator")
12816            .or_else(|| last_named_child(node))
12817            .and_then(cpp_declarator_label_node),
12818        "array_declarator" => node
12819            .child_by_field_name("declarator")
12820            .and_then(cpp_declarator_label_node),
12821        "function_declarator" => node
12822            .child_by_field_name("declarator")
12823            .or_else(|| node.child_by_field_name("name"))
12824            .or_else(|| last_named_child(node))
12825            .and_then(cpp_declarator_label_node),
12826        _ => None,
12827    }
12828}
12829
12830fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
12831    let base_type = parameter
12832        .child_by_field_name("type")
12833        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
12834        .unwrap_or_default();
12835    let declarator = cpp_parameter_declarator(parameter);
12836    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
12837    // are discarded, so `f(const int)` and `f(int)` declare one function. A
12838    // qualifier written next to the parameter's type is only top-level when
12839    // the declarator adds no indirection; behind a pointer, reference or array
12840    // declarator the same qualifier belongs to the pointee, referent or
12841    // element and keeps distinguishing the type (#1827).
12842    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
12843    let mut cursor = parameter.walk();
12844    let qualifiers = parameter
12845        .named_children(&mut cursor)
12846        .filter(|child| child.kind() == "type_qualifier")
12847        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12848        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
12849        .collect::<Vec<_>>()
12850        .join(" ");
12851    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
12852        (true, _) => base_type,
12853        (_, true) => qualifiers,
12854        (false, false) => format!("{qualifiers} {base_type}"),
12855    };
12856    let declarator_suffix = declarator
12857        .map(|node| cpp_declarator_suffix_without_name(node, source))
12858        .unwrap_or_default();
12859
12860    let combined = if type_text.is_empty() {
12861        declarator_suffix
12862    } else if declarator_suffix.is_empty() {
12863        type_text
12864    } else {
12865        format!("{type_text} {declarator_suffix}")
12866    };
12867    normalize_cpp_type_text(&combined)
12868}
12869
12870fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
12871    parameter.child_by_field_name("declarator").or_else(|| {
12872        // Some unnamed prototype parameters expose their abstract declarator
12873        // as a direct named child without the grammar's `declarator` field.
12874        // Recover only the structured abstract-declarator node; the parameter's
12875        // type and qualifiers are distinct children and must not be guessed from
12876        // source text.
12877        let mut cursor = parameter.walk();
12878        parameter
12879            .named_children(&mut cursor)
12880            .find(|child| is_cpp_abstract_declarator(child.kind()))
12881    })
12882}
12883
12884/// Whether a parameter's declarator chain adds indirection - a pointer,
12885/// reference, array or function declarator - to the parameter's written type.
12886pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
12887    let mut current = Some(declarator);
12888    while let Some(node) = current {
12889        if matches!(
12890            node.kind(),
12891            "pointer_declarator"
12892                | "abstract_pointer_declarator"
12893                | "reference_declarator"
12894                | "abstract_reference_declarator"
12895                | "array_declarator"
12896                | "abstract_array_declarator"
12897                | "function_declarator"
12898                | "abstract_function_declarator"
12899        ) {
12900            return true;
12901        }
12902        current = cpp_nested_declarator(node);
12903    }
12904    false
12905}
12906
12907fn is_cpp_abstract_declarator(kind: &str) -> bool {
12908    matches!(
12909        kind,
12910        "abstract_pointer_declarator"
12911            | "abstract_reference_declarator"
12912            | "abstract_array_declarator"
12913            | "abstract_function_declarator"
12914            | "abstract_parenthesized_declarator"
12915    )
12916}
12917
12918fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
12919    node.child_by_field_name("declarator").or_else(|| {
12920        if is_cpp_abstract_declarator(node.kind()) {
12921            let mut cursor = node.walk();
12922            node.named_children(&mut cursor)
12923                .find(|child| is_cpp_abstract_declarator(child.kind()))
12924        } else {
12925            // Named declarators historically use their last named child when
12926            // tree-sitter omits the field. Keep that broad fallback for
12927            // attributed, variadic, and recovered named shapes.
12928            last_named_child(node)
12929        }
12930    })
12931}
12932
12933fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
12934    match node.kind() {
12935        "identifier" | "field_identifier" => String::new(),
12936        "pointer_declarator" | "abstract_pointer_declarator" => {
12937            let inner = cpp_nested_declarator(node)
12938                .map(|child| cpp_declarator_suffix_without_name(child, source))
12939                .unwrap_or_default();
12940            format!("*{inner}")
12941        }
12942        "reference_declarator" | "abstract_reference_declarator" => {
12943            let inner = cpp_nested_declarator(node)
12944                .map(|child| cpp_declarator_suffix_without_name(child, source))
12945                .unwrap_or_default();
12946            let reference = node
12947                .children(&mut node.walk())
12948                .find(|child| matches!(child.kind(), "&" | "&&"))
12949                .map(|child| node_text(child, source))
12950                .unwrap_or("&");
12951            format!("{reference}{inner}")
12952        }
12953        "array_declarator" | "abstract_array_declarator" => {
12954            let inner = cpp_nested_declarator(node)
12955                .map(|child| cpp_declarator_suffix_without_name(child, source))
12956                .unwrap_or_default();
12957            let size = node
12958                .child_by_field_name("size")
12959                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12960                .unwrap_or_default();
12961            format!("{inner}[{size}]")
12962        }
12963        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
12964            let inner = cpp_nested_declarator(node);
12965            inner
12966                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
12967                .unwrap_or_default()
12968        }
12969        "function_declarator" | "abstract_function_declarator" => {
12970            let inner = cpp_nested_declarator(node)
12971                .map(|child| cpp_declarator_suffix_without_name(child, source))
12972                .unwrap_or_default();
12973            let params = node
12974                .child_by_field_name("parameters")
12975                .map(|child| cpp_parameter_signature(child, source))
12976                .unwrap_or_else(|| "()".to_string());
12977            format!("{inner}{params}")
12978        }
12979        _ => {
12980            let text = normalize_cpp_whitespace(node_text(node, source));
12981            let name = extract_declarator_name(node, source);
12982            if name.is_empty() {
12983                text
12984            } else {
12985                text.replace(&name, "").trim().to_string()
12986            }
12987        }
12988    }
12989}
12990
12991fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
12992    collapse_cpp_whitespace(
12993        suffix
12994            .trim()
12995            .trim_start_matches("->")
12996            .trim_start_matches('{')
12997            .trim_end_matches(';'),
12998    )
12999}
13000
13001pub fn normalize_cpp_whitespace(value: &str) -> String {
13002    collapse_cpp_whitespace(value)
13003}
13004
13005fn normalize_cpp_type_text(value: &str) -> String {
13006    collapse_cpp_whitespace(value)
13007        .replace(", ", ",")
13008        .replace(" <", "<")
13009        .replace("< ", "<")
13010        .replace(" >", ">")
13011}
13012
13013fn collapse_cpp_whitespace(value: &str) -> String {
13014    let mut result = String::new();
13015    let mut prev_space = false;
13016    for ch in value.chars() {
13017        if ch.is_whitespace() {
13018            if !prev_space {
13019                result.push(' ');
13020            }
13021            prev_space = true;
13022        } else {
13023            result.push(ch);
13024            prev_space = false;
13025        }
13026    }
13027    result.trim().to_string()
13028}
13029
13030pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
13031    node_source_text(node, source)
13032}
13033
13034pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
13035    walk_named_tree_preorder(node, true, |node| {
13036        match node.kind() {
13037            "type_identifier" | "identifier" | "qualified_identifier" => {
13038                let text = node_text(node, source).trim();
13039                if !text.is_empty() {
13040                    identifiers.insert(text.to_string());
13041                }
13042            }
13043            _ => {}
13044        }
13045        WalkControl::Continue
13046    });
13047}
13048
13049fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
13050    node.child_by_field_name("body").or_else(|| {
13051        let mut cursor = node.walk();
13052        node.named_children(&mut cursor).find(|child| {
13053            matches!(
13054                child.kind(),
13055                "declaration_list" | "field_declaration_list" | "enumerator_list"
13056            )
13057        })
13058    })
13059}
13060
13061/// Return a class body's actual closing brace when the parser supplied one.
13062///
13063/// A malformed namespace sentinel can leave a class node carrying unrelated
13064/// parser errors even though its own class body is complete.  `has_error()` is
13065/// therefore too coarse an admission predicate for sentinel ownership.  The
13066/// body list, however, exposes the opening and closing punctuation directly;
13067/// a real (non-missing) final `}` proves that the class did not borrow the
13068/// enclosing namespace's close.  Requiring the body to end before its parent
13069/// container also rejects a recovered node whose body swallowed that outer
13070/// boundary.
13071fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
13072    if !matches!(
13073        node.kind(),
13074        "class_specifier" | "struct_specifier" | "union_specifier"
13075    ) {
13076        return None;
13077    }
13078    let body = cpp_body_node(node)?;
13079    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
13080        return None;
13081    }
13082    let open = body.child(0)?;
13083    let close = body.child(body.child_count().checked_sub(1)?)?;
13084    if open.kind() != "{"
13085        || open.is_missing()
13086        || close.kind() != "}"
13087        || close.is_missing()
13088        || close.end_byte() != body.end_byte()
13089        || body.end_byte() > node.end_byte()
13090        || node
13091            .parent()
13092            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
13093    {
13094        return None;
13095    }
13096    Some(close)
13097}
13098
13099fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
13100    if node.kind() == "namespace_definition" {
13101        return true;
13102    }
13103    let mut cursor = node.walk();
13104    node.named_children(&mut cursor)
13105        .any(cpp_contains_namespace_definition)
13106}
13107
13108struct CppNestedNamespaceSentinel<'tree> {
13109    function: Node<'tree>,
13110    body: Node<'tree>,
13111    namespace_components: Vec<String>,
13112}
13113
13114/// Owned structural recovery metadata for a namespace-sentinel region.
13115///
13116/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
13117/// instead of the namespace/class scopes that the declaration visitor restores.
13118/// The inverted usage walk has the original CST, so it needs the same ownership
13119/// evidence without borrowing parser nodes across its file scan.  Keep this
13120/// descriptor deliberately source-range based: callers can match a reference
13121/// node by containment and then resolve its structured type spelling in the
13122/// recovered class scope.
13123#[derive(Debug, Clone)]
13124pub struct CppSentinelRecoveredOwner {
13125    pub range: Range,
13126    /// Start of the qualified owner name (`btree<P>::method`).  A leading
13127    /// return type before this byte is looked up from the namespace; parameters,
13128    /// trailing returns, and the body use the member owner scope.
13129    pub owner_name_start_byte: usize,
13130    /// Number of leading components belonging to the namespace rather than
13131    /// the qualified class owner.  A leading return type is looked up before
13132    /// every owner component, not merely before the innermost class.
13133    pub namespace_component_count: usize,
13134    pub scope_components: Vec<String>,
13135}
13136
13137#[derive(Debug, Clone)]
13138pub struct CppSentinelRecoveredClass {
13139    pub namespace_range: Range,
13140    pub namespace_scope_components: Vec<String>,
13141    pub class_range: Range,
13142    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
13143    pub scope_components: Vec<String>,
13144    /// Qualified out-of-line member definitions owned by this class.  Their
13145    /// ranges may extend beyond `class_range` when the malformed sentinel
13146    /// swallowed the namespace close and left definitions as function siblings.
13147    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
13148}
13149
13150/// Resolve the lexical scope restored for a node in a malformed
13151/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
13152/// outrank class spans, which in turn outrank the surviving namespace body.
13153/// The class ancestor suffix is recovered from the original CST so nested
13154/// members keep their complete `Outer::Inner` owner chain.
13155pub fn cpp_sentinel_recovered_scope_for_node(
13156    node: Node<'_>,
13157    source: &str,
13158    recovered_classes: &[CppSentinelRecoveredClass],
13159) -> Option<Vec<String>> {
13160    let contains =
13161        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
13162    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
13163    for recovered in recovered_classes {
13164        for owner in recovered
13165            .owner_ranges
13166            .iter()
13167            .filter(|owner| contains(owner.range))
13168        {
13169            let replace = best_owner.is_none_or(|existing| {
13170                owner.range.end_byte.saturating_sub(owner.range.start_byte)
13171                    < existing
13172                        .range
13173                        .end_byte
13174                        .saturating_sub(existing.range.start_byte)
13175            });
13176            if replace {
13177                best_owner = Some(owner);
13178            }
13179        }
13180    }
13181    if let Some(owner) = best_owner {
13182        let mut scope = owner.scope_components.clone();
13183        if node.start_byte() < owner.owner_name_start_byte {
13184            scope.truncate(owner.namespace_component_count);
13185        }
13186        return Some(scope);
13187    }
13188
13189    let class = recovered_classes
13190        .iter()
13191        .filter(|recovered| contains(recovered.class_range))
13192        .min_by_key(|recovered| {
13193            recovered
13194                .class_range
13195                .end_byte
13196                .saturating_sub(recovered.class_range.start_byte)
13197        });
13198    let class_scope = class.is_some();
13199    let mut scope = if let Some(class) = class {
13200        class.scope_components.clone()
13201    } else {
13202        let namespace = recovered_classes
13203            .iter()
13204            .filter(|recovered| contains(recovered.namespace_range))
13205            .min_by_key(|recovered| {
13206                recovered
13207                    .namespace_range
13208                    .end_byte
13209                    .saturating_sub(recovered.namespace_range.start_byte)
13210            })?;
13211        let mut scope = namespace.namespace_scope_components.clone();
13212        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
13213        let common_prefix = scope
13214            .iter()
13215            .zip(&parser_namespace)
13216            .take_while(|(recovered, parser)| recovered == parser)
13217            .count();
13218        scope.extend(parser_namespace.into_iter().skip(common_prefix));
13219        scope
13220    };
13221    if class_scope {
13222        let mut ancestor_components = Vec::new();
13223        let mut ancestor = node.parent();
13224        while let Some(current) = ancestor {
13225            if matches!(
13226                current.kind(),
13227                "class_specifier" | "struct_specifier" | "union_specifier"
13228            ) && let Some(name) = current.child_by_field_name("name")
13229                && let Some(name_components) = cpp_name_components(name, source)
13230            {
13231                ancestor_components.push(
13232                    name_components
13233                        .into_iter()
13234                        .map(|component| component.name)
13235                        .collect::<Vec<_>>(),
13236                );
13237            }
13238            ancestor = current.parent();
13239        }
13240        ancestor_components.reverse();
13241        let base_len = scope.len();
13242        for component in ancestor_components.into_iter().flatten() {
13243            if scope.len() >= base_len && scope.last() == Some(&component) {
13244                continue;
13245            }
13246            scope.push(component);
13247        }
13248    }
13249    Some(scope)
13250}
13251
13252struct CppSentinelFragmentedClassTail<'tree> {
13253    class_node: Node<'tree>,
13254    template_node: Option<Node<'tree>>,
13255    name: String,
13256    raw_supertypes: Option<Vec<String>>,
13257    fragmented: FragmentedExportBody,
13258    consumed_start: usize,
13259}
13260
13261struct CppSentinelFragmentedClassErrorPrefix<'tree> {
13262    name: String,
13263    open: Node<'tree>,
13264    raw_supertypes: Option<Vec<String>>,
13265}
13266
13267struct CppSentinelDirectBodyClassRegion {
13268    namespace_components: Vec<String>,
13269    class_start: usize,
13270    class_start_line: usize,
13271    class_close_end: usize,
13272    class_close_line: usize,
13273    name: String,
13274}
13275
13276fn cpp_sentinel_body_class_candidate<'tree>(
13277    child: Node<'tree>,
13278) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13279    if matches!(
13280        child.kind(),
13281        "class_specifier" | "struct_specifier" | "union_specifier"
13282    ) {
13283        return Some((child, None));
13284    }
13285    if child.kind() != "template_declaration" {
13286        if child.kind() == "declaration" {
13287            return Some((first_class_like_child(child)?, None));
13288        }
13289        return None;
13290    }
13291    let mut cursor = child.walk();
13292    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
13293        if matches!(
13294            candidate.kind(),
13295            "class_specifier" | "struct_specifier" | "union_specifier"
13296        ) {
13297            Some(candidate)
13298        } else if candidate.kind() == "declaration" {
13299            first_class_like_child(candidate)
13300        } else {
13301            None
13302        }
13303    })?;
13304    Some((class_node, Some(child)))
13305}
13306
13307/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
13308/// namespace-sentinel body when a later member macro ends the bogus sentinel
13309/// function before the real class close. The anonymous class/open tokens and
13310/// direct identifier are the structural proof; a retained direct close would
13311/// be an ordinary malformed class rather than the fragmented tail handled here.
13312fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
13313    node: Node<'tree>,
13314    source: &str,
13315) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
13316    let name = malformed_class_error_owner_name(node, source)?;
13317    let mut cursor = node.walk();
13318    let children = node.children(&mut cursor).collect::<Vec<_>>();
13319    let keyword = children.first()?;
13320    let open_index = children.iter().position(|child| child.kind() == "{")?;
13321    if children[open_index + 1..]
13322        .iter()
13323        .any(|child| child.kind() == "}")
13324    {
13325        return None;
13326    }
13327    let raw_supertypes =
13328        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
13329    Some(CppSentinelFragmentedClassErrorPrefix {
13330        name,
13331        open: children[open_index],
13332        raw_supertypes,
13333    })
13334}
13335
13336fn cpp_sentinel_direct_body_class_candidate<'tree>(
13337    child: Node<'tree>,
13338) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13339    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
13340        return Some(candidate);
13341    }
13342    if child.kind() != "template_declaration" {
13343        return None;
13344    }
13345    let mut cursor = child.walk();
13346    let wrapper = child
13347        .named_children(&mut cursor)
13348        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
13349    Some((first_class_like_child(wrapper)?, Some(child)))
13350}
13351
13352fn cpp_sentinel_direct_namespace_components(
13353    function: Node<'_>,
13354    body: Node<'_>,
13355    source: &str,
13356) -> Option<Vec<String>> {
13357    let mut cursor = function.walk();
13358    let children = function
13359        .named_children(&mut cursor)
13360        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
13361        .collect::<Vec<_>>();
13362    let sentinel_index = children.iter().rposition(|child| {
13363        direct_identifier_name(*child, source)
13364            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
13365    })?;
13366    let mut identifiers = Vec::new();
13367    let mut stack = children[sentinel_index + 1..]
13368        .iter()
13369        .rev()
13370        .copied()
13371        .collect::<Vec<_>>();
13372    while let Some(current) = stack.pop() {
13373        if let Some(name) = direct_identifier_name(current, source) {
13374            identifiers.push(name);
13375            continue;
13376        }
13377        let mut cursor = current.walk();
13378        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
13379        stack.extend(children.into_iter().rev());
13380    }
13381    let [keyword, namespace] = identifiers.as_slice() else {
13382        return None;
13383    };
13384    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
13385        .then(|| vec![namespace.clone()])
13386}
13387
13388fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
13389    let mut sibling = class_semicolon.next_named_sibling();
13390    let namespace_close = loop {
13391        let Some(current) = sibling else {
13392            return false;
13393        };
13394        sibling = current.next_named_sibling();
13395        if current.kind() != "comment" {
13396            break current;
13397        }
13398    };
13399    if !cpp_is_stray_close_brace(namespace_close, source) {
13400        return false;
13401    }
13402    loop {
13403        let Some(current) = sibling else {
13404            return false;
13405        };
13406        sibling = current.next_named_sibling();
13407        if current.kind() == "comment" {
13408            continue;
13409        }
13410        return direct_identifier_name(current, source)
13411            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
13412    }
13413}
13414
13415fn cpp_sentinel_macro_body_class_region<'tree>(
13416    node: Node<'tree>,
13417    source: &str,
13418    ancestry: &ParentIndex<'tree>,
13419) -> Option<CppSentinelDirectBodyClassRegion> {
13420    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
13421        return None;
13422    };
13423    if node.kind() != "function_definition" || !node.has_error() {
13424        return None;
13425    }
13426    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
13427    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
13428    let mut cursor = body.walk();
13429    let candidates = body
13430        .named_children(&mut cursor)
13431        .filter_map(cpp_sentinel_direct_body_class_candidate)
13432        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
13433        .collect::<Vec<_>>();
13434    let [(class_node, template_node)] = candidates.as_slice() else {
13435        return None;
13436    };
13437    let original_body = cpp_body_node(*class_node)?;
13438    let name = class_like_name(*class_node, source, ancestry)?;
13439    if name.is_empty() || cpp_export_macro_token(&name) {
13440        return None;
13441    }
13442
13443    let mut sibling = node.next_named_sibling();
13444    let (class_close_start, class_close_end, class_close_line) = loop {
13445        let current = sibling?;
13446        let next = current.next_named_sibling();
13447        if cpp_is_stray_close_brace(current, source)
13448            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
13449        {
13450            let semicolon = next.expect("checked above");
13451            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
13452                return None;
13453            }
13454            break (
13455                current.start_byte(),
13456                semicolon.end_byte(),
13457                semicolon.end_position().row + 1,
13458            );
13459        }
13460        sibling = next;
13461    };
13462    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
13463    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
13464    let root = tree.root_node();
13465    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
13466    // The region reparse is its own tree, so it needs its own parent index;
13467    // the caller's index answers nothing about these nodes.
13468    let reparsed_ancestry = ParentIndex::new(root);
13469    let reparsed =
13470        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
13471    if reparsed.name != name
13472        || reparsed.declaration_node.start_byte() != class_node.start_byte()
13473        || reparsed.body.start_byte() != original_body.start_byte()
13474        || class_close_start <= reparsed.body.end_byte()
13475        || class_close_end <= class_node.end_byte()
13476    {
13477        return None;
13478    }
13479    Some(CppSentinelDirectBodyClassRegion {
13480        namespace_components,
13481        class_start: reparse_start,
13482        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
13483            node.start_position().row + 1
13484        }),
13485        class_close_end,
13486        class_close_line,
13487        name,
13488    })
13489}
13490
13491/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
13492/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
13493///
13494/// The parser puts the namespace opener and the malformed function in one root
13495/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
13496/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
13497/// the malformed function must begin with an all-caps type, then an ERROR whose
13498/// sole identifier is `namespace`, followed by the inner namespace identifier
13499/// and a compound body; and that body must contain a complete named class or a
13500/// structurally fragmented class prefix. A text reparse cannot prove any of
13501/// those ownership boundaries.
13502fn cpp_nested_namespace_sentinel<'tree>(
13503    node: Node<'tree>,
13504    source: &str,
13505    ancestry: &ParentIndex<'tree>,
13506) -> Option<CppNestedNamespaceSentinel<'tree>> {
13507    if !node.has_error() {
13508        return None;
13509    }
13510
13511    let (function, mut namespace_components) = if node.kind() == "ERROR" {
13512        let mut cursor = node.walk();
13513        let functions = node
13514            .named_children(&mut cursor)
13515            .filter(|child| child.kind() == "function_definition")
13516            .collect::<Vec<_>>();
13517        let [function] = functions.as_slice() else {
13518            return None;
13519        };
13520        if !function.has_error() {
13521            return None;
13522        }
13523        let mut cursor = node.walk();
13524        let children = node.children(&mut cursor).collect::<Vec<_>>();
13525        let function_index = children
13526            .iter()
13527            .position(|child| same_node(*child, *function))?;
13528        let [outer_keyword, outer_name, outer_open] =
13529            children.get(function_index.checked_sub(3)?..function_index)?
13530        else {
13531            return None;
13532        };
13533        if outer_keyword.kind() != "namespace"
13534            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
13535            || outer_open.kind() != "{"
13536        {
13537            return None;
13538        }
13539        (
13540            *function,
13541            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
13542        )
13543    } else if node.kind() == "function_definition" {
13544        let declaration_list = node.parent()?;
13545        let namespace = declaration_list.parent()?;
13546        if declaration_list.kind() != "declaration_list"
13547            || namespace.kind() != "namespace_definition"
13548            || namespace.child_by_field_name("body") != Some(declaration_list)
13549        {
13550            return None;
13551        }
13552        (node, Vec::new())
13553    } else {
13554        return None;
13555    };
13556
13557    let mut cursor = function.walk();
13558    let named = function
13559        .named_children(&mut cursor)
13560        .filter(|child| child.kind() != "comment")
13561        .collect::<Vec<_>>();
13562    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
13563        return None;
13564    };
13565    if first_type.kind() != "type_identifier" {
13566        return None;
13567    }
13568    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
13569    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
13570        return None;
13571    }
13572    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
13573        return None;
13574    }
13575    let inner_keyword = inner_error.named_child(0)?;
13576    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
13577        return None;
13578    }
13579    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
13580        return None;
13581    }
13582    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
13583    if inner_name.is_empty() || body.kind() != "compound_statement" {
13584        return None;
13585    }
13586    namespace_components.push(inner_name);
13587
13588    let mut cursor = body.walk();
13589    let has_complete_class = body.named_children(&mut cursor).any(|child| {
13590        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13591            cpp_body_node(class_node).is_some()
13592                && class_like_name(class_node, source, ancestry)
13593                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13594        })
13595    });
13596    if !has_complete_class
13597        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
13598    {
13599        return None;
13600    }
13601
13602    Some(CppNestedNamespaceSentinel {
13603        function,
13604        body: *body,
13605        namespace_components,
13606    })
13607}
13608
13609/// Recognize a namespace-begin sentinel directly beneath the translation unit.
13610///
13611/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
13612/// function whose type is the sentinel, whose declarator is the structured
13613/// qualified name `namespace::a::b`, and whose body contains the namespace
13614/// items. Declaration indexing already reparses this bounded region. The
13615/// inverse scanner retains the original tree, so recover the same namespace
13616/// components from the declarator fields for its lexical-scope metadata.
13617fn cpp_root_namespace_sentinel<'tree>(
13618    node: Node<'tree>,
13619    source: &str,
13620    ancestry: &ParentIndex<'tree>,
13621) -> Option<CppNestedNamespaceSentinel<'tree>> {
13622    if node.kind() != "function_definition"
13623        || !node.has_error()
13624        || node.parent()?.kind() != "translation_unit"
13625    {
13626        return None;
13627    }
13628    let first_type = node.child_by_field_name("type")?;
13629    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
13630    if first_type.kind() != "type_identifier"
13631        || sentinel.is_empty()
13632        || !cpp_export_macro_token(&sentinel)
13633    {
13634        return None;
13635    }
13636    let declarator = node.child_by_field_name("declarator")?;
13637    let body = node.child_by_field_name("body")?;
13638    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
13639        return None;
13640    }
13641    let mut cursor = node.walk();
13642    let named = node
13643        .named_children(&mut cursor)
13644        .filter(|child| child.kind() != "comment")
13645        .collect::<Vec<_>>();
13646    let [named_type, named_declarator, named_body] = named.as_slice() else {
13647        return None;
13648    };
13649    if !same_node(*named_type, first_type)
13650        || !same_node(*named_declarator, declarator)
13651        || !same_node(*named_body, body)
13652    {
13653        return None;
13654    }
13655    let mut declarator_components = Vec::new();
13656    let mut valid_components = true;
13657    walk_named_tree_preorder(declarator, true, |component| {
13658        if !matches!(
13659            component.kind(),
13660            "identifier" | "namespace_identifier" | "type_identifier"
13661        ) {
13662            return WalkControl::Continue;
13663        }
13664        let Some(component) = canonical_cpp_qualified_component(component, source) else {
13665            valid_components = false;
13666            return WalkControl::Break;
13667        };
13668        declarator_components.push(component.name);
13669        WalkControl::SkipChildren
13670    });
13671    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
13672        return None;
13673    }
13674    declarator_components.remove(0);
13675    let namespace_components = declarator_components;
13676    if namespace_components.is_empty()
13677        || namespace_components
13678            .iter()
13679            .any(|component| component.is_empty() || cpp_export_macro_token(component))
13680    {
13681        return None;
13682    }
13683
13684    let mut cursor = body.walk();
13685    let has_complete_class = body.named_children(&mut cursor).any(|child| {
13686        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13687            cpp_body_node(class_node).is_some()
13688                && class_like_name(class_node, source, ancestry)
13689                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13690        })
13691    });
13692    if !has_complete_class
13693        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
13694    {
13695        return None;
13696    }
13697
13698    Some(CppNestedNamespaceSentinel {
13699        function: node,
13700        body,
13701        namespace_components,
13702    })
13703}
13704
13705/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
13706/// malformed namespace-sentinel function.  The recovery is deliberately
13707/// structural: the class must be a direct body item, its own class node must be
13708/// erroneous and end before a unique anonymous `}` in the enclosing
13709/// declaration-list, and that namespace's next sibling must be a standalone
13710/// `;`.  The complete interior must pass the existing member-shaped reparse
13711/// gate. This avoids source brace scans and does not borrow a close from an
13712/// unrelated later declaration.
13713fn cpp_sentinel_fragmented_class_tail<'tree>(
13714    function: Node<'tree>,
13715    body: Node<'tree>,
13716    source: &str,
13717    ancestry: &ParentIndex<'tree>,
13718) -> Option<CppSentinelFragmentedClassTail<'tree>> {
13719    let mut cursor = body.walk();
13720    let candidates = body
13721        .named_children(&mut cursor)
13722        .filter_map(|child| {
13723            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
13724                let class_body = cpp_body_node(class_node)?;
13725                if !class_node.has_error() {
13726                    return None;
13727                }
13728                let name = class_like_name(class_node, source, ancestry)?;
13729                let raw_supertypes =
13730                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
13731                        .then(|| extract_cpp_supertypes(class_node, source));
13732                return Some((
13733                    class_node,
13734                    template_node,
13735                    name,
13736                    class_body,
13737                    class_body.start_byte().checked_add(1)?,
13738                    raw_supertypes,
13739                ));
13740            }
13741            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
13742            Some((
13743                child,
13744                None,
13745                prefix.name,
13746                prefix.open,
13747                prefix.open.end_byte(),
13748                prefix.raw_supertypes,
13749            ))
13750        })
13751        .collect::<Vec<_>>();
13752    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
13753        candidates.as_slice()
13754    else {
13755        return None;
13756    };
13757    if name.is_empty() || cpp_export_macro_token(name) {
13758        return None;
13759    }
13760
13761    let (close, semicolon) =
13762        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
13763
13764    let reparse_end = close.start_byte();
13765    if *reparse_start >= reparse_end {
13766        return None;
13767    }
13768    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
13769    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
13770        return None;
13771    }
13772    let class_range = Range {
13773        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13774        end_byte: semicolon.end_byte(),
13775        start_line: template_node.map_or(class_node.start_position().row, |node| {
13776            node.start_position().row
13777        }) + 1,
13778        end_line: semicolon.end_position().row + 1,
13779    };
13780    Some(CppSentinelFragmentedClassTail {
13781        class_node: *class_node,
13782        template_node: *template_node,
13783        name: name.clone(),
13784        raw_supertypes: raw_supertypes.clone(),
13785        fragmented: FragmentedExportBody {
13786            reparse_start: *reparse_start,
13787            reparse_end,
13788            class_range,
13789        },
13790        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13791    })
13792}
13793
13794/// Recover the class and out-of-line owner scopes from every malformed
13795/// namespace-sentinel region in `root`.
13796///
13797/// This is the shared structural counterpart to
13798/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
13799/// reuses the visitor's sentinel/class admission predicates instead of parsing
13800/// source text a second time.  The returned values own only ranges and names, so
13801/// they can be retained by an inverted usage scan after the tree borrow ends.
13802pub fn cpp_sentinel_recovered_classes(
13803    root: Node<'_>,
13804    source: &str,
13805) -> Vec<CppSentinelRecoveredClass> {
13806    if !root.has_error() {
13807        return Vec::new();
13808    }
13809    // This scan owns its walk of `root`, so it owns the parent index that walk
13810    // asks its ancestor questions through. Built after the error gate: a clean
13811    // tree returns without paying for one.
13812    let ancestry = ParentIndex::new(root);
13813    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
13814    let mut stack = vec![root];
13815    while let Some(current) = stack.pop() {
13816        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
13817            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
13818        {
13819            let namespace_components = cpp_sentinel_recovered_namespace_components(
13820                recovered.function,
13821                &recovered.namespace_components,
13822                source,
13823            );
13824            let fragmented = cpp_sentinel_fragmented_class_tail(
13825                recovered.function,
13826                recovered.body,
13827                source,
13828                &ancestry,
13829            );
13830            let mut class_candidates = Vec::new();
13831            let mut cursor = recovered.body.walk();
13832            for (class_node, template_node) in recovered
13833                .body
13834                .named_children(&mut cursor)
13835                .filter_map(cpp_sentinel_body_class_candidate)
13836            {
13837                let Some(name) = class_like_name(class_node, source, &ancestry) else {
13838                    continue;
13839                };
13840                if name.is_empty() || cpp_export_macro_token(&name) {
13841                    continue;
13842                }
13843                let is_fragmented = fragmented
13844                    .as_ref()
13845                    .is_some_and(|tail| same_node(tail.class_node, class_node));
13846                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
13847                    continue;
13848                }
13849                let class_range = if is_fragmented {
13850                    fragmented
13851                        .as_ref()
13852                        .map(|tail| tail.fragmented.class_range)
13853                        .expect("fragmented class range is present when class matches")
13854                } else {
13855                    cpp_declaration_range(template_node.unwrap_or(class_node))
13856                };
13857                class_candidates.push((class_range, name));
13858            }
13859            if let Some(fragmented) = fragmented
13860                .as_ref()
13861                .filter(|tail| tail.class_node.kind() == "ERROR")
13862            {
13863                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
13864            }
13865
13866            let mut owner_ranges =
13867                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
13868            cpp_sentinel_extend_unique_owner_ranges(
13869                &mut owner_ranges,
13870                cpp_sentinel_recovered_sibling_owner_ranges(
13871                    recovered.function,
13872                    &namespace_components,
13873                    source,
13874                ),
13875            );
13876            for (class_range, name) in class_candidates {
13877                push_cpp_sentinel_recovered_class(
13878                    &mut recovered_classes,
13879                    cpp_declaration_range(recovered.body),
13880                    &namespace_components,
13881                    class_range,
13882                    name,
13883                    &owner_ranges,
13884                );
13885            }
13886
13887            if let Some(declaration_list) = recovered
13888                .function
13889                .parent()
13890                .filter(|parent| parent.kind() == "declaration_list")
13891            {
13892                let outer_namespace =
13893                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
13894                push_cpp_sentinel_sibling_classes(
13895                    &mut recovered_classes,
13896                    declaration_list,
13897                    recovered.function,
13898                    &outer_namespace,
13899                    source,
13900                    &ancestry,
13901                );
13902            }
13903        } else if let Some(region) =
13904            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
13905        {
13906            let namespace_components = cpp_sentinel_recovered_namespace_components(
13907                current,
13908                &region.namespace_components,
13909                source,
13910            );
13911            let owner_container = current
13912                .parent()
13913                .filter(|parent| parent.kind() == "declaration_list")
13914                .unwrap_or(current);
13915            let owner_ranges =
13916                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
13917            push_cpp_sentinel_recovered_class(
13918                &mut recovered_classes,
13919                cpp_declaration_range(owner_container),
13920                &namespace_components,
13921                Range {
13922                    start_byte: region.class_start,
13923                    end_byte: region.class_close_end,
13924                    start_line: region.class_start_line,
13925                    end_line: region.class_close_line,
13926                },
13927                region.name,
13928                &owner_ranges,
13929            );
13930        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
13931            // A generic sentinel-prefixed class can be reduced as a malformed
13932            // function/ERROR without the explicit `namespace X` token pair.
13933            // Reuse the declaration visitor's bounded reparse and retain only
13934            // the recovered class identity/range here.
13935            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
13936                region;
13937            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
13938                continue;
13939            };
13940            let root = tree.root_node();
13941            let template_node = cpp_sentinel_reparsed_leading_template(root);
13942            // A region reparse is its own tree and needs its own parent index.
13943            let reparsed_ancestry = ParentIndex::new(root);
13944            let Some(reparsed_class) =
13945                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
13946            else {
13947                continue;
13948            };
13949            let class_node = reparsed_class.declaration_node;
13950            let name = reparsed_class.name;
13951            let namespace_components =
13952                cpp_sentinel_recovered_namespace_components(current, &[], source);
13953            let owner_container = current
13954                .parent()
13955                .filter(|parent| parent.kind() == "declaration_list")
13956                .unwrap_or(current);
13957            let mut owner_ranges =
13958                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
13959            cpp_sentinel_extend_unique_owner_ranges(
13960                &mut owner_ranges,
13961                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
13962            );
13963            push_cpp_sentinel_recovered_class(
13964                &mut recovered_classes,
13965                cpp_declaration_range(owner_container),
13966                &namespace_components,
13967                Range {
13968                    start_byte: class_start,
13969                    end_byte: close_end,
13970                    start_line: class_node.start_position().row + 1,
13971                    end_line: class_node.end_position().row + 1,
13972                },
13973                name,
13974                &owner_ranges,
13975            );
13976            if owner_container.kind() == "declaration_list" {
13977                push_cpp_sentinel_sibling_classes(
13978                    &mut recovered_classes,
13979                    owner_container,
13980                    current,
13981                    &namespace_components,
13982                    source,
13983                    &ancestry,
13984                );
13985            }
13986        }
13987
13988        let mut cursor = current.walk();
13989        stack.extend(current.named_children(&mut cursor));
13990    }
13991    // A shallower sentinel can expose nested classes as apparent namespace
13992    // siblings even after a deeper sentinel proves that a containing class
13993    // owns their ranges. Drop those shadow descriptors; scope recovery starts
13994    // from the proven containing class and appends parser-visible class
13995    // ancestors, preserving the full `Outer::Inner` chain.
13996    let shadowed = recovered_classes
13997        .iter()
13998        .map(|candidate| {
13999            recovered_classes.iter().any(|container| {
14000                container.class_range.start_byte <= candidate.class_range.start_byte
14001                    && container.class_range.end_byte >= candidate.class_range.end_byte
14002                    && container.class_range != candidate.class_range
14003                    && container.namespace_scope_components.len()
14004                        > candidate.namespace_scope_components.len()
14005                    && container
14006                        .namespace_scope_components
14007                        .starts_with(&candidate.namespace_scope_components)
14008            })
14009        })
14010        .collect::<Vec<_>>();
14011    let mut index = 0usize;
14012    recovered_classes.retain(|_| {
14013        let keep = !shadowed[index];
14014        index += 1;
14015        keep
14016    });
14017    recovered_classes
14018}
14019
14020/// A flat sentinel can swallow the first class while leaving later classes and
14021/// their out-of-line definitions as ordinary declaration-list siblings.  Once
14022/// the malformed class proves the sentinel envelope, retain those structurally
14023/// complete sibling classes under the same surviving namespace so every member
14024/// owner in the region uses one recovery contract.
14025fn push_cpp_sentinel_sibling_classes<'tree>(
14026    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14027    declaration_list: Node<'tree>,
14028    sentinel_node: Node<'tree>,
14029    namespace_components: &[String],
14030    source: &str,
14031    ancestry: &ParentIndex<'tree>,
14032) {
14033    let owner_ranges =
14034        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
14035    let namespace_range = cpp_declaration_range(declaration_list);
14036    let mut cursor = declaration_list.walk();
14037    for (class_node, template_node) in declaration_list
14038        .named_children(&mut cursor)
14039        .filter(|child| !same_node(*child, sentinel_node))
14040        .filter_map(cpp_sentinel_body_class_candidate)
14041    {
14042        let Some(name) = class_like_name(class_node, source, ancestry) else {
14043            continue;
14044        };
14045        if name.is_empty()
14046            || cpp_export_macro_token(&name)
14047            || cpp_complete_class_body_close(class_node).is_none()
14048        {
14049            continue;
14050        }
14051        push_cpp_sentinel_recovered_class(
14052            recovered_classes,
14053            namespace_range,
14054            namespace_components,
14055            cpp_declaration_range(template_node.unwrap_or(class_node)),
14056            name,
14057            &owner_ranges,
14058        );
14059    }
14060}
14061
14062fn push_cpp_sentinel_recovered_class(
14063    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14064    namespace_range: Range,
14065    namespace_components: &[String],
14066    class_range: Range,
14067    name: String,
14068    owner_ranges: &[CppSentinelRecoveredOwner],
14069) {
14070    let mut scope_components = namespace_components.to_vec();
14071    scope_components.push(name);
14072    let owner_ranges = owner_ranges
14073        .iter()
14074        .filter(|owner| owner.scope_components.starts_with(&scope_components))
14075        .cloned()
14076        .collect::<Vec<_>>();
14077    if recovered_classes.iter().any(|existing| {
14078        existing.class_range == class_range && existing.scope_components == scope_components
14079    }) {
14080        return;
14081    }
14082    recovered_classes.push(CppSentinelRecoveredClass {
14083        namespace_range,
14084        namespace_scope_components: namespace_components.to_vec(),
14085        class_range,
14086        scope_components,
14087        owner_ranges,
14088    });
14089}
14090
14091fn cpp_sentinel_recovered_namespace_components(
14092    function: Node<'_>,
14093    recovered_components: &[String],
14094    source: &str,
14095) -> Vec<String> {
14096    let mut ancestor_components = Vec::new();
14097    let mut ancestor = function.parent();
14098    while let Some(current) = ancestor {
14099        if current.kind() == "namespace_definition"
14100            && let Some(name_node) = current.child_by_field_name("name")
14101            && let Some(components) = cpp_name_components(name_node, source)
14102        {
14103            ancestor_components.push(
14104                components
14105                    .into_iter()
14106                    .map(|component| component.name)
14107                    .collect::<Vec<_>>(),
14108            );
14109        }
14110        ancestor = current.parent();
14111    }
14112    ancestor_components.reverse();
14113    let mut ancestors = ancestor_components
14114        .into_iter()
14115        .flatten()
14116        .collect::<Vec<_>>();
14117
14118    let overlap = (0..=ancestors.len().min(recovered_components.len()))
14119        .rev()
14120        .find(|length| {
14121            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
14122        })
14123        .unwrap_or(0);
14124    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
14125    ancestors
14126}
14127
14128fn cpp_sentinel_recovered_owner_ranges(
14129    body: Node<'_>,
14130    namespace_components: &[String],
14131    source: &str,
14132) -> Vec<CppSentinelRecoveredOwner> {
14133    let mut owners = Vec::new();
14134    walk_named_tree_preorder(body, true, |node| {
14135        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14136    });
14137    owners
14138}
14139
14140fn cpp_sentinel_collect_owner_range(
14141    node: Node<'_>,
14142    namespace_components: &[String],
14143    source: &str,
14144    owners: &mut Vec<CppSentinelRecoveredOwner>,
14145) -> WalkControl {
14146    if node.kind() != "function_definition" {
14147        return WalkControl::Continue;
14148    }
14149    let Some(function_declarator) = extract_function_declarator(node) else {
14150        return WalkControl::Continue;
14151    };
14152    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
14153        return WalkControl::Continue;
14154    };
14155    let Some(mut components) = cpp_name_components(name_node, source) else {
14156        return WalkControl::Continue;
14157    };
14158    if components.len() <= 1 {
14159        return WalkControl::Continue;
14160    }
14161    components.pop();
14162    let mut owner_components = components
14163        .into_iter()
14164        .map(|component| component.name)
14165        .collect::<Vec<_>>();
14166    let overlap = (0..=namespace_components.len().min(owner_components.len()))
14167        .rev()
14168        .find(|length| {
14169            owner_components[..*length]
14170                == namespace_components[namespace_components.len().saturating_sub(*length)..]
14171        })
14172        .unwrap_or(0);
14173    let mut scope_components = namespace_components.to_vec();
14174    scope_components.extend(owner_components.drain(overlap..));
14175    if scope_components.len() <= namespace_components.len() {
14176        return WalkControl::Continue;
14177    }
14178    let range = cpp_declaration_range(node);
14179    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
14180        existing.range == range && existing.scope_components == scope_components
14181    }) {
14182        owners.push(CppSentinelRecoveredOwner {
14183            range,
14184            owner_name_start_byte: name_node.start_byte(),
14185            namespace_component_count: namespace_components.len(),
14186            scope_components,
14187        });
14188    }
14189    WalkControl::Continue
14190}
14191
14192fn cpp_sentinel_extend_unique_owner_ranges(
14193    owners: &mut Vec<CppSentinelRecoveredOwner>,
14194    additional: Vec<CppSentinelRecoveredOwner>,
14195) {
14196    for owner in additional {
14197        if !owners.iter().any(|existing| {
14198            existing.range == owner.range && existing.scope_components == owner.scope_components
14199        }) {
14200            owners.push(owner);
14201        }
14202    }
14203}
14204
14205fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
14206    if node.kind() != "ERROR" || node.named_child_count() != 1 {
14207        return false;
14208    }
14209    let Some(end_name) = node.named_child(0) else {
14210        return false;
14211    };
14212    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
14213        return false;
14214    }
14215    let mut cursor = node.walk();
14216    node.children(&mut cursor)
14217        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
14218}
14219
14220/// Collect owner definitions that the malformed sentinel left as later
14221/// declaration-list siblings. Parser-visible namespace siblings are a hard
14222/// boundary: their declarations must keep their own lexical namespace.
14223fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14224    parent: Node<'_>,
14225    sentinel_node: Node<'_>,
14226    namespace_components: &[String],
14227    source: &str,
14228) -> Vec<CppSentinelRecoveredOwner> {
14229    let mut owners = Vec::new();
14230    let mut after_sentinel = false;
14231    let mut cursor = parent.walk();
14232    for child in parent.named_children(&mut cursor) {
14233        if !after_sentinel {
14234            if same_node(child, sentinel_node) {
14235                after_sentinel = true;
14236            }
14237            continue;
14238        }
14239        walk_named_tree_preorder(child, true, |node| {
14240            if node.kind() == "namespace_definition" {
14241                return WalkControl::SkipChildren;
14242            }
14243            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14244        });
14245    }
14246    owners
14247}
14248
14249/// Collect owner definitions after a malformed namespace, stopping only at
14250/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
14251/// enclosing container is not trusted to belong to the recovered namespace.
14252fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14253    parent: Node<'_>,
14254    sentinel_node: Node<'_>,
14255    namespace_components: &[String],
14256    source: &str,
14257) -> Option<Vec<CppSentinelRecoveredOwner>> {
14258    let mut owners = Vec::new();
14259    let mut after_namespace = false;
14260    let mut cursor = parent.walk();
14261    for child in parent.named_children(&mut cursor) {
14262        if !after_namespace {
14263            if same_node(child, sentinel_node) {
14264                after_namespace = true;
14265            }
14266            continue;
14267        }
14268        if cpp_sentinel_namespace_end(child, source) {
14269            return Some(owners);
14270        }
14271        walk_named_tree_preorder(child, true, |node| {
14272            if node.kind() == "namespace_definition" {
14273                return WalkControl::SkipChildren;
14274            }
14275            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14276        });
14277    }
14278    None
14279}
14280
14281fn cpp_sentinel_recovered_sibling_owner_ranges(
14282    sentinel_node: Node<'_>,
14283    namespace_components: &[String],
14284    source: &str,
14285) -> Vec<CppSentinelRecoveredOwner> {
14286    let Some(declaration_list) = sentinel_node
14287        .parent()
14288        .filter(|parent| parent.kind() == "declaration_list")
14289    else {
14290        return Vec::new();
14291    };
14292    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14293        declaration_list,
14294        sentinel_node,
14295        namespace_components,
14296        source,
14297    );
14298
14299    let Some(namespace) = declaration_list
14300        .parent()
14301        .filter(|parent| parent.kind() == "namespace_definition")
14302    else {
14303        return owners;
14304    };
14305    let Some(outer_parent) = namespace.parent() else {
14306        return owners;
14307    };
14308    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14309        outer_parent,
14310        namespace,
14311        namespace_components,
14312        source,
14313    ) {
14314        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
14315    }
14316    owners
14317}
14318
14319fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
14320    let mut current = function_declarator.child_by_field_name("declarator")?;
14321    loop {
14322        if let Some(name) = macro_decorated_unqualified_name(current) {
14323            current = name;
14324            continue;
14325        }
14326        if matches!(
14327            current.kind(),
14328            "qualified_identifier"
14329                | "scoped_identifier"
14330                | "scoped_type_identifier"
14331                | "identifier"
14332                | "field_identifier"
14333                | "operator_name"
14334                | "destructor_name"
14335                | "literal_operator_name"
14336        ) {
14337            return Some(current);
14338        }
14339        current = current
14340            .child_by_field_name("declarator")
14341            .or_else(|| current.child_by_field_name("name"))
14342            .or_else(|| last_named_child(current))?;
14343    }
14344}
14345
14346/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
14347/// `::` between the halves. The scope is an attribute-like macro token, not
14348/// a namespace; `Name` is the declared name.
14349///
14350/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
14351/// tree-sitter joins the attribute macro and the constructor name into one
14352/// `qualified_identifier` and marks the separator it had to invent as MISSING.
14353/// That flag is the grammar's own record that no separator is in the source,
14354/// so read it instead of looking for `::` in the node text. A genuine
14355/// `Outer::Inner` keeps a present separator and is declined here, and so is
14356/// the explicit-global `::name`, whose `::` is present and whose scope is
14357/// absent.
14358fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
14359    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
14360        return None;
14361    }
14362    let mut cursor = node.walk();
14363    if node
14364        .children(&mut cursor)
14365        .any(|child| child.kind() == "::" && !child.is_missing())
14366    {
14367        return None;
14368    }
14369    node.child_by_field_name("name")
14370}
14371
14372fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
14373    if let Some(name) = macro_decorated_unqualified_name(node) {
14374        return cpp_name_components(name, source);
14375    }
14376    match node.kind() {
14377        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14378            let mut components = match node.child_by_field_name("scope") {
14379                Some(scope) => cpp_name_components(scope, source)?,
14380                None => Vec::new(),
14381            };
14382            let name = node.child_by_field_name("name")?;
14383            components.push(canonical_cpp_qualified_component(name, source)?);
14384            Some(components)
14385        }
14386        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
14387    }
14388}
14389
14390fn cpp_sentinel_fragment_boundary<'tree>(
14391    function: Node<'tree>,
14392    class_node: Node<'tree>,
14393    class_body: Node<'tree>,
14394    source: &str,
14395) -> Option<(Node<'tree>, Node<'tree>)> {
14396    let declaration_list = function.parent()?;
14397    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
14398        return None;
14399    }
14400    let namespace = declaration_list.parent()?;
14401    if namespace.kind() != "namespace_definition"
14402        || namespace.child_by_field_name("body") != Some(declaration_list)
14403    {
14404        return None;
14405    }
14406    let mut cursor = declaration_list.walk();
14407    let closes = declaration_list
14408        .children(&mut cursor)
14409        .filter(|child| {
14410            !child.is_named()
14411                && child.kind() == "}"
14412                && child.start_byte() >= function.end_byte()
14413                && child.start_byte() > class_node.end_byte()
14414                && child.start_byte() > class_body.start_byte()
14415        })
14416        .collect::<Vec<_>>();
14417    let [close] = closes.as_slice() else {
14418        return None;
14419    };
14420    let semicolon = namespace.next_named_sibling()?;
14421    if !cpp_is_stray_semicolon(semicolon, source)
14422        || close.end_byte() != namespace.end_byte()
14423        || semicolon.start_byte() < namespace.end_byte()
14424    {
14425        return None;
14426    }
14427    Some((*close, semicolon))
14428}
14429
14430/// Detect the bogus declaration/function tree that tree-sitter recovers for a
14431/// region prefixed by an object-like macro sentinel the parser cannot see
14432/// (issue #941), and return the byte range `[start, end)` of the swallowed
14433/// declaration interior to reparse.
14434///
14435/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
14436/// `function_definition` whose first non-comment named child is the sentinel
14437/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
14438/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
14439/// the real items.
14440/// `start` is the end of the sentinel identifier -- everything after it is the
14441/// genuine source. `end` is the node's end, extended across any trailing empty
14442/// `;` statement the mis-parse displaced past the node (the class/struct closing
14443/// semicolon), so the reparse sees a complete, brace-balanced item.
14444///
14445/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
14446/// node (`has_error`). Unknown annotation/export macros can make a real callable
14447/// error-recovered even though tree-sitter still preserves its declarator, so a
14448/// preserved callable is admitted only when a displaced class keyword precedes
14449/// that declarator. The clean-reparse-to-items gate in
14450/// `cpp_reparsed_items_are_indexable` is the final arbiter.
14451/// Return the reparse start and, when present, the structurally recovered class
14452/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
14453/// separately from the reparse start because an opaque template-declaration
14454/// macro may precede it.
14455fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
14456    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
14457    {
14458        return None;
14459    }
14460    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
14461    // retain a valid function declarator despite the unknown export macro making
14462    // the outer node erroneous. Remember that declarator for the ordering gate
14463    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
14464    // class keyword precedes a spurious callable assembled from a later member.
14465    let mut declarator_cursor = node.walk();
14466    let preserved_callable = node
14467        .children_by_field_name("declarator", &mut declarator_cursor)
14468        .find_map(extract_function_declarator);
14469    // Leading documentation comments are attached to the malformed
14470    // `function_definition` as named children.  They are not part of the
14471    // sentinel prefix, so select the first non-comment child structurally
14472    // rather than requiring the sentinel to be child zero.  This is the shape
14473    // emitted for nlohmann/json's `basic_json`: its class documentation comment
14474    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
14475    // envelope otherwise ends at the first nested union.
14476    let mut cursor = node.walk();
14477    let first = node
14478        .named_children(&mut cursor)
14479        .find(|child| child.kind() != "comment")?;
14480    if first.kind() != "type_identifier" {
14481        return None;
14482    }
14483    let sentinel = normalize_cpp_whitespace(node_text(first, source));
14484    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
14485        return None;
14486    }
14487    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
14488    // makes the trailing sentinel of one region and the leading sentinel of the
14489    // next both land as bare macro-token identifiers ahead of the real content.
14490    // Advance past every leading macro-token identifier so the reparse begins at
14491    // genuine source rather than another sentinel that would re-form the bogus
14492    // shape and fail the reparse gate.
14493    let mut start = first.end_byte();
14494    let mut after_first = false;
14495    let mut cursor = node.walk();
14496    for child in node.named_children(&mut cursor) {
14497        if !after_first {
14498            if same_node(child, first) {
14499                after_first = true;
14500            }
14501            continue;
14502        }
14503        if matches!(child.kind(), "identifier" | "type_identifier")
14504            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
14505        {
14506            start = child.end_byte();
14507        } else {
14508            break;
14509        }
14510    }
14511    // An additional opaque template-declaration macro before a class can be
14512    // folded into the bogus function's qualified declarator.  In that shape
14513    // the macro is not a direct sibling we can skip above; tree-sitter exposes
14514    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
14515    // Reparse from that keyword (or a real preceding `template` keyword) so the
14516    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
14517    // a class nested in a genuine sentinel-wrapped namespace lies after the
14518    // body opening and must not change the established region start.
14519    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
14520    let mut class_start = None;
14521    let mut template_start = None;
14522    let mut stack = vec![node];
14523    while let Some(current) = stack.pop() {
14524        if current.start_byte() >= prefix_end {
14525            continue;
14526        }
14527        if matches!(
14528            current.kind(),
14529            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
14530        ) {
14531            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
14532                "class" | "struct" | "union" | "enum" => {
14533                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
14534                        seen.min(current.start_byte())
14535                    }));
14536                }
14537                "template" => {
14538                    template_start =
14539                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
14540                            seen.min(current.start_byte())
14541                        }));
14542                }
14543                _ => {}
14544            }
14545        }
14546        let mut cursor = current.walk();
14547        stack.extend(current.children(&mut cursor));
14548    }
14549    if preserved_callable.is_some_and(|callable| {
14550        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
14551    }) {
14552        return None;
14553    }
14554    if let Some(class_start) = class_start {
14555        start = template_start
14556            .filter(|template_start| *template_start < class_start)
14557            .unwrap_or(class_start);
14558    }
14559    Some((start, class_start))
14560}
14561
14562/// Locate a sentinel-prefixed class whose malformed declaration was split across
14563/// root-level siblings. The true class close is represented structurally as a
14564/// lone `}` error followed by the class's displaced `;`; nested method/body
14565/// errors are not direct siblings of the sentinel node and therefore cannot
14566/// satisfy this pair.
14567fn cpp_sentinel_macro_class_region<'tree>(
14568    node: Node<'tree>,
14569    source: &str,
14570) -> Option<(usize, usize, usize, usize, usize, usize)> {
14571    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
14572        return None;
14573    };
14574    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
14575        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
14576        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
14577    if class_start >= body_open_start {
14578        return None;
14579    }
14580    let sibling_close = {
14581        let mut sibling = node.next_named_sibling();
14582        let mut found = None;
14583        while let Some(current) = sibling {
14584            let next = current.next_named_sibling();
14585            if cpp_is_stray_close_brace(current, source)
14586                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
14587            {
14588                let semicolon = next.expect("checked above");
14589                found = Some((
14590                    current.start_byte(),
14591                    semicolon.end_byte(),
14592                    semicolon.end_position().row + 1,
14593                ));
14594                break;
14595            }
14596            sibling = next;
14597        }
14598        found
14599    };
14600    // A stray `};` sibling is this class's close only when the bounded reparse
14601    // agrees the first body-bearing class ENDS there. When the malformed
14602    // envelope swallowed the class's true close, the scan can promote a much
14603    // later scope's close instead -- in protobuf-generated headers
14604    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
14605    // `struct TableStruct_*` paired with the first message class's `};`, making
14606    // the recovered "class body" span whole `namespace {}` blocks and minting
14607    // namespace-scope classes as nested members of the recovered class, which
14608    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
14609    // (#2275). On disagreement, fall through to the suffix-reparse boundary
14610    // below, which derives the close from the class node's own balanced body
14611    // range.
14612    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
14613        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14614            return false;
14615        };
14616        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14617        // A region reparse is its own tree and needs its own parent index.
14618        let reparsed_ancestry = ParentIndex::new(tree.root_node());
14619        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
14620            tree.root_node(),
14621            template_node,
14622            source,
14623            &reparsed_ancestry,
14624        ) else {
14625            return false;
14626        };
14627        let body = reparsed_class.body;
14628        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
14629    });
14630    let (class_close_start, class_close_end, class_close_line) =
14631        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
14632            (class_close_start, class_close_end, class_close_line)
14633        } else {
14634            // When the malformed envelope itself is an ERROR, tree-sitter can
14635            // leave the class's balanced close in the source while promoting
14636            // all following members to siblings. Reparse the complete suffix
14637            // and use the first body-bearing class node's own field range as
14638            // the partition boundary. This keeps balancing in tree-sitter and
14639            // preserves the source's original byte offsets.
14640            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
14641            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14642            // A region reparse is its own tree and needs its own parent index.
14643            let reparsed_ancestry = ParentIndex::new(tree.root_node());
14644            let reparsed_class = cpp_sentinel_reparsed_class(
14645                tree.root_node(),
14646                template_node,
14647                source,
14648                &reparsed_ancestry,
14649            )?;
14650            let body = reparsed_class.body;
14651            let class_close_end = body.end_byte();
14652            let class_close_start = class_close_end.checked_sub(1)?;
14653            let class_close_line = body.end_position().row + 1;
14654            (class_close_start, class_close_end, class_close_line)
14655        };
14656    if class_close_start <= class_start {
14657        return None;
14658    }
14659
14660    // Reparse only far enough to expose the class body opening. This is a
14661    // structured check that the candidate really begins with a body-bearing
14662    // class-like item; the original malformed tree cannot provide that node.
14663    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
14664    let class_root = tree.root_node();
14665    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
14666    // A region reparse is its own tree and needs its own parent index.
14667    let reparsed_ancestry = ParentIndex::new(class_root);
14668    let reparsed_class =
14669        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
14670    let body = reparsed_class.body;
14671    // The class body opening must agree with the malformed wrapper's structured
14672    // body field. This rejects an inner nested class while permitting later
14673    // members to remain fragmented as root-level siblings in the bounded parse.
14674    if body.start_byte() != body_open_start {
14675        return None;
14676    }
14677    let body_start = body.start_byte().checked_add(1)?;
14678    (body_start < class_close_start).then_some((
14679        reparse_start,
14680        class_start,
14681        body_start,
14682        class_close_start,
14683        class_close_end,
14684        class_close_line,
14685    ))
14686}
14687
14688/// Find the `{` token immediately following the class/struct/union/enum token
14689/// at `class_start` in the malformed tree. The token is anonymous in the C++
14690/// grammar, so this deliberately walks all children (not only named children)
14691/// and relies on sibling structure rather than source-text searching.
14692fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
14693    let mut stack = vec![node];
14694    while let Some(current) = stack.pop() {
14695        if current.start_byte() == class_start
14696            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
14697        {
14698            let mut sibling = current.next_sibling();
14699            while let Some(candidate) = sibling {
14700                if candidate.kind() == "{" {
14701                    return Some(candidate.start_byte());
14702                }
14703                sibling = candidate.next_sibling();
14704            }
14705        }
14706        let mut cursor = current.walk();
14707        stack.extend(current.children(&mut cursor));
14708    }
14709    None
14710}
14711
14712/// The class body that tree-sitter displaced out of a sentinel-prefixed
14713/// declaration and left as the malformed node's next sibling.
14714///
14715/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
14716/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
14717/// the last child of that `ERROR` and its `{` opens a sibling
14718/// `compound_statement` instead. The body is still the malformed tree's own
14719/// structured token, which is what the caller's `body.start_byte() !=
14720/// body_open_start` agreement check needs; it just is not reachable by walking
14721/// forward from the class token inside the node.
14722fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
14723    node.next_named_sibling()
14724        .filter(|sibling| sibling.kind() == "compound_statement")
14725}
14726
14727fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
14728    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
14729    let mut end = if class_start.is_some() {
14730        cpp_macro_prefixed_class_end(source, start)?
14731    } else {
14732        node.end_byte()
14733    };
14734    if class_start.is_none()
14735        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
14736    {
14737        end = end.max(namespace_end);
14738    }
14739    let mut sibling = node.next_named_sibling();
14740    while let Some(current) = sibling {
14741        if !cpp_is_stray_semicolon(current, source) {
14742            break;
14743        }
14744        end = current.end_byte();
14745        sibling = current.next_named_sibling();
14746    }
14747    (start < end).then_some((start, end))
14748}
14749
14750/// Extend a sentinel reparse through a following namespace that tree-sitter
14751/// flattened into the sentinel node's sibling list.
14752///
14753/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
14754/// unknown macro becomes a false function return type and consumes the first
14755/// namespace body. A second `namespace detail` then loses its enclosing node:
14756/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
14757/// attaches its declarations to the surrounding error tree. Reparse from that
14758/// structured keyword so tree-sitter, rather than a source-text brace scan,
14759/// supplies the complete namespace boundary.
14760fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
14761    let mut sibling = node.next_sibling();
14762    let keyword = loop {
14763        let candidate = sibling?;
14764        sibling = candidate.next_sibling();
14765        if candidate.kind() != "comment" {
14766            break candidate;
14767        }
14768    };
14769    if keyword.kind() != "namespace" {
14770        return None;
14771    }
14772    let name = loop {
14773        let candidate = sibling?;
14774        sibling = candidate.next_sibling();
14775        if candidate.kind() != "comment" {
14776            break candidate;
14777        }
14778    };
14779    if cpp_namespace_name_components(name, source).is_empty() {
14780        return None;
14781    }
14782    let open = loop {
14783        let candidate = sibling?;
14784        sibling = candidate.next_sibling();
14785        if candidate.kind() != "comment" {
14786            break candidate;
14787        }
14788    };
14789    if open.kind() != "{" {
14790        return None;
14791    }
14792
14793    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
14794    let root = tree.root_node();
14795    let mut cursor = root.walk();
14796    let namespace = root
14797        .named_children(&mut cursor)
14798        .find(|candidate| candidate.kind() != "comment")?;
14799    (namespace.kind() == "namespace_definition"
14800        && namespace.start_byte() == keyword.start_byte()
14801        && namespace.child_by_field_name("body").is_some())
14802    .then_some(namespace.end_byte())
14803}
14804
14805/// Parse the source suffix beginning at a structurally recovered class/template
14806/// keyword and return the end of its first body-bearing class item.  The parser,
14807/// rather than a brace scanner, owns nested-body balancing.  This is needed when
14808/// the original error tree truncates the class and scatters later members as
14809/// top-level siblings.
14810fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
14811    let tree = cpp_reparse_region_items(source, start, source.len())?;
14812    let root = tree.root_node();
14813    let mut cursor = root.walk();
14814    for item in root.named_children(&mut cursor) {
14815        if item.end_byte() <= start || item.kind() == "comment" {
14816            continue;
14817        }
14818        let mut stack = vec![item];
14819        while let Some(current) = stack.pop() {
14820            if matches!(
14821                current.kind(),
14822                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
14823            ) && cpp_body_node(current).is_some()
14824            {
14825                return Some(current.end_byte());
14826            }
14827            let mut cursor = current.walk();
14828            stack.extend(current.named_children(&mut cursor));
14829        }
14830        // The recovered prefix is required to begin with the class item.  If
14831        // the first real item is something else, fail closed rather than skip
14832        // arbitrary source looking for a later class.
14833        return None;
14834    }
14835    None
14836}
14837
14838/// An empty `;` statement: the displaced closing semicolon of a struct/class that
14839/// the sentinel mis-parse split off past the bogus function node.
14840fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
14841    node.kind() == "expression_statement"
14842        && node.named_child_count() == 0
14843        && node_text(node, source).trim() == ";"
14844}
14845
14846/// Recover the member that follows CPython's object header macro when
14847/// tree-sitter folds both declarations into one malformed field.
14848///
14849/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
14850/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
14851/// sole child is the real field declarator. The tail may wrap that identifier
14852/// in pointer declarators, as in `PyObject_HEAD ImagingObject *image;`; other
14853/// malformed wrappers do not provide the same evidence that this is the
14854/// CPython macro boundary.
14855#[derive(Clone, Copy)]
14856pub(crate) struct RecoveredPyObjectHeadField<'tree> {
14857    pub(crate) type_node: Node<'tree>,
14858    pub(crate) name: Node<'tree>,
14859    pub(crate) declarator: Node<'tree>,
14860}
14861
14862impl RecoveredPyObjectHeadField<'_> {
14863    pub(crate) fn pointer_depth(self) -> i32 {
14864        let mut depth = 0;
14865        let mut current = self.declarator;
14866        while current != self.name {
14867            debug_assert_eq!(current.kind(), "pointer_declarator");
14868            depth += 1;
14869            current = current
14870                .child_by_field_name("declarator")
14871                .expect("recovered PyObject field pointer has an inner declarator");
14872        }
14873        depth
14874    }
14875}
14876
14877pub(crate) fn recovered_pyobject_head_field<'tree>(
14878    node: Node<'tree>,
14879    source: &str,
14880) -> Option<RecoveredPyObjectHeadField<'tree>> {
14881    if node.kind() != "field_declaration" {
14882        return None;
14883    }
14884    let type_node = node.child_by_field_name("type")?;
14885    if type_node.kind() != "type_identifier"
14886        || node_text(type_node, source).trim() != "PyObject_HEAD"
14887    {
14888        return None;
14889    }
14890    let pseudo_declarator = node.child_by_field_name("declarator")?;
14891    let mut cursor = node.walk();
14892    let errors = node
14893        .named_children(&mut cursor)
14894        .filter(|child| child.kind() == "ERROR")
14895        .collect::<Vec<_>>();
14896    let [error] = errors.as_slice() else {
14897        return None;
14898    };
14899    if error.named_child_count() != 1 {
14900        return None;
14901    }
14902    let error_child = error.named_child(0)?;
14903    if pseudo_declarator.kind() == "field_identifier"
14904        && error.start_byte() >= pseudo_declarator.end_byte()
14905        && error_child.kind() == "identifier"
14906    {
14907        return Some(RecoveredPyObjectHeadField {
14908            type_node: pseudo_declarator,
14909            name: error_child,
14910            declarator: error_child,
14911        });
14912    }
14913    if pseudo_declarator.kind() != "pointer_declarator"
14914        || error.end_byte() > pseudo_declarator.start_byte()
14915        || error_child.kind() != "identifier"
14916    {
14917        return None;
14918    }
14919    let mut name = pseudo_declarator;
14920    while name.kind() == "pointer_declarator" {
14921        name = name.child_by_field_name("declarator")?;
14922    }
14923    (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
14924        type_node: error_child,
14925        name,
14926        declarator: pseudo_declarator,
14927    })
14928}
14929
14930/// Recover the real field name when a leading object-like annotation macro
14931/// displaces a qualified type into tree-sitter's bit-field recovery shape.
14932///
14933/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
14934/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
14935/// `bitfield_clause` containing an error plus an assignment.  The assignment's
14936/// left field is the only structured declaration name in that malformed tail.
14937/// A real bit-field is excluded by the all-caps macro type and required error.
14938fn recovered_macro_qualified_field_declarators<'tree>(
14939    node: Node<'tree>,
14940    source: &str,
14941) -> Option<Vec<Node<'tree>>> {
14942    if node.kind() != "field_declaration" {
14943        return None;
14944    }
14945    let macro_type = node.child_by_field_name("type")?;
14946    if macro_type.kind() != "type_identifier"
14947        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
14948    {
14949        return None;
14950    }
14951    let pseudo_declarator = node.child_by_field_name("declarator")?;
14952    if pseudo_declarator.kind() != "field_identifier" {
14953        return None;
14954    }
14955    let mut cursor = node.walk();
14956    let clause = node
14957        .named_children(&mut cursor)
14958        .find(|child| child.kind() == "bitfield_clause")?;
14959    if !(0..clause.named_child_count()).any(|index| {
14960        clause
14961            .named_child(index)
14962            .is_some_and(|child| child.kind() == "ERROR")
14963    }) {
14964        return None;
14965    }
14966    let mut recovered = Vec::new();
14967    let mut stack = vec![clause];
14968    while let Some(current) = stack.pop() {
14969        if current.kind() == "assignment_expression"
14970            && let Some(left) = current.child_by_field_name("left")
14971            && extract_variable_name(left, source).is_some()
14972        {
14973            recovered.push(left);
14974            break;
14975        }
14976        let mut cursor = current.walk();
14977        stack.extend(current.named_children(&mut cursor));
14978    }
14979    if recovered.is_empty() {
14980        return None;
14981    }
14982    let mut cursor = node.walk();
14983    recovered.extend(
14984        node.children_by_field_name("declarator", &mut cursor)
14985            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
14986    );
14987    Some(recovered)
14988}
14989
14990/// Recover a macro-qualified constructor that tree-sitter represents as one
14991/// field declaration. The constructor call remains inside the direct recovery
14992/// error, while each member initializer becomes a false function declarator.
14993/// The class owner proves the constructor name and lets the caller ignore those
14994/// initializer declarators.
14995fn recovered_macro_qualified_constructor_call<'tree>(
14996    node: Node<'tree>,
14997    class_name: &str,
14998    source: &str,
14999) -> Option<Node<'tree>> {
15000    if node.kind() != "field_declaration" {
15001        return None;
15002    }
15003    let macro_type = node.child_by_field_name("type")?;
15004    if macro_type.kind() != "type_identifier"
15005        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15006    {
15007        return None;
15008    }
15009    let mut cursor = node.walk();
15010    let bitfield = node
15011        .named_children(&mut cursor)
15012        .find(|child| child.kind() == "bitfield_clause")?;
15013    let error = bitfield
15014        .named_child(0)
15015        .filter(|child| child.kind() == "ERROR")?;
15016    let mut stack = vec![error];
15017    while let Some(current) = stack.pop() {
15018        if current.kind() == "call_expression"
15019            && current
15020                .child_by_field_name("function")
15021                .is_some_and(|function| node_text(function, source) == class_name)
15022            && current
15023                .child_by_field_name("arguments")
15024                .is_some_and(|arguments| arguments.kind() == "argument_list")
15025        {
15026            return Some(current);
15027        }
15028        let mut cursor = current.walk();
15029        stack.extend(current.named_children(&mut cursor));
15030    }
15031    None
15032}
15033
15034/// Recover a macro-qualified member function declaration that tree-sitter
15035/// represents as a pseudo-field. An object-like export macro before a qualified
15036/// return type can displace the namespace and type into an ERROR/bitfield
15037/// recovery, leaving the callable as a structured `call_expression`.
15038///
15039/// The caller must route this shape before ordinary declarator classification;
15040/// otherwise the displaced namespace identifier is published as a field.
15041fn recovered_macro_qualified_function_call<'tree>(
15042    node: Node<'tree>,
15043    source: &str,
15044) -> Option<Node<'tree>> {
15045    if node.kind() != "field_declaration" {
15046        return None;
15047    }
15048    let macro_type = node.child_by_field_name("type")?;
15049    if macro_type.kind() != "type_identifier"
15050        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15051    {
15052        return None;
15053    }
15054    let declarator = node.child_by_field_name("declarator")?;
15055    if declarator.kind() != "field_identifier" {
15056        return None;
15057    }
15058    let mut cursor = node.walk();
15059    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15060    if !named.iter().any(|child| {
15061        child.kind() == "storage_class_specifier"
15062            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
15063    }) {
15064        return None;
15065    }
15066    let bitfield = named
15067        .iter()
15068        .find(|child| child.kind() == "bitfield_clause")?;
15069    let mut bitfield_cursor = bitfield.walk();
15070    let payload = bitfield
15071        .named_children(&mut bitfield_cursor)
15072        .collect::<Vec<_>>();
15073    let [displaced_error, call] = payload.as_slice() else {
15074        return None;
15075    };
15076    if displaced_error.kind() != "ERROR"
15077        || displaced_error.named_child_count() != 1
15078        || displaced_error
15079            .named_child(0)
15080            .is_none_or(|child| child.kind() != "identifier")
15081        || call.kind() != "call_expression"
15082        || call
15083            .child_by_field_name("function")
15084            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
15085        || call
15086            .child_by_field_name("arguments")
15087            .is_none_or(|arguments| arguments.kind() != "argument_list")
15088    {
15089        return None;
15090    }
15091    Some(*call)
15092}
15093
15094fn recovered_macro_qualified_function_parameters(
15095    arguments: Node<'_>,
15096    source: &str,
15097) -> Option<(String, Vec<String>)> {
15098    if arguments.kind() != "argument_list" {
15099        return None;
15100    }
15101    let mut cursor = arguments.walk();
15102    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
15103    if named.is_empty() {
15104        return Some(("()".to_string(), Vec::new()));
15105    }
15106    let mut types = Vec::new();
15107    let mut labels = Vec::new();
15108    let mut index = 0;
15109    while index < named.len() {
15110        let parameter_type = named[index];
15111        let parameter_name = named.get(index + 1).copied()?;
15112        if !matches!(
15113            parameter_type.kind(),
15114            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
15115        ) || parameter_name.kind() != "ERROR"
15116            || parameter_name.named_child_count() != 1
15117            || parameter_name
15118                .named_child(0)
15119                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
15120        {
15121            return None;
15122        }
15123        let parameter_name = parameter_name.named_child(0)?;
15124        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
15125        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
15126        index += 2;
15127    }
15128    Some((format!("({})", types.join(", ")), labels))
15129}
15130
15131/// Recognize the phantom field tree-sitter emits for a macro-qualified
15132/// function return type.  For example,
15133/// `static API result_type ThresholdForSmallA() { ... }` can become a
15134/// `field_declaration` (`API` as the type and `result_type` as a field name)
15135/// followed by a clean `function_definition` for `ThresholdForSmallA`.
15136///
15137/// Keep this predicate entirely tied to the CST envelope: the type must be an
15138/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
15139/// the declaration must carry a missing semicolon rather than a real one, and
15140/// the immediate named sibling must expose a function declarator.  A real
15141/// macro-decorated field with an explicit semicolon therefore remains a field.
15142pub fn recovered_macro_return_type_node<'tree>(
15143    node: Node<'tree>,
15144    source: &str,
15145) -> Option<Node<'tree>> {
15146    if node.kind() != "field_declaration" {
15147        return None;
15148    }
15149    let macro_type = node.child_by_field_name("type")?;
15150    if macro_type.kind() != "type_identifier"
15151        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15152    {
15153        return None;
15154    }
15155    let declarator = node.child_by_field_name("declarator")?;
15156    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
15157        return None;
15158    }
15159    let mut has_missing_semicolon = false;
15160    let mut has_real_semicolon = false;
15161    for child in children_iter(node) {
15162        if child.kind() != ";" {
15163            continue;
15164        }
15165        if child.is_missing() {
15166            has_missing_semicolon = true;
15167        } else {
15168            has_real_semicolon = true;
15169        }
15170    }
15171    if !has_missing_semicolon || has_real_semicolon {
15172        return None;
15173    }
15174    let mut next = node.next_named_sibling();
15175    while next.is_some_and(|sibling| sibling.kind() == "comment") {
15176        next = next.and_then(|sibling| sibling.next_named_sibling());
15177    }
15178    let next = next?;
15179    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
15180        return None;
15181    }
15182    let function_declarator = next.child_by_field_name("declarator")?;
15183    extract_function_declarator(function_declarator).map(|_| declarator)
15184}
15185
15186/// Whether `name` is a type parameter of a template declaration that lexically
15187/// encloses `node`. The malformed macro-return field uses the parameter name as
15188/// its pseudo-declarator; preserving that field is necessary to publish a
15189/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
15190/// ancestors instead of interpreting source text so nested templates and
15191/// parser-recovered regions retain their real lexical scopes.
15192pub(crate) fn cpp_active_template_type_parameter<'tree>(
15193    node: Node<'tree>,
15194    name: &str,
15195    source: &str,
15196    ancestry: &ParentIndex<'tree>,
15197) -> bool {
15198    let mut ancestor = ancestry.parent(node);
15199    while let Some(current) = ancestor {
15200        if current.kind() == "template_declaration"
15201            && let Some(parameters) = current.child_by_field_name("parameters")
15202        {
15203            let mut cursor = parameters.walk();
15204            if parameters.named_children(&mut cursor).any(|parameter| {
15205                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
15206                    && cpp_template_parameter_name(parameter, source)
15207                        .is_some_and(|parameter_name| parameter_name == name)
15208            }) {
15209                return true;
15210            }
15211        }
15212        ancestor = ancestry.parent(current);
15213    }
15214    false
15215}
15216
15217/// Reparse the region `[start, end)` of `source` as C++, confined to the region
15218/// via included ranges so every reparsed node keeps its original byte offset and
15219/// line number. The existing visitors read node text from the original source,
15220/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
15221/// `parse_rust_region_tree` technique.
15222fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
15223    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
15224}
15225
15226fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
15227    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
15228        return None;
15229    }
15230    let semicolon = node.next_sibling()?;
15231    if semicolon.kind() != ";" || semicolon.is_missing() {
15232        return None;
15233    }
15234    let row = node.start_position().row;
15235    let mut start = node.start_byte();
15236    let mut sibling = node.prev_sibling();
15237    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
15238        if previous.kind() == ";" {
15239            break;
15240        }
15241        start = previous.start_byte();
15242        sibling = previous.prev_sibling();
15243    }
15244    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
15245}
15246
15247/// One `RET name _(( args ));`-style K&R prototype-macro invocation
15248/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
15249/// byte spans a clean reparse needs, in original-source order.
15250struct PrototypeMacroCandidate {
15251    /// Where the return type and declared name begin.
15252    run_start: usize,
15253    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
15254    /// exclusive end of the first reparse span.
15255    identifier_start: usize,
15256    /// The inner `(`'s start byte: the start of the second reparse span.
15257    /// This paren is kept so the reparse sees exactly one parameter-list
15258    /// paren pair.
15259    inner_open_start: usize,
15260    /// Byte just past the inner `)`: the end of the second reparse span.
15261    inner_close_end: usize,
15262    /// Byte just past the outer `)`: the start of the third reparse span.
15263    outer_close_end: usize,
15264    /// Byte just past the terminating `;`: the end of the third reparse
15265    /// span, and of the whole candidate.
15266    semicolon_end: usize,
15267}
15268
15269impl PrototypeMacroCandidate {
15270    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
15271    /// one included-range tree: the return type and name, the parenthesized
15272    /// argument list, and the terminating `;`. The macro token and its outer
15273    /// wrapping parenthesis are omitted -- deleting them is exactly what the
15274    /// (absent) preprocessor macro expansion would do.
15275    fn ranges(&self) -> [(usize, usize); 3] {
15276        [
15277            (self.run_start, self.identifier_start),
15278            (self.inner_open_start, self.inner_close_end),
15279            (self.outer_close_end, self.semicolon_end),
15280        ]
15281    }
15282}
15283
15284/// A live terminating semicolon owned directly by `node`.
15285fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
15286    node.child(node.child_count().checked_sub(1)?)
15287        .filter(|child| child.kind() == ";" && !child.is_missing())
15288}
15289
15290/// The known pre-ANSI prototype macros whose expansion is exactly their one
15291/// argument. The malformed CST cannot prove that an arbitrary identifier has
15292/// that definition, so unknown spellings fail closed rather than turning an
15293/// ordinary broken declaration into a Function (issue #2932).
15294fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
15295    matches!(
15296        node.kind(),
15297        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
15298    ) && matches!(
15299        normalize_cpp_whitespace(node_text(node, source)).as_str(),
15300        "_" | "__P" | "OF" | "PROTO"
15301    )
15302}
15303
15304/// Read tree-sitter's recovery for `declared_name MACRO`: a
15305/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
15306/// declared name, and whose `name` is a known prototype macro.
15307fn cpp_prototype_macro_qualified_parts<'tree>(
15308    node: Node<'tree>,
15309    source: &str,
15310) -> Option<(Node<'tree>, Node<'tree>)> {
15311    if node.kind() != "qualified_identifier" {
15312        return None;
15313    }
15314    let declared_name = node
15315        .child_by_field_name("scope")
15316        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
15317    let macro_name = macro_decorated_unqualified_name(node)?;
15318    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
15319}
15320
15321/// The inner parenthesized node of tree-sitter's structured
15322/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
15323/// the macro invocation's outer parentheses while retaining the one pair an
15324/// ordinary function declarator needs. No delimiter search is involved: both
15325/// pairs and their ownership come from the CST.
15326fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
15327    if arguments.kind() != "argument_list"
15328        || arguments.named_child_count() != 1
15329        || arguments.child_count() != 3
15330        || arguments
15331            .child(0)
15332            .is_none_or(|open| open.kind() != "(" || open.is_missing())
15333        || arguments
15334            .child(2)
15335            .is_none_or(|close| close.kind() != ")" || close.is_missing())
15336    {
15337        return None;
15338    }
15339    let inner = arguments.named_child(0)?;
15340    let close_index = match inner.kind() {
15341        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
15342        // tree-sitter completes the C++ cast production with a zero-width
15343        // value after the live close parenthesis.
15344        "cast_expression" => inner.child_count().checked_sub(2)?,
15345        _ => return None,
15346    };
15347    (inner
15348        .child(0)
15349        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
15350        && inner
15351            .child(close_index)
15352            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
15353    .then_some(inner)
15354}
15355
15356fn cpp_prototype_macro_candidate_from_init_declaration(
15357    declaration: Node<'_>,
15358    source: &str,
15359) -> Option<PrototypeMacroCandidate> {
15360    let init = declaration
15361        .child_by_field_name("declarator")
15362        .filter(|declarator| declarator.kind() == "init_declarator")?;
15363    let malformed_declarator = init.child_by_field_name("declarator")?;
15364    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
15365        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
15366    } else {
15367        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
15368            return None;
15369        }
15370        let declared_name_error = init
15371            .prev_named_sibling()
15372            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
15373        let declared_name = declared_name_error
15374            .named_child(0)
15375            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
15376        (declared_name, malformed_declarator)
15377    };
15378    let arguments = init
15379        .child_by_field_name("value")
15380        .filter(|value| value.kind() == "argument_list")?;
15381    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15382    let semicolon = cpp_direct_semicolon(declaration)?;
15383    let return_type = declaration.child_by_field_name("type")?;
15384    if return_type.end_byte() > declared_name.start_byte()
15385        || declared_name.end_byte() > macro_name.start_byte()
15386        || macro_name.end_byte() > arguments.start_byte()
15387        || arguments.end_byte() > semicolon.start_byte()
15388    {
15389        return None;
15390    }
15391    Some(PrototypeMacroCandidate {
15392        run_start: declaration.start_byte(),
15393        identifier_start: macro_name.start_byte(),
15394        inner_open_start: inner.start_byte(),
15395        inner_close_end: inner.end_byte(),
15396        outer_close_end: arguments.end_byte(),
15397        semicolon_end: semicolon.end_byte(),
15398    })
15399}
15400
15401fn cpp_prototype_macro_candidate_from_qualified_declaration(
15402    declaration: Node<'_>,
15403    source: &str,
15404) -> Option<PrototypeMacroCandidate> {
15405    let qualified = declaration
15406        .child_by_field_name("declarator")
15407        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
15408    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15409    let open_error = qualified
15410        .next_named_sibling()
15411        .filter(|next| next.kind() == "ERROR")?;
15412    let close_error = last_named_child(declaration)
15413        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
15414    if open_error.child_count() < 3
15415        || open_error
15416            .child(0)
15417            .is_none_or(|open| open.kind() != "(" || open.is_missing())
15418        || open_error
15419            .child(1)
15420            .is_none_or(|open| open.kind() != "(" || open.is_missing())
15421        || close_error.child_count() != 2
15422        || close_error
15423            .child(0)
15424            .is_none_or(|close| close.kind() != ")" || close.is_missing())
15425        || close_error
15426            .child(1)
15427            .is_none_or(|close| close.kind() != ")" || close.is_missing())
15428    {
15429        return None;
15430    }
15431    let inner_open = open_error.child(1)?;
15432    let inner_close = close_error.child(0)?;
15433    let outer_close = close_error.child(1)?;
15434    let semicolon = cpp_direct_semicolon(declaration)?;
15435    let return_type = declaration.child_by_field_name("type")?;
15436    if return_type.end_byte() > qualified.start_byte()
15437        || macro_name.end_byte() > open_error.start_byte()
15438        || inner_open.start_byte() > inner_close.end_byte()
15439        || inner_close.end_byte() > outer_close.start_byte()
15440        || outer_close.end_byte() > semicolon.start_byte()
15441    {
15442        return None;
15443    }
15444    Some(PrototypeMacroCandidate {
15445        run_start: declaration.start_byte(),
15446        identifier_start: macro_name.start_byte(),
15447        inner_open_start: inner_open.start_byte(),
15448        inner_close_end: inner_close.end_byte(),
15449        outer_close_end: outer_close.end_byte(),
15450        semicolon_end: semicolon.end_byte(),
15451    })
15452}
15453
15454fn cpp_prototype_macro_candidate_from_pointer_expression(
15455    statement: Node<'_>,
15456    source: &str,
15457) -> Option<PrototypeMacroCandidate> {
15458    if statement.kind() != "expression_statement"
15459        || statement.named_child_count() != 1
15460        || !statement.has_error()
15461    {
15462        return None;
15463    }
15464    let expansion = statement
15465        .named_child(0)
15466        .filter(|child| child.kind() == "parameter_pack_expansion")?;
15467    let binary = expansion
15468        .child_by_field_name("pattern")
15469        .filter(|pattern| pattern.kind() == "binary_expression")?;
15470    if binary.child_count() != 3
15471        || binary
15472            .child(1)
15473            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
15474        || expansion
15475            .child(expansion.child_count().checked_sub(1)?)
15476            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
15477    {
15478        return None;
15479    }
15480    let return_type = binary.child_by_field_name("left")?;
15481    let call = binary
15482        .child_by_field_name("right")
15483        .filter(|right| right.kind() == "call_expression")?;
15484    let qualified = call.child_by_field_name("function")?;
15485    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15486    let arguments = call
15487        .child_by_field_name("arguments")
15488        .filter(|arguments| arguments.kind() == "argument_list")?;
15489    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15490    let semicolon = cpp_direct_semicolon(statement)?;
15491    if return_type.end_byte() > declared_name.start_byte()
15492        || macro_name.end_byte() > arguments.start_byte()
15493        || arguments.end_byte() > semicolon.start_byte()
15494    {
15495        return None;
15496    }
15497    Some(PrototypeMacroCandidate {
15498        run_start: statement.start_byte(),
15499        identifier_start: macro_name.start_byte(),
15500        inner_open_start: inner.start_byte(),
15501        inner_close_end: inner.end_byte(),
15502        outer_close_end: arguments.end_byte(),
15503        semicolon_end: semicolon.end_byte(),
15504    })
15505}
15506
15507/// Recover only CST shapes whose fields preserve the declaration name, macro
15508/// invocation, nested argument list, and terminator. Arbitrary flattened
15509/// `ERROR` wreckage stays unsupported rather than being interpreted through a
15510/// token or delimiter scan (issue #2932).
15511fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
15512    let candidate = match node.kind() {
15513        "declaration" if node.has_error() => {
15514            cpp_prototype_macro_candidate_from_init_declaration(node, source)
15515                .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
15516        }
15517        "expression_statement" => {
15518            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
15519        }
15520        _ => None,
15521    };
15522    candidate.into_iter().collect()
15523}
15524
15525fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
15526    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
15527        return false;
15528    }
15529    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
15530        return false;
15531    }
15532    let Some(declarator) = (if node.kind() == "function_definition" {
15533        node.child_by_field_name("declarator")
15534            .and_then(extract_function_declarator)
15535    } else {
15536        node.named_child(0)
15537            .filter(|child| child.kind() == "function_declarator")
15538    }) else {
15539        return false;
15540    };
15541    let Some(name) = cpp_function_declarator_name_node(declarator) else {
15542        return false;
15543    };
15544    declarator.start_byte() == node.start_byte()
15545        && name.kind() == "identifier"
15546        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
15547}
15548
15549/// Reparse a fragmented class-body interior while preserving its original byte
15550/// and line offsets, confined to the region by tree-sitter included ranges.
15551///
15552/// This used to materialize the region's whole file prefix as whitespace and
15553/// make the lexer walk it, the technique #1309 replaced on the other reparse
15554/// path: O(file) per fragmented-class recovery, on files that are already
15555/// error-recovered and already slow (#2788). Included ranges give the parser
15556/// the same view -- the region's bytes, at their original offsets and
15557/// line/column positions -- without materializing or lexing anything before it.
15558///
15559/// The equality of the two views is the claim, so a debug build parses both and
15560/// asserts the trees agree node for node.
15561fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15562    let region = cpp_reparse_region_items(source, start, end);
15563
15564    #[cfg(debug_assertions)]
15565    assert_eq!(
15566        region.as_ref().map(cpp_tree_shape),
15567        cpp_reparse_padded_class_body(source, start, end)
15568            .as_ref()
15569            .map(cpp_tree_shape),
15570        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
15571         prefix produces"
15572    );
15573
15574    region
15575}
15576
15577/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
15578/// replaces, kept as the oracle a debug build asserts every region reparse
15579/// against and as the release-mode parity tests' reference (#2788).
15580#[cfg(any(debug_assertions, test))]
15581fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15582    if start >= end {
15583        // The included-range parser refuses an empty region; the padded one
15584        // returned a tree holding nothing, which every caller read as "no
15585        // members here".
15586        return None;
15587    }
15588    let bytes = source.as_bytes();
15589    let prefix = bytes.get(..start)?;
15590    let interior = bytes.get(start..end)?;
15591    let mut padded = Vec::with_capacity(end);
15592    padded.extend(
15593        prefix
15594            .iter()
15595            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
15596    );
15597    padded.extend_from_slice(interior);
15598    let padded = String::from_utf8(padded).ok()?;
15599    let mut parser = Parser::new();
15600    parser
15601        .set_language(&tree_sitter_cpp::LANGUAGE.into())
15602        .ok()?;
15603    parser.parse(&padded, None)
15604}
15605
15606/// Every node of `tree` in preorder, by kind, span and position, which is what
15607/// two reparses of one region have to agree on for their callers to read the
15608/// same declarations out of either (#2788).
15609#[cfg(any(debug_assertions, test))]
15610fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
15611    let mut shape = Vec::new();
15612    let mut cursor = tree.root_node().walk();
15613    let mut stack = vec![tree.root_node()];
15614    while let Some(node) = stack.pop() {
15615        shape.push((
15616            node.kind(),
15617            node.start_byte(),
15618            node.end_byte(),
15619            node.start_position().row,
15620            node.start_position().column,
15621            node.is_named(),
15622            node.is_missing(),
15623        ));
15624        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
15625        stack.extend(children.into_iter().rev());
15626    }
15627    shape
15628}
15629
15630/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
15631/// reparsed interior is indexed only when every top-level named node is a
15632/// well-formed C++ item (or a comment) and at least one real item is present.
15633/// Expression/statement soup surfaces as a top-level `ERROR` or
15634/// `expression_statement`, neither of which is an item kind, so it is rejected.
15635///
15636/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
15637/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
15638/// swallowed by a preceding dangling sentinel) reparses to a real
15639/// `namespace_definition` whose body still holds a bogus `function_definition`,
15640/// so the subtree legitimately carries an error. Container items are admitted
15641/// even with an internal error; the inner bogus function is recovered recursively
15642/// when `visit_function_definition` walks it. Each recursion strips at least one
15643/// leading sentinel, so the region strictly shrinks and recovery terminates.
15644///
15645/// A top-level `function_definition` is the one place we stay strict: it is
15646/// admitted only when it is clean or is itself a sentinel candidate. A function
15647/// that has an error and is not a sentinel is a real callable with a broken body,
15648/// so we refuse the whole reparse and let the ordinary path handle it (preserving
15649/// its real return type rather than re-deriving an implicit one).
15650fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
15651    let mut cursor = root.walk();
15652    let mut saw_item = false;
15653    for child in root.named_children(&mut cursor) {
15654        match child.kind() {
15655            "comment" => {}
15656            "function_definition" => {
15657                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
15658                    return false;
15659                }
15660                saw_item = true;
15661            }
15662            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
15663            _ => return false,
15664        }
15665    }
15666    saw_item
15667}
15668
15669/// Robustness gate for a reparsed fragmented multiple-base export class body
15670/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
15671/// kinds a class body produces when reparsed at translation-unit scope: the
15672/// access-specifier label preceding the first member surfaces as a
15673/// `labeled_statement` wrapping that member, and members surface as
15674/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
15675/// Statement or expression soup surfaces as other top-level kinds and is rejected,
15676/// so only a genuinely member-shaped body is ever re-owned as members; anything
15677/// ambiguous falls back to indexing the class alone.
15678fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
15679    if node.kind() != "ERROR" {
15680        return false;
15681    }
15682    let mut stack = Vec::new();
15683    let mut saw_function_declarator = false;
15684    let mut cursor = node.walk();
15685    for child in node.named_children(&mut cursor) {
15686        stack.push(child);
15687    }
15688    while let Some(current) = stack.pop() {
15689        match current.kind() {
15690            // Tree-sitter may wrap adjacent copy-control declarations in a
15691            // nested ERROR. Keep descending only through ERROR wrappers; the
15692            // actual declaration payload must be a function_declarator.
15693            "ERROR" => {
15694                let mut cursor = current.walk();
15695                stack.extend(current.named_children(&mut cursor));
15696            }
15697            "function_declarator" => saw_function_declarator = true,
15698            _ => return false,
15699        }
15700    }
15701    saw_function_declarator
15702}
15703
15704fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
15705    if node.kind() != "ERROR" {
15706        return false;
15707    }
15708    let mut cursor = node.walk();
15709    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15710    let [explicit, constructor_error, destructor] = named.as_slice() else {
15711        return false;
15712    };
15713    let Some(constructor) = constructor_error.named_child(0) else {
15714        return false;
15715    };
15716    let Some(constructor_name) =
15717        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
15718    else {
15719        return false;
15720    };
15721    let Some(destructor_name) =
15722        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
15723    else {
15724        return false;
15725    };
15726    let Some(destroyed_type) = destructor_name.named_child(0) else {
15727        return false;
15728    };
15729    explicit.kind() == "explicit_function_specifier"
15730        && constructor_error.kind() == "ERROR"
15731        && constructor_error.named_child_count() == 1
15732        && constructor.kind() == "function_declarator"
15733        && constructor_name.kind() == "identifier"
15734        && destructor.kind() == "function_declarator"
15735        && destructor_name.kind() == "destructor_name"
15736        && destroyed_type.kind() == "identifier"
15737        && node_text(constructor_name, source) == node_text(destroyed_type, source)
15738}
15739
15740fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
15741    if node.kind() != "compound_statement" {
15742        return false;
15743    }
15744    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
15745        return false;
15746    };
15747    if prefix.kind() == "labeled_statement"
15748        && prefix.named_child(0).is_some_and(|label| {
15749            matches!(
15750                node_text(label, source).trim(),
15751                "public" | "private" | "protected"
15752            )
15753        })
15754    {
15755        return prefix.named_children(&mut prefix.walk()).any(|child| {
15756            child.kind() == "declaration"
15757                && child.has_error()
15758                && child
15759                    .named_children(&mut child.walk())
15760                    .any(cpp_reparsed_member_error_is_indexable)
15761        });
15762    }
15763    // A malformed constructor initializer can be split into a declaration
15764    // followed by its compound body when the class prefix already contains
15765    // realistic members. Keep this admission tied to that exact structured
15766    // declaration/error/body chain rather than accepting arbitrary blocks.
15767    prefix.kind() == "declaration"
15768        && prefix.has_error()
15769        && prefix
15770            .named_children(&mut prefix.walk())
15771            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
15772}
15773
15774fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
15775    if !cpp_reparsed_member_error_is_indexable(node) {
15776        return false;
15777    }
15778    let Some(preproc) = node.next_named_sibling() else {
15779        return false;
15780    };
15781    preproc.kind() == "preproc_if"
15782        && preproc.has_error()
15783        && preproc
15784            .named_children(&mut preproc.walk())
15785            .any(|child| child.kind() == "expression_statement" && child.has_error())
15786        && preproc
15787            .next_named_sibling()
15788            .is_some_and(|body| body.kind() == "compound_statement")
15789}
15790
15791/// Return a function body whose braces and ownership are explicit in the
15792/// reparsed class-member tree. An error below a real function envelope is
15793/// recoverable by the ordinary function visitor; a missing/deferred body is
15794/// not, because accepting it would let statement soup masquerade as a member.
15795fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
15796    if node.kind() != "function_definition" {
15797        return None;
15798    }
15799    let body = node.child_by_field_name("body")?;
15800    if body.kind() != "compound_statement" {
15801        return None;
15802    }
15803    let open = body.child(0)?;
15804    let close = body.child(body.child_count().checked_sub(1)?)?;
15805    if open.kind() != "{"
15806        || open.is_missing()
15807        || close.kind() != "}"
15808        || close.is_missing()
15809        || close.end_byte() != body.end_byte()
15810        || body.end_byte() != node.end_byte()
15811    {
15812        return None;
15813    }
15814    Some(body)
15815}
15816
15817fn cpp_reparsed_member_function_errors_are_in_body(
15818    node: Node<'_>,
15819    body: Node<'_>,
15820    source: &str,
15821) -> bool {
15822    let mut cursor = node.walk();
15823    node.children(&mut cursor).all(|child| {
15824        same_node(child, body)
15825            || cpp_reparsed_member_attribute_error(child, source)
15826            || cpp_reparsed_member_signature_identifier_errors(child)
15827            || (!child.has_error() && !child.is_error() && !child.is_missing())
15828    })
15829}
15830
15831/// A complete callable can still carry parser errors in its signature when a
15832/// project annotation is not part of the C++ grammar (`nonneg int`,
15833/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
15834/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
15835/// leaves inside the already-proven callable envelope; structured statements,
15836/// literals, missing tokens, and other malformed signature payload remain
15837/// rejected.
15838fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
15839    if !node.has_error() && !node.is_error() && !node.is_missing() {
15840        return false;
15841    }
15842    let mut stack = vec![node];
15843    let mut saw_error = false;
15844    while let Some(current) = stack.pop() {
15845        if current.is_missing() {
15846            return false;
15847        }
15848        if current.kind() == "ERROR" {
15849            saw_error = true;
15850            let mut cursor = current.walk();
15851            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
15852            if children
15853                .iter()
15854                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
15855            {
15856                return false;
15857            }
15858            stack.extend(children);
15859            continue;
15860        }
15861        let mut cursor = current.walk();
15862        stack.extend(current.children(&mut cursor));
15863    }
15864    saw_error
15865}
15866
15867fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
15868    node.kind() == "ERROR"
15869        && node.named_child_count() == 1
15870        && node.named_child(0).is_some_and(|attribute| {
15871            attribute.kind() == "identifier"
15872                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
15873        })
15874}
15875
15876/// A C++ attribute placed between a member's declarator and body can make
15877/// tree-sitter expose the callable as
15878/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
15879/// Keep this admission tied to that exact node geometry. In particular, an
15880/// arbitrary ERROR or identifier before a compound statement is not enough.
15881fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
15882    let Some(body) = cpp_reparsed_member_function_body(node) else {
15883        return false;
15884    };
15885    let mut cursor = node.walk();
15886    let named = node
15887        .named_children(&mut cursor)
15888        .filter(|child| child.kind() != "comment")
15889        .collect::<Vec<_>>();
15890    let [type_node, error, attribute, body_node] = named.as_slice() else {
15891        return false;
15892    };
15893    if !same_node(*body_node, body)
15894        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
15895        || attribute.kind() != "identifier"
15896        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
15897        || error.kind() != "ERROR"
15898        || error.named_child_count() != 1
15899    {
15900        return false;
15901    }
15902    error
15903        .named_child(0)
15904        .is_some_and(cpp_reparsed_attribute_callable_declarator)
15905}
15906
15907fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
15908    cpp_structured_type_path(node, source).is_some()
15909        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
15910}
15911
15912fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
15913    let Some(body) = cpp_reparsed_member_function_body(node) else {
15914        return false;
15915    };
15916    let mut cursor = node.walk();
15917    let named = node
15918        .named_children(&mut cursor)
15919        .filter(|child| child.kind() != "comment")
15920        .collect::<Vec<_>>();
15921    let [friend, return_error, declarator, body_node] = named.as_slice() else {
15922        return false;
15923    };
15924    let Some(return_type) = return_error.named_child(0) else {
15925        return false;
15926    };
15927    same_node(*body_node, body)
15928        && friend.kind() == "type_identifier"
15929        && node_text(*friend, source) == "friend"
15930        && return_error.kind() == "ERROR"
15931        && return_error.named_child_count() == 1
15932        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
15933        && extract_function_declarator(*declarator)
15934            .and_then(cpp_function_declarator_name_node)
15935            .is_some()
15936}
15937
15938fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
15939    let Some(body) = cpp_reparsed_member_function_body(node) else {
15940        return false;
15941    };
15942    let mut cursor = node.walk();
15943    let named = node
15944        .named_children(&mut cursor)
15945        .filter(|child| child.kind() != "comment")
15946        .collect::<Vec<_>>();
15947    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
15948        return false;
15949    };
15950    let Some(return_type) = return_error.named_child(0) else {
15951        return false;
15952    };
15953    same_node(*body_node, body)
15954        && prefix
15955            .iter()
15956            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
15957        && attribute.kind() == "type_identifier"
15958        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
15959        && return_error.kind() == "ERROR"
15960        && return_error.named_child_count() == 1
15961        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
15962        && extract_function_declarator(*declarator)
15963            .and_then(cpp_function_declarator_name_node)
15964            .is_some()
15965}
15966
15967/// An included-range reparse that begins inside a malformed class can merge an
15968/// access label and following template member. Tree-sitter then emits the label
15969/// as the `template_type` name, the template parameter list as its arguments,
15970/// an ERROR-wrapped return type, the callable declarator, and its complete
15971/// body. Admit only that exact structured displacement.
15972fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
15973    let Some(body) = cpp_reparsed_member_function_body(node) else {
15974        return false;
15975    };
15976    let mut cursor = node.walk();
15977    let named = node
15978        .named_children(&mut cursor)
15979        .filter(|child| child.kind() != "comment")
15980        .collect::<Vec<_>>();
15981    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
15982        return false;
15983    };
15984    let Some(template_name) = template_type.child_by_field_name("name") else {
15985        return false;
15986    };
15987    let Some(arguments) = template_type.child_by_field_name("arguments") else {
15988        return false;
15989    };
15990    let Some(return_type) = return_error.named_child(0) else {
15991        return false;
15992    };
15993    let mut cursor = template_type.walk();
15994    let template_errors = template_type
15995        .named_children(&mut cursor)
15996        .filter(|child| child.kind() == "ERROR")
15997        .collect::<Vec<_>>();
15998    let [comment_error] = template_errors.as_slice() else {
15999        return false;
16000    };
16001    let mut cursor = comment_error.walk();
16002    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
16003    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
16004        return false;
16005    };
16006    same_node(*body_node, body)
16007        && template_type.kind() == "template_type"
16008        && template_name.kind() == "type_identifier"
16009        && matches!(
16010            node_text(template_name, source).trim(),
16011            "public" | "private" | "protected"
16012        )
16013        && arguments.kind() == "template_argument_list"
16014        && arguments.named_child_count() > 0
16015        && !arguments.has_error()
16016        && !colon.is_named()
16017        && colon.kind() == ":"
16018        && comments.iter().all(|child| child.kind() == "comment")
16019        && !template_keyword.is_named()
16020        && template_keyword.kind() == "template"
16021        && return_error.kind() == "ERROR"
16022        && return_error.named_child_count() == 1
16023        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16024        && extract_function_declarator(*declarator)
16025            .and_then(cpp_function_declarator_name_node)
16026            .is_some()
16027}
16028
16029/// Return the constructor declaration tree-sitter can merge into an access
16030/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
16031/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
16032/// declaration's apparent type; the callable name must still exactly match the
16033/// recovered class, so unrelated labeled statements are never re-owned.
16034fn cpp_reparsed_preprocessor_constructor<'tree>(
16035    node: Node<'tree>,
16036    class_name: &str,
16037    source: &str,
16038) -> Option<Node<'tree>> {
16039    if node.kind() != "labeled_statement" {
16040        return None;
16041    }
16042    let mut cursor = node.walk();
16043    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16044    let [label, directive_error, declaration] = named.as_slice() else {
16045        return None;
16046    };
16047    if label.kind() != "statement_identifier"
16048        || !matches!(
16049            node_text(*label, source),
16050            "public" | "private" | "protected"
16051        )
16052        || directive_error.kind() != "ERROR"
16053        || directive_error.child_count() != 1
16054        || directive_error
16055            .child(0)
16056            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
16057        || declaration.kind() != "declaration"
16058        || declaration.named_child_count() != 2
16059    {
16060        return None;
16061    }
16062    let apparent_type = declaration.child_by_field_name("type")?;
16063    if apparent_type.kind() != "type_identifier"
16064        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
16065    {
16066        return None;
16067    }
16068    let declarator = declaration.child_by_field_name("declarator")?;
16069    let function = extract_function_declarator(declarator)?;
16070    let name = cpp_function_declarator_name_node(function)?;
16071    (node_text(name, source) == class_name).then_some(*declaration)
16072}
16073
16074fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
16075    if extract_function_declarator(node)
16076        .and_then(cpp_function_declarator_name_node)
16077        .is_some()
16078    {
16079        return true;
16080    }
16081    node.kind() == "init_declarator"
16082        && node
16083            .child_by_field_name("declarator")
16084            .is_some_and(|declarator| declarator.kind() == "identifier")
16085        && node
16086            .child_by_field_name("value")
16087            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
16088}
16089
16090/// Return true for the constrained/attribute form that tree-sitter splits into
16091/// an ERROR declaration, a preprocessor `requires` clause, and a following
16092/// compound statement. The three nodes must remain immediate named siblings;
16093/// this deliberately does not search source text or skip unrelated statements.
16094fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
16095    if node.kind() != "ERROR" || node.named_child_count() != 3 {
16096        return false;
16097    }
16098    let mut cursor = node.walk();
16099    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16100    let [type_node, function_declarator, attribute] = named.as_slice() else {
16101        return false;
16102    };
16103    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16104        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
16105        || attribute.kind() != "identifier"
16106        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16107    {
16108        return false;
16109    }
16110    let Some(preproc) =
16111        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16112    else {
16113        return false;
16114    };
16115    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
16116        .filter(|sibling| sibling.kind() == "compound_statement")
16117    else {
16118        return false;
16119    };
16120    let Some(open) = body.child(0) else {
16121        return false;
16122    };
16123    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
16124        return false;
16125    };
16126    let Some(condition) = preproc.child_by_field_name("condition") else {
16127        return false;
16128    };
16129    let mut cursor = preproc.walk();
16130    let payload = preproc
16131        .named_children(&mut cursor)
16132        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
16133        .collect::<Vec<_>>();
16134    let [requires_statement] = payload.as_slice() else {
16135        return false;
16136    };
16137    let requires_clause = requires_statement.named_child(0);
16138
16139    open.kind() == "{"
16140        && !open.is_missing()
16141        && close.kind() == "}"
16142        && !close.is_missing()
16143        && close.end_byte() == body.end_byte()
16144        && requires_statement.kind() == "expression_statement"
16145        && requires_statement.named_child_count() == 1
16146        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
16147}
16148
16149fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16150    let mut sibling = node.next_named_sibling();
16151    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16152        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
16153    }
16154    sibling
16155}
16156
16157fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16158    let mut sibling = node.prev_named_sibling();
16159    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16160        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
16161    }
16162    sibling
16163}
16164
16165fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
16166    let Some(preproc) =
16167        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16168    else {
16169        return false;
16170    };
16171    let Some(error) =
16172        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
16173    else {
16174        return false;
16175    };
16176    cpp_reparsed_attribute_requires_error(error, source)
16177}
16178
16179fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
16180    node: Node<'tree>,
16181    source: &str,
16182) -> Option<Node<'tree>> {
16183    if node.kind() != "ERROR" {
16184        return None;
16185    }
16186    let mut cursor = node.walk();
16187    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16188    let [parameter, macro_name, message] = named.as_slice() else {
16189        return None;
16190    };
16191    let parameter_name = parameter.named_child(0)?;
16192    (parameter.kind() == "type_parameter_declaration"
16193        && parameter_name.kind() == "type_identifier"
16194        && macro_name.kind() == "type_identifier"
16195        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16196        && message.kind() == "string_literal")
16197        .then_some(parameter_name)
16198}
16199
16200/// Recognize the alternate constraint-macro prefix where tree-sitter retains
16201/// the complete qualified constraint as a fourth child instead of moving it
16202/// into the following function. Keep the gate tied to a two-type template
16203/// constraint that names the declared type parameter.
16204fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
16205    node: Node<'tree>,
16206    source: &str,
16207) -> Option<Node<'tree>> {
16208    if node.kind() != "ERROR" {
16209        return None;
16210    }
16211    let mut cursor = node.walk();
16212    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16213    let [parameter, macro_name, message, constraint] = named.as_slice() else {
16214        return None;
16215    };
16216    let parameter_name = parameter.named_child(0)?;
16217    let constraint_scope = constraint.child_by_field_name("scope")?;
16218    let constraint_template = constraint.child_by_field_name("name")?;
16219    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16220    let mut argument_cursor = constraint_arguments.walk();
16221    let constraint_types = constraint_arguments
16222        .named_children(&mut argument_cursor)
16223        .collect::<Vec<_>>();
16224    if parameter.kind() != "type_parameter_declaration"
16225        || parameter_name.kind() != "type_identifier"
16226        || macro_name.kind() != "type_identifier"
16227        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16228        || message.kind() != "string_literal"
16229        || constraint.kind() != "qualified_identifier"
16230        || constraint_scope.kind() != "namespace_identifier"
16231        || !matches!(
16232            constraint_template.kind(),
16233            "template_function" | "template_type"
16234        )
16235        || !matches!(constraint_types.as_slice(), [left, right]
16236            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16237        || constraint_arguments.has_error()
16238    {
16239        return None;
16240    }
16241    let parameter_text = node_text(parameter_name, source);
16242    let mut stack = constraint_types;
16243    while let Some(current) = stack.pop() {
16244        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
16245            return Some(parameter_name);
16246        }
16247        let mut cursor = current.walk();
16248        stack.extend(current.named_children(&mut cursor));
16249    }
16250    None
16251}
16252
16253fn cpp_reparsed_template_macro_companion_is_indexable(
16254    node: Node<'_>,
16255    parameter_name: Node<'_>,
16256    source: &str,
16257) -> bool {
16258    let Some(body) = cpp_reparsed_member_function_body(node) else {
16259        return false;
16260    };
16261    let mut cursor = node.walk();
16262    let named = node
16263        .named_children(&mut cursor)
16264        .filter(|child| child.kind() != "comment")
16265        .collect::<Vec<_>>();
16266    let [
16267        constraint,
16268        close_error,
16269        storage,
16270        return_error,
16271        declarator,
16272        body_node,
16273    ] = named.as_slice()
16274    else {
16275        return false;
16276    };
16277    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
16278        return false;
16279    };
16280    let Some(constraint_template) = constraint.child_by_field_name("name") else {
16281        return false;
16282    };
16283    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
16284        return false;
16285    };
16286    let Some(return_type) = return_error.named_child(0) else {
16287        return false;
16288    };
16289    let mut cursor = constraint_arguments.walk();
16290    let constraint_types = constraint_arguments
16291        .named_children(&mut cursor)
16292        .collect::<Vec<_>>();
16293    same_node(*body_node, body)
16294        && constraint.kind() == "qualified_identifier"
16295        && constraint_scope.kind() == "namespace_identifier"
16296        && constraint_template.kind() == "template_type"
16297        && matches!(constraint_types.as_slice(), [left, right]
16298            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16299        && !constraint_arguments.has_error()
16300        && close_error.kind() == "ERROR"
16301        && close_error.named_child_count() == 0
16302        && storage.kind() == "storage_class_specifier"
16303        && return_error.kind() == "ERROR"
16304        && return_error.named_child_count() == 1
16305        && return_type.kind() == "identifier"
16306        && node_text(return_type, source) == node_text(parameter_name, source)
16307        && extract_function_declarator(*declarator)
16308            .and_then(cpp_function_declarator_name_node)
16309            .is_some()
16310}
16311
16312fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
16313    node: Node<'tree>,
16314    parameter_name: Node<'_>,
16315    source: &str,
16316) -> Option<Node<'tree>> {
16317    let body = cpp_reparsed_member_function_body(node)?;
16318    let constraint = node.child_by_field_name("type")?;
16319    let constraint_template = constraint.child_by_field_name("name")?;
16320    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16321    let mut argument_cursor = constraint_arguments.walk();
16322    let constraint_types = constraint_arguments
16323        .named_children(&mut argument_cursor)
16324        .collect::<Vec<_>>();
16325    if constraint.kind() != "qualified_identifier"
16326        || constraint_template.kind() != "template_type"
16327        || !matches!(constraint_types.as_slice(), [left, right]
16328            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16329        || constraint_arguments.has_error()
16330        || node
16331            .child_by_field_name("body")
16332            .is_none_or(|candidate| !same_node(candidate, body))
16333    {
16334        return None;
16335    }
16336
16337    let mut cursor = node.walk();
16338    let recovery_errors = node
16339        .named_children(&mut cursor)
16340        .filter(|child| child.kind() == "ERROR")
16341        .collect::<Vec<_>>();
16342    if !recovery_errors
16343        .iter()
16344        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
16345        || !recovery_errors.iter().all(|error| {
16346            error.named_child_count() == 0
16347                || cpp_reparsed_constraint_macro_error(*error, source)
16348                || (error.named_child_count() == 1
16349                    && error
16350                        .named_child(0)
16351                        .is_some_and(|child| child.kind() == "function_declarator"))
16352        })
16353    {
16354        return None;
16355    }
16356
16357    let parameter_text = node_text(parameter_name, source);
16358    let mut declarators = node
16359        .child_by_field_name("declarator")
16360        .and_then(extract_function_declarator)
16361        .into_iter()
16362        .collect::<Vec<_>>();
16363    for error in recovery_errors {
16364        let mut stack = vec![error];
16365        while let Some(current) = stack.pop() {
16366            if current.kind() == "function_declarator" {
16367                declarators.push(current);
16368            }
16369            let mut cursor = current.walk();
16370            stack.extend(current.named_children(&mut cursor));
16371        }
16372    }
16373    declarators.into_iter().find(|declarator| {
16374        cpp_function_declarator_name_node(*declarator)
16375            .is_some_and(|name| name.kind() == "identifier")
16376            && declarator
16377                .child_by_field_name("parameters")
16378                .is_some_and(|parameters| {
16379                    parameters
16380                        .named_children(&mut parameters.walk())
16381                        .filter_map(|parameter| parameter.child_by_field_name("type"))
16382                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
16383                })
16384    })
16385}
16386
16387fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
16388    node: Node<'_>,
16389    parameter_name: Node<'_>,
16390    source: &str,
16391) -> bool {
16392    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
16393}
16394
16395fn cpp_reparsed_template_macro_function_companion_is_indexable(
16396    node: Node<'_>,
16397    parameter_name: Node<'_>,
16398    source: &str,
16399) -> bool {
16400    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
16401        return false;
16402    }
16403    let Some(return_type) = node.child_by_field_name("type") else {
16404        return false;
16405    };
16406    let Some(function_declarator) = node
16407        .child_by_field_name("declarator")
16408        .and_then(extract_function_declarator)
16409    else {
16410        return false;
16411    };
16412    if cpp_function_declarator_name_node(function_declarator).is_none()
16413        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
16414    {
16415        return false;
16416    }
16417    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
16418        return false;
16419    };
16420    let parameter_text = node_text(parameter_name, source);
16421    parameters
16422        .named_children(&mut parameters.walk())
16423        .any(|parameter| {
16424            parameter
16425                .child_by_field_name("type")
16426                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
16427        })
16428}
16429
16430fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
16431    if node.kind() != "ERROR" {
16432        return false;
16433    }
16434    let mut stack = vec![node];
16435    while let Some(current) = stack.pop() {
16436        let macro_shape = match current.kind() {
16437            "call_expression" => current
16438                .child_by_field_name("function")
16439                .zip(current.child_by_field_name("arguments")),
16440            "init_declarator" => current
16441                .child_by_field_name("declarator")
16442                .zip(current.child_by_field_name("value")),
16443            _ => None,
16444        };
16445        if let Some((name, arguments)) = macro_shape
16446            && name.kind() == "identifier"
16447            && arguments.kind() == "argument_list"
16448            && arguments.named_child_count() >= 2
16449            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
16450        {
16451            return true;
16452        }
16453        let mut cursor = current.walk();
16454        stack.extend(current.named_children(&mut cursor));
16455    }
16456    false
16457}
16458
16459fn cpp_recovered_template_macro_constructor<'tree>(
16460    node: Node<'tree>,
16461    source: &str,
16462) -> Option<(Node<'tree>, Node<'tree>)> {
16463    let mut prefix = node.prev_named_sibling()?;
16464    while prefix.kind() == "comment" {
16465        prefix = prefix.prev_named_sibling()?;
16466    }
16467    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
16468    let parameter = parameter_name
16469        .parent()
16470        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
16471    let declarator =
16472        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
16473    Some((declarator, parameter))
16474}
16475
16476fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
16477    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
16478        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16479            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
16480                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
16481                    function,
16482                    parameter_name,
16483                    source,
16484                )
16485        });
16486    }
16487    let Some(parameter_name) =
16488        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
16489    else {
16490        return false;
16491    };
16492    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16493        cpp_reparsed_template_macro_function_companion_is_indexable(
16494            function,
16495            parameter_name,
16496            source,
16497        )
16498    })
16499}
16500
16501fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16502    let function_name = node
16503        .child_by_field_name("declarator")
16504        .and_then(extract_function_declarator)
16505        .and_then(cpp_function_declarator_name_node);
16506    if let Some(body) = cpp_reparsed_member_function_body(node)
16507        && function_name.is_some()
16508        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
16509    {
16510        return true;
16511    }
16512    cpp_reparsed_attribute_member_function(node, source)
16513        || cpp_reparsed_friend_function_is_indexable(node, source)
16514        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
16515        || cpp_reparsed_access_template_function_is_indexable(node, source)
16516        || cpp_recovered_template_macro_constructor(node, source).is_some()
16517}
16518
16519/// Recognize the three top-level nodes produced when an unknown attribute
16520/// macro separates an inline member's declarator from its body in a reparsed
16521/// class interior: an errorful declaration with a missing semicolon, the macro
16522/// call expression, and the complete compound body. Their adjacency and exact
16523/// structured shapes prove one recoverable member envelope; arbitrary calls or
16524/// blocks do not pass this gate.
16525fn cpp_reparsed_macro_attribute_member_sequence(
16526    children: &[Node<'_>],
16527    index: usize,
16528    source: &str,
16529) -> bool {
16530    let Some(prefix) = children.get(index).copied() else {
16531        return false;
16532    };
16533    let declaration = if prefix.kind() == "labeled_statement" {
16534        prefix
16535            .named_child(prefix.named_child_count().saturating_sub(1))
16536            .filter(|child| child.kind() == "declaration")
16537    } else {
16538        (prefix.kind() == "declaration").then_some(prefix)
16539    };
16540    let Some(declaration) = declaration else {
16541        return false;
16542    };
16543    if !declaration.has_error()
16544        || declaration
16545            .child_by_field_name("declarator")
16546            .and_then(extract_function_declarator)
16547            .and_then(cpp_function_declarator_name_node)
16548            .is_none()
16549    {
16550        return false;
16551    }
16552    let Some(attribute_statement) = children.get(index + 1).copied() else {
16553        return false;
16554    };
16555    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
16556        .then(|| attribute_statement.named_child(0))
16557        .flatten()
16558        .filter(|child| child.kind() == "call_expression")
16559    else {
16560        return false;
16561    };
16562    let Some(attribute_name) = attribute_call
16563        .child_by_field_name("function")
16564        .filter(|function| function.kind() == "identifier")
16565        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
16566    else {
16567        return false;
16568    };
16569    if !cpp_export_macro_token(&attribute_name) {
16570        return false;
16571    }
16572    let Some(body) = children.get(index + 2).copied() else {
16573        return false;
16574    };
16575    body.kind() == "compound_statement"
16576        && body.child(0).is_some_and(|open| open.kind() == "{")
16577        && body
16578            .child(body.child_count().saturating_sub(1))
16579            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
16580        && declaration.end_byte() <= attribute_statement.start_byte()
16581        && attribute_statement.end_byte() <= body.start_byte()
16582}
16583
16584/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
16585/// types and specifiers followed by a declarator, over and over, with nothing
16586/// left over. A string-argument attribute macro is what strands them there, and
16587/// the walk indexes exactly what this reads, so admitting the region is safe
16588/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
16589/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
16590/// the whole class body.
16591fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
16592    if node.kind() != "ERROR" {
16593        return false;
16594    }
16595    let run = stranded_declaration_run(node, source);
16596    run.complete && !run.declarations.is_empty()
16597}
16598
16599fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
16600    let mut cursor = root.walk();
16601    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
16602    let mut saw_member = false;
16603    let mut index = 0;
16604    while index < children.len() {
16605        let child = children[index];
16606        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
16607            saw_member = true;
16608            index += 3;
16609            continue;
16610        }
16611        if let Some(recovered) = fragmented_class_body(child, source) {
16612            let Some(tree) = cpp_reparse_fragmented_class_body(
16613                source,
16614                recovered.body.reparse_start,
16615                recovered.body.reparse_end,
16616            ) else {
16617                return false;
16618            };
16619            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
16620                return false;
16621            }
16622            saw_member = true;
16623            index += 1;
16624            while index < children.len()
16625                && children[index].end_byte() <= recovered.body.class_range.end_byte
16626            {
16627                index += 1;
16628            }
16629            continue;
16630        }
16631        match child.kind() {
16632            "comment" => {}
16633            "labeled_statement" => saw_member = true,
16634            "function_definition" => {
16635                if child.has_error()
16636                    && !cpp_reparsed_member_function_is_indexable(child, source)
16637                    && cpp_sentinel_macro_region(child, source).is_none()
16638                {
16639                    return false;
16640                }
16641                saw_member = true;
16642            }
16643            // The attribute a string-argument macro leaves as a call statement
16644            // of its own. It declares nothing; the member it decorated is the
16645            // sibling after it, checked on its own turn.
16646            "expression_statement" if is_string_attribute_macro_statement(child) => {}
16647            "ERROR"
16648                if (cpp_reparsed_member_error_is_indexable(child)
16649                    || cpp_reparsed_adjacent_copy_control_error(child, source)
16650                    || cpp_reparsed_stranded_member_error(child, source))
16651                    && (child
16652                        .next_named_sibling()
16653                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
16654                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
16655            {
16656                saw_member = true;
16657            }
16658            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
16659                saw_member = true;
16660            }
16661            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
16662                saw_member = true;
16663            }
16664            "expression_statement"
16665                if cpp_is_stray_semicolon(child, source)
16666                    && child.prev_named_sibling().is_some_and(|error| {
16667                        cpp_reparsed_member_error_is_indexable(error)
16668                            || cpp_reparsed_adjacent_copy_control_error(error, source)
16669                            || cpp_reparsed_stranded_member_error(error, source)
16670                    }) =>
16671            {
16672                saw_member = true;
16673            }
16674            "compound_statement"
16675                if cpp_reparsed_constructor_body_is_indexable(child, source)
16676                    || cpp_reparsed_attribute_requires_body(child, source) =>
16677            {
16678                saw_member = true;
16679            }
16680            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
16681            _ => return false,
16682        }
16683        index += 1;
16684    }
16685    saw_member
16686}
16687
16688/// Detect the malformed constructor shape that tree-sitter exposes as an
16689/// access-label statement followed by initializer-looking declarations. The
16690/// declarations are not class members: visiting their `location(loc)` and
16691/// `string(s)` function declarators would publish synthetic functions. The
16692/// export-class fallback keeps the original sibling nodes and therefore avoids
16693/// this parser artifact. The returned range identifies the real constructor
16694/// header, which can be reparsed independently as a structured declarator.
16695fn cpp_reparsed_synthetic_initializer_constructor_range(
16696    root: Node<'_>,
16697    class_name: &str,
16698    source: &str,
16699    constructor_end: usize,
16700) -> Option<std::ops::Range<usize>> {
16701    let mut stack = {
16702        let mut cursor = root.walk();
16703        root.named_children(&mut cursor).collect::<Vec<_>>()
16704    };
16705    while let Some(current) = stack.pop() {
16706        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
16707            current,
16708            class_name,
16709            source,
16710            constructor_end,
16711        ) {
16712            return Some(range);
16713        }
16714        if current.kind() == "ERROR" {
16715            let mut cursor = current.walk();
16716            stack.extend(current.named_children(&mut cursor));
16717        }
16718    }
16719    None
16720}
16721
16722/// Recover an inline constructor that a function-like export macro makes
16723/// tree-sitter merge with the following overload. In the reparsed class-body
16724/// region, the access label wraps one declaration whose ERROR contains the
16725/// constructor declarator and its base-initializer/body, while the declaration's
16726/// ordinary declarator is the following overload. Every boundary below comes
16727/// from that CST; no source syntax is reparsed by hand.
16728fn cpp_reparsed_merged_inline_constructor<'tree>(
16729    root: Node<'tree>,
16730    class_name: &str,
16731    source: &str,
16732) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
16733    let mut stack = vec![root];
16734    while let Some(current) = stack.pop() {
16735        if current.kind() != "labeled_statement" {
16736            let mut cursor = current.walk();
16737            stack.extend(current.named_children(&mut cursor));
16738            continue;
16739        }
16740        let declaration = current
16741            .named_children(&mut current.walk())
16742            .find(|child| child.kind() == "declaration")?;
16743        if declaration
16744            .child_by_field_name("type")
16745            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
16746        {
16747            continue;
16748        }
16749        let following = declaration
16750            .child_by_field_name("declarator")
16751            .and_then(extract_function_declarator)
16752            .and_then(cpp_function_declarator_name_node);
16753        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
16754            continue;
16755        }
16756        let mut declaration_cursor = declaration.walk();
16757        let Some(error) = declaration
16758            .named_children(&mut declaration_cursor)
16759            .find(|child| child.kind() == "ERROR")
16760        else {
16761            continue;
16762        };
16763        let mut error_cursor = error.walk();
16764        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
16765        let Some(constructor) = error_children.iter().copied().find(|child| {
16766            child.kind() == "function_declarator"
16767                && cpp_function_declarator_name_node(*child)
16768                    .is_some_and(|name| node_text(name, source).trim() == class_name)
16769        }) else {
16770            continue;
16771        };
16772        let Some(body) = error_children.iter().copied().find_map(|child| {
16773            (child.kind() == "init_declarator")
16774                .then(|| child.child_by_field_name("value"))
16775                .flatten()
16776                .filter(|value| value.kind() == "initializer_list")
16777        }) else {
16778            continue;
16779        };
16780        if constructor.end_byte() > body.start_byte() {
16781            continue;
16782        }
16783        return Some((constructor.start_byte()..body.end_byte(), body));
16784    }
16785    None
16786}
16787
16788fn cpp_reparsed_synthetic_initializer_constructor(
16789    node: Node<'_>,
16790    class_name: &str,
16791    source: &str,
16792    constructor_end: usize,
16793) -> Option<std::ops::Range<usize>> {
16794    if node.kind() != "labeled_statement" {
16795        return None;
16796    }
16797    let mut cursor = node.walk();
16798    let named = node
16799        .named_children(&mut cursor)
16800        .filter(|child| child.kind() != "comment")
16801        .collect::<Vec<_>>();
16802    let label = named.first()?;
16803    if label.kind() != "statement_identifier"
16804        || !matches!(
16805            node_text(*label, source).trim(),
16806            "public" | "private" | "protected"
16807        )
16808    {
16809        return None;
16810    }
16811    let call_error_index = named.iter().position(|child| {
16812        if child.kind() != "ERROR" {
16813            return false;
16814        }
16815        let mut stack = vec![*child];
16816        while let Some(current) = stack.pop() {
16817            if current.kind() == "call_expression"
16818                && current
16819                    .child_by_field_name("function")
16820                    .is_some_and(|function| {
16821                        function.kind() == "identifier"
16822                            && node_text(function, source).trim() == class_name
16823                    })
16824            {
16825                return true;
16826            }
16827            let mut cursor = current.walk();
16828            stack.extend(current.named_children(&mut cursor));
16829        }
16830        false
16831    })?;
16832    let constructor_call = {
16833        let mut stack = vec![named[call_error_index]];
16834        let mut found = None;
16835        while let Some(current) = stack.pop() {
16836            if current.kind() == "call_expression"
16837                && current
16838                    .child_by_field_name("function")
16839                    .is_some_and(|function| {
16840                        function.kind() == "identifier"
16841                            && node_text(function, source).trim() == class_name
16842                    })
16843            {
16844                found = Some(current);
16845                break;
16846            }
16847            let mut cursor = current.walk();
16848            stack.extend(current.named_children(&mut cursor));
16849        }
16850        found
16851    };
16852    let constructor_call = constructor_call?;
16853    named.iter().skip(call_error_index + 1).find(|child| {
16854        child.kind() == "declaration" && child.has_error() && {
16855            let mut cursor = child.walk();
16856            child.named_children(&mut cursor).any(|declarator| {
16857                declarator.kind() == "init_declarator"
16858                    && declarator
16859                        .child_by_field_name("declarator")
16860                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
16861                    && declarator
16862                        .child_by_field_name("value")
16863                        .is_some_and(|value| value.kind() == "initializer_list")
16864            })
16865        }
16866    })?;
16867    Some(constructor_call.start_byte()..constructor_end)
16868}
16869
16870fn cpp_reparsed_exact_constructor_declarator<'tree>(
16871    root: Node<'tree>,
16872    start: usize,
16873    class_name: &str,
16874    source: &str,
16875) -> Option<Node<'tree>> {
16876    let mut candidate = None;
16877    let mut stack = vec![root];
16878    while let Some(current) = stack.pop() {
16879        if current.kind() == "function_declarator"
16880            && current.start_byte() == start
16881            && cpp_function_declarator_name_node(current)
16882                .is_some_and(|name| node_text(name, source).trim() == class_name)
16883        {
16884            if candidate.is_some() {
16885                return None;
16886            }
16887            candidate = Some(current);
16888            continue;
16889        }
16890        let mut cursor = current.walk();
16891        stack.extend(current.named_children(&mut cursor));
16892    }
16893    candidate
16894}
16895
16896fn cpp_is_indexable_item_kind(kind: &str) -> bool {
16897    matches!(
16898        kind,
16899        "namespace_definition"
16900            | "class_specifier"
16901            | "struct_specifier"
16902            | "union_specifier"
16903            | "enum_specifier"
16904            | "function_definition"
16905            | "template_declaration"
16906            | "declaration"
16907            | "field_declaration"
16908            | "alias_declaration"
16909            | "static_assert_declaration"
16910            | "type_definition"
16911            | "using_declaration"
16912            | "linkage_specification"
16913            | "preproc_def"
16914            | "preproc_function_def"
16915            | "preproc_include"
16916            | "preproc_if"
16917            | "preproc_ifdef"
16918            | "preproc_call"
16919    )
16920}
16921
16922#[cfg(test)]
16923mod tests {
16924    use super::*;
16925    use crate::adapter::parse_cpp_file;
16926    use brokk_bifrost_core::analyzer::parsed_file::{
16927        finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
16928        start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
16929    };
16930    use std::fmt::Write;
16931
16932    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
16933        let mut parser = tree_sitter::Parser::new();
16934        parser
16935            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16936            .unwrap();
16937        let tree = parser.parse(source, None).unwrap();
16938        let file = ProjectFile::new(std::env::temp_dir(), name);
16939        parse_cpp_file(&file, source, &tree)
16940    }
16941
16942    #[test]
16943    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
16944        let source = "struct Image { PyObject_HEAD Imaging image; };";
16945        let parsed = parse_cpp_declarations(source, "image.h");
16946        let names = parsed
16947            .declarations()
16948            .iter()
16949            .map(|unit| unit.fq_name())
16950            .collect::<Vec<_>>();
16951
16952        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
16953        assert!(
16954            names.iter().all(|name| name != "Image.Imaging"),
16955            "the pseudo-declarator must not become a field: {names:#?}"
16956        );
16957
16958        let pointer = parse_cpp_declarations(
16959            "struct Image { PyObject_HEAD Imaging *image; };",
16960            "image-pointer.h",
16961        );
16962        let pointer_names = pointer
16963            .declarations()
16964            .iter()
16965            .map(|unit| unit.fq_name())
16966            .collect::<Vec<_>>();
16967        assert!(
16968            pointer_names.iter().any(|name| name == "Image.image"),
16969            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
16970        );
16971        assert!(
16972            pointer_names.iter().all(|name| name != "Image.Imaging"),
16973            "the pointer recovery error must not become a field: {pointer_names:#?}"
16974        );
16975
16976        let unrelated_macro = parse_cpp_declarations(
16977            "struct Image { OTHER_HEAD Imaging other; };",
16978            "image-near-miss.h",
16979        );
16980        let unrelated_names = unrelated_macro
16981            .declarations()
16982            .iter()
16983            .map(|unit| unit.fq_name())
16984            .collect::<Vec<_>>();
16985        assert!(
16986            unrelated_names.iter().all(|name| name != "Image.other"),
16987            "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
16988        );
16989    }
16990
16991    #[test]
16992    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
16993        let source = r#"namespace testing {
16994namespace internal {
16995    namespace detail {
16996        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
16997        public:
16998            int value() const { return count_ + 1; }
16999        private:
17000            int count_;
17001        };
17002        class GTEST_API_ [[nodiscard]] OtherReporter {
17003        public:
17004            int value() const { return count_ + 2; }
17005        private:
17006            int count_;
17007        };
17008    }
17009
17010    template <typename T>
17011    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
17012
17013    class TailReporter {};
17014}
17015}
17016"#;
17017        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
17018        let declarations = parsed.declarations();
17019        let mut parser = tree_sitter::Parser::new();
17020        parser
17021            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17022            .unwrap();
17023        let tree = parser.parse(source, None).unwrap();
17024        let tail_start = source.find("TailReporter").expect("tail class");
17025        let tail_node = tree
17026            .root_node()
17027            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
17028            .expect("tail class AST node");
17029        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
17030        assert!(
17031            tree.root_node().has_error(),
17032            "the malformed class must exercise recovery"
17033        );
17034        assert!(index.region_at(tail_start).is_some());
17035        assert_eq!(
17036            index.enclosing_namespace_components(tail_node, source),
17037            ["testing", "internal"]
17038        );
17039        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
17040        let mut recovered_parsed = ParsedFile::new(String::new());
17041        let class_unit = CodeUnit::new_fq(
17042            file.clone(),
17043            CodeUnitType::Class,
17044            "testing",
17045            "ScopedFakeTestPartResultReporter",
17046            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
17047        );
17048        let scope = ScopeInfo {
17049            package_name: "testing".to_string(),
17050            module: None,
17051            class_unit: Some(class_unit),
17052            template_signature: Some("<typename T>".to_string()),
17053            template_metadata: Some(CppTemplateMetadata {
17054                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
17055                primary_fq_name: String::new(),
17056                parameters: Vec::new(),
17057                specialization_arguments: Vec::new(),
17058                alias_target: None,
17059            }),
17060            declarations_are_fields: true,
17061            recovered_specialization_member_scope: true,
17062            visible_using_namespaces: Vec::new(),
17063        };
17064        let mut visitor = CppVisitor {
17065            file: &file,
17066            source,
17067            parsed: &mut recovered_parsed,
17068            c_tag_semantics: false,
17069            recovered_class_sibling_scopes: HashMap::default(),
17070            consumed_fragment_regions: Vec::new(),
17071            orphaned_namespaces: index,
17072            partitioned_regions: Vec::new(),
17073            namespace_forward_scans: HashMap::default(),
17074            field_owners: None,
17075            recovery_captures: Vec::new(),
17076            object_macro_fields: HashMap::default(),
17077            ambiguous_object_macro_fields: HashSet::default(),
17078        };
17079        let recovered = visitor
17080            .recovered_namespace_scope(tail_node, &scope)
17081            .expect("the tail must use the stolen namespace scope");
17082        assert_eq!(recovered.package_name, "testing::internal");
17083        assert!(
17084            recovered.class_unit.is_none(),
17085            "recovered namespace declarations cannot retain the malformed class owner"
17086        );
17087        assert!(recovered.template_signature.is_none());
17088        assert!(recovered.template_metadata.is_none());
17089        assert!(!recovered.declarations_are_fields);
17090        assert!(!recovered.recovered_specialization_member_scope);
17091        assert!(
17092            declarations
17093                .iter()
17094                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
17095            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
17096        );
17097        assert!(
17098            declarations
17099                .iter()
17100                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
17101            "the recovered free function remains in its namespace: {declarations:#?}"
17102        );
17103        assert!(
17104            declarations
17105                .iter()
17106                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
17107            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
17108        );
17109        assert!(
17110            declarations.iter().all(|unit| {
17111                !unit
17112                    .short_name()
17113                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
17114            }),
17115            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
17116        );
17117        assert!(
17118            declarations
17119                .iter()
17120                .all(|unit| !unit.identifier().is_empty()),
17121            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
17122        );
17123    }
17124
17125    #[test]
17126    fn object_like_field_macros_materialize_owner_specific_declarations() {
17127        let source = r#"#define PUBLIC_FIELDS int public_value;
17128#define PRIVATE_FIELDS int private_value;
17129#define NOT_A_FIELD_LIST not a declaration
17130
17131struct First {
17132  PUBLIC_FIELDS
17133  PRIVATE_FIELDS
17134};
17135struct Second {
17136  PUBLIC_FIELDS
17137  NOT_A_FIELD_LIST
17138};
17139#undef PUBLIC_FIELDS
17140struct Third {
17141  PUBLIC_FIELDS
17142};
17143"#;
17144        let parsed = parse_cpp_declarations(source, "macro-fields.c");
17145        let fields = parsed
17146            .declarations()
17147            .iter()
17148            .filter(|unit| unit.is_field())
17149            .map(|unit| unit.fq_name())
17150            .collect::<Vec<_>>();
17151
17152        assert!(
17153            fields.contains(&"First.public_value".to_string()),
17154            "{fields:?}"
17155        );
17156        assert!(
17157            fields.contains(&"First.private_value".to_string()),
17158            "{fields:?}"
17159        );
17160        assert!(
17161            fields.contains(&"Second.public_value".to_string()),
17162            "{fields:?}"
17163        );
17164        assert!(
17165            !fields.iter().any(|field| field.contains("not_a_field")),
17166            "malformed macro must fail closed: {fields:?}"
17167        );
17168        assert!(
17169            !fields.iter().any(|field| field.starts_with("Third.")),
17170            "undefined macro must fail closed: {fields:?}"
17171        );
17172    }
17173
17174    #[test]
17175    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
17176        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
17177        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
17178        let mut macros = parsed
17179            .declarations()
17180            .iter()
17181            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
17182            .collect::<Vec<_>>();
17183        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
17184
17185        assert_eq!(macros.len(), 2, "{macros:#?}");
17186        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
17187        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
17188        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
17189        assert_eq!(
17190            parsed.declaration_ranges(macros[1])[0].start_byte,
17191            source.rfind("#define VALUE 2").expect("second definition")
17192        );
17193    }
17194
17195    #[test]
17196    fn identifies_export_macro_class_base_displaced_into_declarator() {
17197        let source = r#"#define PROJECT_API_
17198namespace project {
17199namespace internal {
17200template <typename T>
17201class Base {};
17202}
17203template <typename T>
17204class Wrapper;
17205template <>
17206class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
17207}
17208"#;
17209        let mut parser = tree_sitter::Parser::new();
17210        parser
17211            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17212            .unwrap();
17213        let tree = parser.parse(source, None).unwrap();
17214        let start = source.find("internal::Base<int>").expect("base");
17215        let mut base = tree
17216            .root_node()
17217            .descendant_for_byte_range(start, start + 8)
17218            .expect("base syntax");
17219        while base.kind() != "qualified_identifier" {
17220            base = base.parent().expect("qualified base ancestor");
17221        }
17222        assert!(
17223            is_recovered_exported_class_base_type_node(base, source),
17224            "{}",
17225            tree.root_node().to_sexp()
17226        );
17227    }
17228
17229    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
17230        let mut identities = parsed
17231            .declarations()
17232            .iter()
17233            .filter(|unit| unit.is_function())
17234            .map(|unit| {
17235                (
17236                    unit.fq_name(),
17237                    unit.signature().unwrap_or_default().to_string(),
17238                )
17239            })
17240            .collect::<Vec<_>>();
17241        identities.sort();
17242        identities
17243    }
17244
17245    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
17246    /// prototype macros. Each case is parsed independently so a preceding
17247    /// malformed line cannot flatten the next declaration's fields into one
17248    /// cascading `ERROR`; that shape has no structural proof and must fail
17249    /// closed rather than fall back to a token scan. Parameter names are
17250    /// dropped from signatures just as they are for ordinary C prototypes.
17251    #[test]
17252    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
17253        let cases = [
17254            (
17255                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
17256                "pg_typemap_fit_to_result",
17257                "(VALUE, VALUE)",
17258            ),
17259            (
17260                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
17261                "pg_typemap_result_value",
17262                "(t_typemap *, VALUE, int, int)",
17263            ),
17264            (
17265                "void pg_typemap_mark _(( void * ));",
17266                "pg_typemap_mark",
17267                "(void *)",
17268            ),
17269            (
17270                "void init_pg_type_map _(( void ));",
17271                "init_pg_type_map",
17272                "(void)",
17273            ),
17274            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
17275            (
17276                "extern VALUE pg_extern _(( VALUE, VALUE ));",
17277                "pg_extern",
17278                "(VALUE, VALUE)",
17279            ),
17280            (
17281                "size_t pg_typemap_memsize _(( const void * ));",
17282                "pg_typemap_memsize",
17283                "(const void *)",
17284            ),
17285            (
17286                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
17287                "pg_wrap_socket_io",
17288                "(int, VALUE, VALUE *, int *)",
17289            ),
17290            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
17291            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
17292            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
17293        ];
17294
17295        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
17296            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
17297            assert_eq!(
17298                function_identities(&parsed),
17299                vec![(name.to_string(), signature.to_string())],
17300                "{source}: {:#?}",
17301                parsed.declarations()
17302            );
17303            assert!(
17304                parsed.declarations().iter().all(|unit| !unit.is_field()),
17305                "{source} must not retain the malformed field: {:#?}",
17306                parsed.declarations()
17307            );
17308        }
17309    }
17310
17311    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
17312    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
17313    /// it as a multiplication of a type by a call
17314    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
17315    /// wrapped in an `expression_statement` that `has_error()` only because
17316    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
17317    /// here with a clean preceding field so the prototype remains the
17318    /// parser-owned `expression_statement` this recovery requires.
17319    #[test]
17320    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
17321        let cases = [
17322            (
17323                "PGconn *pg_get_pgconn _(( VALUE ));",
17324                "pg_get_pgconn",
17325                "(VALUE)",
17326            ),
17327            (
17328                "PGresult* pgresult_get _(( VALUE ));",
17329                "pgresult_get",
17330                "(VALUE)",
17331            ),
17332        ];
17333        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
17334            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
17335            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
17336            assert_eq!(
17337                function_identities(&parsed),
17338                vec![(name.to_string(), signature.to_string())],
17339                "{source}: {:#?}",
17340                parsed.declarations()
17341            );
17342            assert_eq!(
17343                parsed
17344                    .declarations()
17345                    .iter()
17346                    .filter(|unit| unit.is_field())
17347                    .map(|unit| unit.fq_name())
17348                    .collect::<Vec<_>>(),
17349                vec!["rb_mPG".to_string()],
17350                "{source}: {:#?}",
17351                parsed.declarations()
17352            );
17353        }
17354    }
17355
17356    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
17357    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
17358    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
17359    /// regions. The witnessed declaration must still become a Function and
17360    /// its type must not survive as the name of a bogus Field. This test does
17361    /// not claim that structurally flattened neighboring prototypes are safe
17362    /// to recover.
17363    #[test]
17364    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
17365        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
17366VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
17367int pg_typemap_fit_to_copy_get                         _(( VALUE ));
17368VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
17369t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
17370VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
17371void pg_typemap_mark                                   _(( void * ));
17372size_t pg_typemap_memsize                              _(( const void * ));
17373void pg_typemap_compact                                _(( void * ));
17374
17375PGconn *pg_get_pgconn                                  _(( VALUE ));
17376t_pg_connection *pg_get_connection                     _(( VALUE ));
17377VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
17378#ifdef __GNUC__
17379__attribute__((format(printf, 3, 4)))
17380#endif
17381NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
17382VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
17383void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
17384
17385
17386VALUE pg_new_result                                    _(( PGresult *, VALUE ));
17387VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
17388PGresult* pgresult_get                                 _(( VALUE ));
17389VALUE pg_result_check                                  _(( VALUE ));
17390VALUE pg_result_clear                                  _(( VALUE ));
17391VALUE pg_tuple_new                                     _(( VALUE, int ));
17392
17393/*
17394 * Fetch the data pointer for the result object
17395 */
17396static inline t_pg_result *
17397pgresult_get_this( VALUE self )
17398{
17399	return RTYPEDDATA_DATA(self);
17400}
17401
17402
17403rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
17404const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
17405rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
17406
17407"#;
17408        let parsed = parse_cpp_declarations(source, "pg.h");
17409        assert!(
17410            function_identities(&parsed)
17411                .iter()
17412                .any(|(name, signature)| name == "pg_typemap_result_value"
17413                    && signature == "(t_typemap *, VALUE, int, int)"),
17414            "the real issue witness must be a Function with its C signature: {:#?}",
17415            parsed.declarations()
17416        );
17417        assert!(
17418            parsed
17419                .declarations()
17420                .iter()
17421                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
17422            "the issue witness must not leave its return type as a Field name: {:#?}",
17423            parsed.declarations()
17424        );
17425    }
17426
17427    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
17428    /// between `explicit` and a constructor name onto the name, producing a
17429    /// `qualified_identifier` whose `::` it had to invent. The declared member
17430    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
17431    /// macro in type position, was already recovered and is the control that
17432    /// both spellings agree.
17433    #[test]
17434    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
17435        let source = r#"class SIMD_4x26 final {
17436   public:
17437      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
17438      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
17439      int m_v;
17440};
17441"#;
17442        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
17443        assert_eq!(
17444            function_identities(&parsed),
17445            vec![
17446                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
17447                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
17448            ],
17449            "{:#?}",
17450            parsed.declarations()
17451        );
17452    }
17453
17454    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
17455    /// `ERROR` holding the wrapped declaration and every declaration after it
17456    /// until the parser recovers. All of them must be indexed, each with the
17457    /// byte range that spells it.
17458    #[test]
17459    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
17460        let source = r#"#include <cstdint>
17461struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
17462    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
17463                     struct llama_model * model,
17464              struct llama_context_params   params),
17465            "use llama_init_from_model instead");
17466    LLAMA_API int32_t llama_tokenize(
17467        const struct llama_vocab * vocab,
17468                      const char * text,
17469                            bool   parse_special);
17470    LLAMA_API int32_t llama_other(int a);
17471"#;
17472        let parsed = parse_cpp_declarations(source, "llama.h");
17473        assert_eq!(
17474            function_identities(&parsed),
17475            vec![
17476                (
17477                    "llama_new_context_with_model".to_string(),
17478                    "(struct llama_model *, struct llama_context_params)".to_string()
17479                ),
17480                ("llama_other".to_string(), "(int)".to_string()),
17481                (
17482                    "llama_tokenize".to_string(),
17483                    "(const struct llama_vocab *, const char *, bool)".to_string()
17484                ),
17485            ],
17486            "{:#?}",
17487            parsed.declarations()
17488        );
17489
17490        // Each recovered declaration owns the source that spells it, so
17491        // navigation lands on the declaration and not on the macro envelope.
17492        for (name, expected) in [
17493            (
17494                "llama_new_context_with_model",
17495                "LLAMA_API struct llama_context * llama_new_context_with_model(",
17496            ),
17497            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
17498            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
17499        ] {
17500            let unit = parsed
17501                .declarations()
17502                .iter()
17503                .find(|unit| unit.is_function() && unit.fq_name() == name)
17504                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
17505            let [range] = parsed.declaration_ranges(unit) else {
17506                panic!("{name} must have exactly one range");
17507            };
17508            let text = &source[range.start_byte..range.end_byte];
17509            assert!(
17510                text.starts_with(expected),
17511                "{name} range is {text:?}, expected it to start with {expected:?}"
17512            );
17513            assert!(
17514                text.ends_with(')') || text.ends_with(';'),
17515                "{name}: {text:?}"
17516            );
17517        }
17518    }
17519
17520    /// whisper.cpp's own `include/whisper.h` writes the same construct with the
17521    /// invocation's `(` at the end of its line, and the parser then keeps no
17522    /// invocation whole. It hands the first one's hint and its own `)` and `;`
17523    /// to the `expression_statement` beside it, and flattens the ones after
17524    /// that into bare `identifier`/`type_identifier`, `(`,
17525    /// `parameter_declaration`, `,` and `ERROR` parts of whatever node it is
17526    /// already inside. No part of that is a head the grouping can be read from,
17527    /// which is why the invocation is read from the token order instead
17528    /// (#3094).
17529    ///
17530    /// The identifiers are `library_*` at whisper's own lengths on purpose:
17531    /// tree-sitter prices error recovery partly by how much input a repair
17532    /// skips, and the same construct written with shorter names parses cleanly.
17533    #[test]
17534    fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
17535        let source = r#"    LIBRARY_DEPRECATED(
17536        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17537        "use library_init_from_file_with_params instead"
17538    );
17539    LIBRARY_DEPRECATED(
17540        LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
17541        "use library_init_from_buffer_with_params instead"
17542    );
17543    LIBRARY_DEPRECATED(
17544        LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
17545        "use library_init_with_params instead"
17546    );
17547"#;
17548        let mut parser = tree_sitter::Parser::new();
17549        parser
17550            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17551            .expect("C++ grammar");
17552        let tree = parser.parse(source, None).expect("C++ tree");
17553        let root = tree.root_node();
17554
17555        // The shape this recovery exists for: the second invocation is not a
17556        // node of its own, and the third is a run of bare siblings inside the
17557        // second one's `ERROR`.
17558        let mut cursor = root.walk();
17559        let items = root.children(&mut cursor).collect::<Vec<_>>();
17560        let [first, hint_statement, rest @ ..] = items.as_slice() else {
17561            panic!("{}", root.to_sexp());
17562        };
17563        assert_eq!(first.kind(), "ERROR");
17564        assert_eq!(hint_statement.kind(), "expression_statement");
17565        assert!(
17566            node_text(*first, source).ends_with(','),
17567            "the first invocation's own `)` and `;` are the statement's, not its own: {}",
17568            root.to_sexp()
17569        );
17570        let swallowing = rest
17571            .iter()
17572            .find(|item| item.kind() == "ERROR")
17573            .unwrap_or_else(|| panic!("{}", root.to_sexp()));
17574        let mut swallowing_cursor = swallowing.walk();
17575        let flattened = swallowing
17576            .children(&mut swallowing_cursor)
17577            .map(|child| child.kind())
17578            .collect::<Vec<_>>();
17579        assert_eq!(
17580            flattened,
17581            vec![
17582                "identifier",
17583                "(",
17584                "parameter_declaration",
17585                ",",
17586                "ERROR",
17587                "type_identifier",
17588                "(",
17589                "parameter_declaration",
17590                ",",
17591                "\"",
17592                "identifier",
17593                "identifier",
17594                "identifier",
17595                "\"",
17596                ")",
17597            ],
17598            "the third invocation must be flattened into the second one's node: {}",
17599            root.to_sexp()
17600        );
17601
17602        // Both are admitted, and each names the byte just past its own `;`.
17603        let first_run =
17604            collapsed_macro_declaration_run(*first, source).expect("the first invocation");
17605        assert_eq!(
17606            &source[..first_run.invocation_end],
17607            &source[..source.find("instead\"\n    );").expect("first hint")
17608                + "instead\"\n    );".len()]
17609        );
17610        assert_eq!(
17611            first_run.region_end, first_run.invocation_end,
17612            "the first invocation swallowed nothing, so the recovery owns only its own bytes"
17613        );
17614        let swallowing_run =
17615            collapsed_macro_declaration_run(*swallowing, source).expect("the second invocation");
17616        assert!(
17617            swallowing_run.invocation_end < swallowing.end_byte(),
17618            "the second invocation swallowed the third"
17619        );
17620        assert_eq!(
17621            swallowing_run.region_end,
17622            root.end_byte(),
17623            "a swallowing invocation owns the region to the close of its declaration scope"
17624        );
17625
17626        let parsed = parse_cpp_declarations(source, "library.h");
17627        assert_eq!(
17628            function_identities(&parsed),
17629            vec![
17630                (
17631                    "library_init".to_string(),
17632                    "(struct library_model_loader *)".to_string()
17633                ),
17634                (
17635                    "library_init_from_buffer".to_string(),
17636                    "(void *, size_t)".to_string()
17637                ),
17638                (
17639                    "library_init_from_file".to_string(),
17640                    "(const char *)".to_string()
17641                ),
17642            ],
17643            "{:#?}",
17644            parsed.declarations()
17645        );
17646    }
17647
17648    /// The hand-off that keeps the scan finite. An invocation that reaches the
17649    /// end of its own declaration scope swallowed nothing, and the ordinary
17650    /// `macro_wrapped_declarations` reader has it. The scan reparses exactly
17651    /// that region for every invocation it finds, so admitting it here would
17652    /// hand the scan its own input back.
17653    #[test]
17654    fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
17655        let source = r#"LIBRARY_DEPRECATED(
17656        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17657        "use library_init_from_file_with_params instead"
17658    );"#;
17659        let mut parser = tree_sitter::Parser::new();
17660        parser
17661            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17662            .expect("C++ grammar");
17663        let tree = parser.parse(source, None).expect("C++ tree");
17664        let root = tree.root_node();
17665        let head = root.named_child(0).expect("the invocation");
17666        assert!(
17667            collapsed_macro_declaration_run(head, source).is_none(),
17668            "{}",
17669            root.to_sexp()
17670        );
17671        assert!(
17672            !macro_wrapped_declarations(head, source).is_empty(),
17673            "the ordinary reader must be the one that has it: {}",
17674            root.to_sexp()
17675        );
17676    }
17677
17678    /// Negative controls for the same recovery: a macro invocation whose
17679    /// arguments are not a declaration recovers nothing, whether the parser
17680    /// keeps it clean, reads the arguments as a type, or reads them as a bare
17681    /// declarator.
17682    #[test]
17683    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
17684        for source in [
17685            "int before;\nFOO(1, 2);\nint after;\n",
17686            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
17687            "int before;\nMACRO(int a, int b);\nint after;\n",
17688            "DECLARE_HANDLE(HWND);\nint after;\n",
17689        ] {
17690            let parsed = parse_cpp_declarations(source, "macro-call.h");
17691            assert_eq!(
17692                function_identities(&parsed),
17693                Vec::new(),
17694                "{source:?} must declare no function: {:#?}",
17695                parsed.declarations()
17696            );
17697        }
17698    }
17699
17700    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
17701    /// makes tree-sitter read the macro as the member's type and its argument
17702    /// list as a parenthesized declarator, which then swallows the attributed
17703    /// member and the member written after it. Both are members.
17704    #[test]
17705    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
17706        let source = r#"#include <string_view>
17707namespace Botan {
17708class DL_Group final {
17709   public:
17710      DL_Group() = default;
17711      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17712      DL_Group(std::string_view pem, int format);
17713      size_t get_p() const;
17714};
17715}
17716"#;
17717        let parsed = parse_cpp_declarations(source, "dl_group.h");
17718        assert_eq!(
17719            function_identities(&parsed),
17720            vec![
17721                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
17722                (
17723                    "Botan.DL_Group.DL_Group".to_string(),
17724                    "(std::string_view)".to_string()
17725                ),
17726                (
17727                    "Botan.DL_Group.DL_Group".to_string(),
17728                    "(std::string_view, int)".to_string()
17729                ),
17730                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17731            ],
17732            "{:#?}",
17733            parsed.declarations()
17734        );
17735    }
17736
17737    /// #2552 shape 3, export-macro class. The class head macro makes the body a
17738    /// `compound_statement`, so members come from the region reparse, and one
17739    /// string-attribute member used to make that reparse unindexable -- which
17740    /// left the class with no members at all.
17741    #[test]
17742    fn an_export_macro_class_keeps_its_string_attribute_members() {
17743        let source = r#"#include <string_view>
17744namespace Botan {
17745class BOTAN_PUBLIC_API(2, 0) DL_Group final {
17746   public:
17747      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17748      DL_Group(std::string_view pem, int format);
17749      size_t get_p() const;
17750};
17751}
17752"#;
17753        let parsed = parse_cpp_declarations(source, "dl_group.h");
17754        assert!(
17755            parsed
17756                .declarations()
17757                .iter()
17758                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
17759            "{:#?}",
17760            parsed.declarations()
17761        );
17762        assert_eq!(
17763            function_identities(&parsed),
17764            vec![
17765                (
17766                    "Botan.DL_Group.DL_Group".to_string(),
17767                    "(std::string_view)".to_string()
17768                ),
17769                (
17770                    "Botan.DL_Group.DL_Group".to_string(),
17771                    "(std::string_view, int)".to_string()
17772                ),
17773                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17774            ],
17775            "{:#?}",
17776            parsed.declarations()
17777        );
17778    }
17779
17780    /// #2552 shape 3, the `= default` variant plus the access-label
17781    /// constructor. The attributed defaulted constructor separates cleanly, but
17782    /// it strands the next member in a bare `ERROR`, and a constructor written
17783    /// with a member-initializer list under `private:` dissolves into
17784    /// expression soup that the reparse recovers from its own source range.
17785    #[test]
17786    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
17787        let source = r#"namespace Botan {
17788class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
17789   public:
17790      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
17791      XMSS_Parameters(int oid, int len);
17792      size_t len() const;
17793
17794   private:
17795      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
17796            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
17797
17798      int m_oid;
17799      int m_wots_oid;
17800      size_t m_element_size;
17801      size_t m_tree_height;
17802};
17803}
17804"#;
17805        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
17806        let constructors = function_identities(&parsed)
17807            .into_iter()
17808            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
17809            .map(|(_, signature)| signature)
17810            .collect::<Vec<_>>();
17811        assert_eq!(
17812            constructors,
17813            vec![
17814                "()".to_string(),
17815                "(int, int)".to_string(),
17816                "(int, int, size_t, size_t)".to_string(),
17817            ],
17818            "{:#?}",
17819            parsed.declarations()
17820        );
17821    }
17822
17823    /// Negative control for the same rule: a real qualified name spells its
17824    /// `::` in the source, so the separator is present rather than MISSING and
17825    /// the out-of-line definition keeps its owner.
17826    #[test]
17827    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
17828        let source = r#"namespace shell {
17829struct Outer {
17830   struct Inner {
17831      Inner(int v);
17832      void run(int v);
17833   };
17834};
17835Outer::Inner::Inner(int v) {}
17836void Outer::Inner::run(int v) {}
17837}
17838"#;
17839        let parsed = parse_cpp_declarations(source, "outer.cpp");
17840        let names = function_identities(&parsed)
17841            .into_iter()
17842            .map(|(fq_name, _)| fq_name)
17843            .collect::<Vec<_>>();
17844        assert!(
17845            names
17846                .iter()
17847                .all(|name| name.starts_with("shell.Outer$Inner.")),
17848            "{names:#?}"
17849        );
17850    }
17851
17852    #[test]
17853    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
17854        let source = r#"namespace control {
17855template <typename T>
17856class AnySpan;
17857template <typename T>
17858class ABSL_ATTRIBUTE_VIEW AnySpan {
17859 public:
17860  int begin() const;
17861};
17862}
17863
17864namespace absl {
17865ABSL_NAMESPACE_BEGIN
17866template <typename T>
17867class ABSL_ATTRIBUTE_VIEW Span {
17868 public:
17869  int begin() const;
17870  int back() const;
17871};
17872
17873int begin();
17874int back();
17875}
17876"#;
17877        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
17878        let declarations = parsed.declarations();
17879        assert!(
17880            declarations
17881                .iter()
17882                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
17883        );
17884        for method in ["begin", "back"] {
17885            assert!(declarations.iter().any(|unit| {
17886                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
17887            }));
17888            assert!(
17889                declarations.iter().any(|unit| {
17890                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
17891                })
17892            );
17893        }
17894        assert!(
17895            declarations
17896                .iter()
17897                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
17898        );
17899        assert!(
17900            declarations
17901                .iter()
17902                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
17903        );
17904        assert!(
17905            declarations
17906                .iter()
17907                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
17908        );
17909    }
17910
17911    #[test]
17912    fn explicit_global_member_definition_has_canonical_package_boundary() {
17913        let source = r#"
17914namespace arangodb::aql {
17915class ExecutionPlan {
17916 public:
17917  template<class... Args> Node* createNode(Args&&... args);
17918};
17919}
17920
17921template<class... Args>
17922Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
17923"#;
17924        let parsed = parse_cpp_declarations(source, "global-member.cpp");
17925
17926        assert!(parsed.declarations().iter().any(|unit| {
17927            unit.is_function()
17928                && unit.package_name() == "arangodb::aql"
17929                && unit.short_name() == "ExecutionPlan.createNode"
17930                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
17931        }));
17932    }
17933
17934    #[test]
17935    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
17936        let source = r#"
17937#ifndef TINYXML2_INCLUDED
17938#define TINYXML2_INCLUDED
17939namespace tinyxml2 {
17940class TINYXML2_LIB XMLUtil {
17941 public:
17942  static const char* SkipWhiteSpace(const char* p) {
17943    while (*p) {
17944      if (*p == ' ') {
17945        ++p;
17946      }
17947    }
17948    return p;
17949  }
17950  static bool StringEqual(const char* p, const char* q) {
17951    return p == q;
17952  }
17953  class TINYXML2_LIB Helper {
17954   public:
17955    void Touch();
17956  };
17957  static void ToStr(int value, char* buffer);
17958 private:
17959  static const char* writeBoolTrue;
17960};
17961
17962class TINYXML2_LIB XMLNode {
17963 public:
17964  virtual XMLNode* ShallowClone() const = 0;
17965  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
17966};
17967}
17968#endif
17969"#;
17970        let mut parser = tree_sitter::Parser::new();
17971        parser
17972            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17973            .unwrap();
17974        let tree = parser.parse(source, None).unwrap();
17975        let mut boundary_found = false;
17976        walk_named_tree_preorder(tree.root_node(), true, |node| {
17977            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
17978                && name == "XMLUtil"
17979            {
17980                boundary_found = fragmented_export_sibling_class_boundary(node, source)
17981                    .and_then(|boundary| {
17982                        recover_exported_class_function_definition(boundary, source)
17983                    })
17984                    .is_some_and(|(_, name, _)| name == "XMLNode");
17985            }
17986            WalkControl::Continue
17987        });
17988        assert!(
17989            boundary_found,
17990            "fixture must exercise the recovered sibling boundary"
17991        );
17992
17993        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
17994        assert!(
17995            parsed
17996                .declarations()
17997                .iter()
17998                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
17999            "{:#?}",
18000            parsed.declarations()
18001        );
18002        assert!(
18003            parsed
18004                .declarations()
18005                .iter()
18006                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
18007            "{:#?}",
18008            parsed.declarations()
18009        );
18010        assert!(parsed.declarations().iter().any(|unit| {
18011            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
18012        }));
18013        assert!(
18014            parsed
18015                .declarations()
18016                .iter()
18017                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
18018        );
18019        assert!(
18020            parsed
18021                .declarations()
18022                .iter()
18023                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
18024        );
18025    }
18026
18027    #[test]
18028    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
18029        // Clang's diagnostic suite intentionally contains this ill-formed
18030        // spelling. The analyzer must retain the parser's explicit-global AST
18031        // boundary instead of constructing `cwg311::::cwg311::X`.
18032        let parsed = parse_cpp_declarations(
18033            r#"
18034namespace cwg311 {
18035namespace X { namespace Y {} }
18036namespace ::cwg311::X {}
18037}
18038"#,
18039            "explicit-global-namespace.cpp",
18040        );
18041
18042        assert!(parsed.declarations().iter().any(|unit| {
18043            unit.kind() == CodeUnitType::Module
18044                && unit.short_name() == "cwg311::X"
18045                && unit.fq_name() == "cwg311::X"
18046        }));
18047        assert!(
18048            parsed
18049                .declarations()
18050                .iter()
18051                .all(|unit| !unit.short_name().contains("::::")),
18052            "recovered namespace names must not retain empty scope components: {:#?}",
18053            parsed.declarations()
18054        );
18055    }
18056
18057    #[test]
18058    fn repeated_scope_separator_does_not_create_empty_function_owner() {
18059        let scope = ScopeInfo {
18060            package_name: "X".to_string(),
18061            module: None,
18062            class_unit: None,
18063            template_signature: None,
18064            template_metadata: None,
18065            declarations_are_fields: false,
18066            recovered_specialization_member_scope: false,
18067            visible_using_namespaces: Vec::new(),
18068        };
18069
18070        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
18071
18072        assert!(owner.is_none());
18073        assert_eq!(name, "doit");
18074        assert_eq!(package, "X");
18075    }
18076
18077    #[test]
18078    fn trailing_decltype_expression_is_not_a_function_declarator() {
18079        let source = r#"
18080namespace boost { namespace detail {
18081#if ! defined(BOOST_NO_SFINAE_EXPR) && \
18082    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
18083    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
18084#define BOOST_THREAD_PROVIDES_INVOKE
18085#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
18086template <class Fp, class A0, class ...Args>
18087inline auto
18088invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
18089       BOOST_THREAD_RV_REF(Args) ...args)
18090    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
18091{
18092    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
18093}
18094#endif
18095#endif
18096}}
18097"#;
18098        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
18099
18100        assert!(
18101            parsed
18102                .declarations()
18103                .iter()
18104                .all(|unit| unit.short_name() != ".*f")
18105        );
18106    }
18107
18108    fn find_class_named<'tree>(
18109        root: Node<'tree>,
18110        source: &str,
18111        expected_name: &str,
18112    ) -> Option<Node<'tree>> {
18113        let mut stack = vec![root];
18114        while let Some(node) = stack.pop() {
18115            if node.kind() == "class_specifier"
18116                && node
18117                    .child_by_field_name("name")
18118                    .is_some_and(|name| node_text(name, source) == expected_name)
18119            {
18120                return Some(node);
18121            }
18122            let mut cursor = node.walk();
18123            stack.extend(node.named_children(&mut cursor));
18124        }
18125        None
18126    }
18127
18128    #[test]
18129    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
18130        let source = r#"EXPORT void definition(struct Value value) {}
18131EXPORT void prototype(struct Value value);
18132"#;
18133        let mut parser = tree_sitter::Parser::new();
18134        parser
18135            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18136            .unwrap();
18137        let tree = parser.parse(source, None).unwrap();
18138        let root = tree.root_node();
18139        let mut cursor = root.walk();
18140        let callables = root
18141            .named_children(&mut cursor)
18142            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
18143            .collect::<Vec<_>>();
18144
18145        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
18146        for callable in callables {
18147            assert!(callable.has_error(), "fixture must exercise error recovery");
18148            assert!(
18149                cpp_sentinel_macro_parts(callable, source).is_none(),
18150                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
18151            );
18152        }
18153    }
18154
18155    #[test]
18156    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
18157        let source = r#"namespace absl {
18158ABSL_NAMESPACE_BEGIN
18159// Generate a floating-point variate conforming to a Beta distribution:
18160template <typename RealType = double>
18161class beta_distribution {
18162 public:
18163  using result_type = RealType;
18164
18165
18166  beta_distribution() : beta_distribution(1) {}
18167
18168  explicit beta_distribution(result_type alpha, result_type beta = 1)
18169      : param_(alpha, beta) {}
18170
18171  explicit beta_distribution(const param_type& p) : param_(p) {}
18172
18173  void reset() {}
18174
18175  // Generating functions
18176  template <typename URBG>
18177  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
18178    return (*this)(g, param_);
18179  }
18180
18181};
18182ABSL_NAMESPACE_END
18183}  // namespace absl
18184"#;
18185        let mut parser = tree_sitter::Parser::new();
18186        parser
18187            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18188            .unwrap();
18189        let tree = parser.parse(source, None).unwrap();
18190        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18191        let body = namespace
18192            .child_by_field_name("body")
18193            .expect("fixture namespace body");
18194        let sentinel = body.named_child(0).expect("sentinel envelope");
18195        let callable = sentinel
18196            .child_by_field_name("declarator")
18197            .and_then(extract_function_declarator)
18198            .and_then(cpp_function_declarator_name_node)
18199            .expect("preserved callable name");
18200
18201        assert_eq!(sentinel.kind(), "function_definition");
18202        assert_eq!(callable.kind(), "operator_name");
18203        assert!(
18204            cpp_sentinel_macro_parts(sentinel, source).is_some(),
18205            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
18206        );
18207    }
18208
18209    #[test]
18210    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
18211        let source = r#"namespace absl {
18212ABSL_NAMESPACE_BEGIN
18213// absl::discrete_distribution
18214//
18215// A discrete distribution produces random integers i, where 0 <= i < n
18216template <typename IntType = int>
18217class discrete_distribution {
18218 public:
18219  using result_type = IntType;
18220  class param_type {
18221   public:
18222    param_type() { init(); }
18223    template <typename InputIterator>
18224    explicit param_type(InputIterator begin, InputIterator end)
18225        : p_(begin, end) {
18226      init();
18227    }
18228  };
18229  discrete_distribution() : param_() {}
18230  explicit discrete_distribution(const param_type& p) : param_(p) {}
18231};
18232ABSL_NAMESPACE_END
18233}  // namespace absl
18234"#;
18235        let mut parser = tree_sitter::Parser::new();
18236        parser
18237            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18238            .unwrap();
18239        let tree = parser.parse(source, None).unwrap();
18240        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18241        let body = namespace
18242            .child_by_field_name("body")
18243            .expect("fixture namespace body");
18244        let sentinel = body.named_child(0).expect("sentinel envelope");
18245        let callable = sentinel
18246            .child_by_field_name("declarator")
18247            .and_then(extract_function_declarator)
18248            .and_then(cpp_function_declarator_name_node)
18249            .expect("preserved callable name");
18250
18251        assert_eq!(sentinel.kind(), "function_definition");
18252        assert_eq!(callable.kind(), "identifier");
18253        assert!(
18254            cpp_sentinel_macro_parts(sentinel, source).is_some(),
18255            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
18256        );
18257    }
18258
18259    #[test]
18260    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
18261        let source = r#"
18262#define CPPCHECKLIB
18263class Library {
18264    struct Container {
18265        CPPCHECKLIB static std::string toString(Yield yield);
18266        CPPCHECKLIB static std::string toString(Action action);
18267    };
18268};
18269"#;
18270        let mut parser = tree_sitter::Parser::new();
18271        parser
18272            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18273            .unwrap();
18274        let tree = parser.parse(source, None).unwrap();
18275        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
18276        let parsed = parse_cpp_file(&file, source, &tree);
18277        assert!(
18278            parsed
18279                .declarations()
18280                .iter()
18281                .all(|unit| unit.fq_name() != "Library$Container.std"),
18282            "the qualified return-type namespace must not become a field: {:#?}",
18283            parsed.declarations()
18284        );
18285        for expected in ["(Yield)", "(Action)"] {
18286            assert!(
18287                parsed.declarations().iter().any(|unit| {
18288                    unit.is_function()
18289                        && unit.fq_name() == "Library$Container.toString"
18290                        && unit.signature() == Some(expected)
18291                }),
18292                "recovered toString overload {expected} is missing: {:#?}",
18293                parsed.declarations()
18294            );
18295        }
18296    }
18297
18298    #[test]
18299    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
18300        let source = r#"
18301#define SIMPLECPP_LIB
18302namespace simplecpp {
18303using TokenString = std::string;
18304struct Location { int line{}; };
18305class SIMPLECPP_LIB Token {
18306  TokenString prefix;
18307  void prefix_method() {}
18308 public:
18309  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18310      whitespaceahead(wsahead), location(loc), string(s)
18311      // The comment must not hide the constructor body from recovery.
18312      {
18313      flags();
18314  }
18315  TokenString string;
18316  bool whitespaceahead;
18317  Location location;
18318  Token *previous{};
18319 private:
18320  void flags() {
18321      whitespaceahead = true;
18322  }
18323};
18324}
18325"#;
18326        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
18327
18328        let location_fields = parsed
18329            .declarations()
18330            .iter()
18331            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
18332            .collect::<Vec<_>>();
18333        assert_eq!(
18334            location_fields.len(),
18335            1,
18336            "location should have one class-owned declaration: {:#?}",
18337            parsed.declarations()
18338        );
18339        assert!(
18340            location_fields[0].is_field(),
18341            "location has wrong kind: {:#?}",
18342            parsed.declarations()
18343        );
18344        assert!(
18345            parsed.declarations().iter().all(|unit| {
18346                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
18347            })
18348        );
18349        assert!(
18350            parsed.declarations().iter().all(|unit| {
18351                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18352            })
18353        );
18354        assert!(
18355            parsed
18356                .declarations()
18357                .iter()
18358                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
18359        );
18360        assert!(
18361            parsed
18362                .declarations()
18363                .iter()
18364                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
18365            "the recovered class must retain its constructor: {:#?}",
18366            parsed.declarations()
18367        );
18368        assert!(
18369            parsed
18370                .declarations()
18371                .iter()
18372                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
18373        );
18374        assert!(parsed.declarations().iter().any(|unit| {
18375            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
18376        }));
18377        let constructor = parsed
18378            .declarations()
18379            .iter()
18380            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
18381            .expect("recovered constructor");
18382        let constructor_start = source.find("Token(const").expect("constructor start");
18383        let constructor_end = source
18384            .get(
18385                ..source
18386                    .find("  TokenString string;")
18387                    .expect("constructor end"),
18388            )
18389            .expect("constructor slice")
18390            .trim_end()
18391            .len();
18392        assert!(
18393            parsed
18394                .navigation_ranges
18395                .get(constructor)
18396                .is_some_and(|ranges| {
18397                    ranges.iter().any(|range| {
18398                        range.start_byte == constructor_start && range.end_byte == constructor_end
18399                    })
18400                }),
18401            "constructor navigation must span the full body: {:#?}",
18402            parsed.navigation_ranges
18403        );
18404        assert_eq!(
18405            parsed
18406                .signature_metadata
18407                .get(constructor)
18408                .and_then(|metadata| metadata.first())
18409                .and_then(SignatureMetadata::callable_linkage),
18410            Some(CallableLinkage::External)
18411        );
18412        let token_class = parsed
18413            .declarations()
18414            .iter()
18415            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
18416            .expect("recovered Token class");
18417        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
18418        assert!(
18419            parsed
18420                .navigation_ranges
18421                .get(token_class)
18422                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
18423            "class navigation must include the terminating semicolon: {:#?}",
18424            parsed.navigation_ranges
18425        );
18426    }
18427
18428    #[test]
18429    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
18430        let source = r#"
18431#define SIMPLECPP_LIB
18432namespace simplecpp {
18433using TokenString = std::string;
18434class Macro;
18435struct Location {
18436  unsigned int fileIndex{};
18437  unsigned int line{};
18438  unsigned int col{};
18439};
18440struct Output {
18441  int type;
18442};
18443class SIMPLECPP_LIB Token {
18444 public:
18445  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18446      whitespaceahead(wsahead), location(loc), string(s) {
18447      flags();
18448  }
18449  Token(const Token &tok) :
18450      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
18451      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
18452      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
18453  Token &operator=(const Token &tok) = delete;
18454  const TokenString& str() const { return string; }
18455  void setstr(const std::string &s) { string = s; flags(); }
18456  bool isOneOf(const char ops[]) const;
18457  TokenString macro;
18458  char op;
18459  bool comment;
18460  bool name;
18461  bool number;
18462  bool whitespaceahead;
18463  Location location;
18464  Token *previous{};
18465  Token *next{};
18466 private:
18467  void flags() {
18468      name = !string.empty();
18469      comment = false;
18470      number = false;
18471      op = 0;
18472  }
18473  TokenString string;
18474};
18475}
18476struct Following {
18477  int type;
18478};
18479class SIMPLECPP_LIB Later {
18480 public:
18481  Later(int value) : value(value) {}
18482  int value;
18483};
18484"#;
18485        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
18486        assert!(
18487            parsed
18488                .declarations()
18489                .iter()
18490                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
18491        );
18492        assert!(
18493            !parsed
18494                .declarations()
18495                .iter()
18496                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
18497        );
18498        assert!(
18499            parsed
18500                .declarations()
18501                .iter()
18502                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
18503        );
18504        assert!(
18505            !parsed
18506                .declarations()
18507                .iter()
18508                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18509        );
18510        assert!(
18511            parsed
18512                .declarations()
18513                .iter()
18514                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
18515        );
18516        assert!(
18517            parsed
18518                .declarations()
18519                .iter()
18520                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
18521        );
18522        assert!(
18523            parsed
18524                .declarations()
18525                .iter()
18526                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
18527        );
18528        assert!(
18529            parsed
18530                .declarations()
18531                .iter()
18532                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
18533        );
18534        assert!(
18535            parsed
18536                .declarations()
18537                .iter()
18538                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
18539        );
18540        assert!(
18541            parsed
18542                .declarations()
18543                .iter()
18544                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
18545        );
18546        assert!(parsed.declarations().iter().all(|unit| {
18547            !matches!(
18548                unit.fq_name().as_str(),
18549                "simplecpp.Token.Following" | "simplecpp.Token.Later"
18550            )
18551        }));
18552        assert!(
18553            !parsed
18554                .declarations()
18555                .iter()
18556                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
18557            "the following struct must remain outside the recovered Token class"
18558        );
18559    }
18560
18561    #[test]
18562    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
18563        let source = r#"
18564#define SIMPLECPP_LIB
18565namespace {
18566namespace simplecpp {
18567using TokenString = std::string;
18568struct Location { int line{}; };
18569class SIMPLECPP_LIB HiddenToken {
18570 public:
18571  HiddenToken(const TokenString &s, const Location &loc) :
18572      location(loc), string(s) {
18573      flags();
18574  }
18575  TokenString string;
18576  Location location;
18577  HiddenToken *previous{};
18578 private:
18579  void flags() {}
18580};
18581}
18582}
18583"#;
18584        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
18585        let constructor = parsed
18586            .declarations()
18587            .iter()
18588            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
18589            .expect("recovered anonymous-namespace constructor");
18590        assert_eq!(
18591            parsed
18592                .signature_metadata
18593                .get(constructor)
18594                .and_then(|metadata| metadata.first())
18595                .and_then(SignatureMetadata::callable_linkage),
18596            Some(CallableLinkage::Internal)
18597        );
18598    }
18599
18600    #[test]
18601    fn macro_qualified_static_field_keeps_real_declarator() {
18602        let source = r#"#define JSON_INLINE_VARIABLE
18603struct Reader {
18604static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
18605static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
18606static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
18607};"#;
18608        let mut parser = tree_sitter::Parser::new();
18609        parser
18610            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18611            .unwrap();
18612        let tree = parser.parse(source, None).unwrap();
18613        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
18614        let parsed = parse_cpp_file(&file, source, &tree);
18615        for expected in [
18616            "Reader.npos",
18617            "Reader.other",
18618            "Reader.pointer",
18619            "Reader.reference",
18620        ] {
18621            assert!(
18622                parsed
18623                    .declarations()
18624                    .iter()
18625                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
18626                "real macro-decorated field {expected} is missing: {:#?}",
18627                parsed.declarations()
18628            );
18629        }
18630        assert!(
18631            parsed
18632                .declarations()
18633                .iter()
18634                .all(|unit| unit.fq_name() != "Reader.std"),
18635            "qualified type prefix became a pseudo-field: {:#?}",
18636            parsed.declarations()
18637        );
18638        let root = tree.root_node();
18639        let mut stack = vec![root];
18640        let mut signatures = Vec::new();
18641        while let Some(current) = stack.pop() {
18642            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
18643            {
18644                signatures.extend(
18645                    declarators
18646                        .into_iter()
18647                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
18648                );
18649            }
18650            let mut cursor = current.walk();
18651            stack.extend(current.named_children(&mut cursor));
18652        }
18653        signatures.sort();
18654        assert_eq!(
18655            signatures,
18656            [
18657                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
18658                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
18659                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
18660                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
18661            ]
18662        );
18663    }
18664
18665    fn member_function_linkage(source: &str) -> CallableLinkage {
18666        let mut parser = tree_sitter::Parser::new();
18667        parser
18668            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18669            .unwrap();
18670        let tree = parser.parse(source, None).unwrap();
18671        let ancestry = ParentIndex::new(tree.root_node());
18672        let mut stack = vec![tree.root_node()];
18673        while let Some(node) = stack.pop() {
18674            if node.kind() == "function_definition" {
18675                let mut current = node.parent();
18676                while let Some(parent) = current {
18677                    if matches!(
18678                        parent.kind(),
18679                        "class_specifier" | "struct_specifier" | "union_specifier"
18680                    ) {
18681                        return cpp_callable_linkage(node, source, &ancestry);
18682                    }
18683                    current = parent.parent();
18684                }
18685            }
18686            let mut cursor = node.walk();
18687            stack.extend(node.named_children(&mut cursor));
18688        }
18689        panic!("fixture has no member function definition");
18690    }
18691
18692    #[test]
18693    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
18694        assert_eq!(
18695            member_function_linkage("struct Named { int method() { return 1; } };"),
18696            CallableLinkage::External
18697        );
18698        assert_eq!(
18699            member_function_linkage(
18700                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
18701            ),
18702            CallableLinkage::Internal
18703        );
18704        assert_eq!(
18705            member_function_linkage("struct { int method() { return 1; } } instance;"),
18706            CallableLinkage::Internal
18707        );
18708        assert_eq!(
18709            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
18710            CallableLinkage::Internal
18711        );
18712    }
18713
18714    #[test]
18715    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
18716        let source = r#"
18717#ifndef PROTON_VALUE_HPP
18718#define PROTON_VALUE_HPP
18719namespace proton {
18720namespace internal {
18721class value_base {
18722  protected:
18723    internal::data& data();
18724    internal::data data_;
18725  friend class codec::encoder;
18726  friend class codec::decoder;
18727};
18728}
18729class value : public internal::value_base, private internal::comparable<value> {
18730  private:
18731    template<class T, class U=void> struct assignable :
18732        public std::enable_if<codec::is_encodable<T>::value, U> {};
18733    template<class U> struct assignable<value, U> {};
18734  public:
18735    PN_CPP_EXTERN value();
18736    PN_CPP_EXTERN value(const value&);
18737    PN_CPP_EXTERN value& operator=(const value&);
18738    PN_CPP_EXTERN value(value&&);
18739    PN_CPP_EXTERN value& operator=(value&&);
18740    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
18741    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
18742        codec::encoder e(*this);
18743        e << x;
18744        return *this;
18745    }
18746    PN_CPP_EXTERN type_id type() const;
18747    PN_CPP_EXTERN bool empty() const;
18748    PN_CPP_EXTERN void clear();
18749    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
18750    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
18751  friend PN_CPP_EXTERN void swap(value&, value&);
18752  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
18753  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
18754  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
18755    value(pn_data_t* d);
18756    void reset(pn_data_t* d = 0);
18757};
18758}
18759#endif
18760"#;
18761        let mut parser = tree_sitter::Parser::new();
18762        parser
18763            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18764            .unwrap();
18765        let tree = parser.parse(source, None).unwrap();
18766        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
18767        let parsed = parse_cpp_file(&file, source, &tree);
18768        let macro_constructors = parsed
18769            .signature_metadata
18770            .iter()
18771            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
18772            .flat_map(|(_, metadata)| metadata)
18773            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
18774            .collect::<Vec<_>>();
18775
18776        assert_eq!(
18777            macro_constructors.len(),
18778            3,
18779            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
18780            parsed.declarations()
18781        );
18782        assert!(
18783            macro_constructors.iter().all(|metadata| {
18784                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
18785            }),
18786            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
18787        );
18788    }
18789
18790    #[test]
18791    fn recovered_export_class_typedef_uses_displaced_alias_name() {
18792        let source = r#"
18793namespace spi {
18794class Filter {
18795public:
18796    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
18797};
18798}
18799namespace filter {
18800class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
18801{
18802public:
18803    typedef spi::Filter BASE_CLASS;
18804    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
18805    BEGIN_LOG4CXX_CAST_MAP()
18806    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
18807    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
18808    END_LOG4CXX_CAST_MAP()
18809    FilterDecision decide() const;
18810};
18811}
18812"#;
18813        let mut parser = tree_sitter::Parser::new();
18814        parser
18815            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18816            .unwrap();
18817        let tree = parser.parse(source, None).unwrap();
18818        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
18819        let parsed = parse_cpp_file(&file, source, &tree);
18820        assert!(
18821            parsed.declarations().iter().any(|unit| {
18822                unit.is_class()
18823                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
18824                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
18825            }),
18826            "the displaced typedef alias must retain its declared name: {:#?}",
18827            parsed.declarations()
18828        );
18829        assert!(
18830            parsed
18831                .declarations()
18832                .iter()
18833                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
18834            "the qualified underlying type must not become a false nested alias: {:#?}",
18835            parsed.declarations()
18836        );
18837    }
18838
18839    #[test]
18840    fn exported_single_base_recovery_uses_displaced_class_name() {
18841        let source = r#"
18842class CORE_EXPORT QgsPoint : public AbstractGeometry
18843{
18844    Q_GADGET
18845
18846    Q_PROPERTY( double x READ x WRITE setX )
18847    Q_PROPERTY( double y READ y WRITE setY )
18848    Q_PROPERTY( double z READ z WRITE setZ )
18849    Q_PROPERTY( double m READ m WRITE setM )
18850
18851  public:
18852#ifndef SIP_RUN
18853    QgsPoint(
18854      double x = std::numeric_limits<double>::quiet_NaN(),
18855      double y = std::numeric_limits<double>::quiet_NaN(),
18856      double z = std::numeric_limits<double>::quiet_NaN(),
18857      double m = std::numeric_limits<double>::quiet_NaN(),
18858      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
18859    );
18860#else
18861    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 )];
18862    % MethodCode
18863    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
18864    {
18865      int state;
18866      sipIsErr = 0;
18867      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
18868      if ( !sipIsErr )
18869      {
18870        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
18871      }
18872      sipReleaseType( p, sipType_QgsPointXY, state );
18873    }
18874    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
18875    {
18876      int state;
18877      sipIsErr = 0;
18878
18879      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
18880      if ( !sipIsErr )
18881      {
18882        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
18883      }
18884      sipReleaseType( p, sipType_QPointF, state );
18885    }
18886    else if (
18887      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
18888      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
18889      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
18890      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
18891    {
18892      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
18893      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
18894      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
18895      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
18896      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
18897      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
18898    }
18899    else // Invalid ctor arguments
18900    {
18901      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
18902      sipIsErr = 1;
18903    }
18904    % End
18905#endif
18906
18907    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
18908    explicit QgsPoint( QPointF p ) SIP_SKIP;
18909    explicit QgsPoint(
18910      Qgis::WkbType wkbType,
18911      double x = std::numeric_limits<double>::quiet_NaN(),
18912      double y = std::numeric_limits<double>::quiet_NaN(),
18913      double z = std::numeric_limits<double>::quiet_NaN(),
18914      double m = std::numeric_limits<double>::quiet_NaN()
18915    ) SIP_SKIP;
18916    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
18917    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
18918    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
18919#ifndef SIP_RUN
18920  private:
18921    bool fuzzyHelper(
18922      double epsilon,
18923      const AbstractGeometry &other,
18924      bool is3DFlag,
18925      bool isMeasureFlag
18926    ) const
18927    {
18928      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
18929    }
18930#endif
18931};
18932class Ordinary : public Base { public: Ordinary(); };
18933class API_EXPORT Plain { public: Plain(); };
18934class API_EXPORT : public Base {};
18935class
18936PN_CPP_CLASS_EXTERN Sender : public Link {
18937    Sender();
18938    struct impl;
18939    struct impl& get_impl() const;
18940};
18941class thread_ctx_t {};
18942class ctx_t ZMQ_FINAL : public thread_ctx_t {
18943    bool start();
18944};
18945"#;
18946        let mut parser = tree_sitter::Parser::new();
18947        parser
18948            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18949            .unwrap();
18950        let tree = parser.parse(source, None).unwrap();
18951        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
18952        let parsed = parse_cpp_file(&file, source, &tree);
18953        let declarations = parsed.declarations();
18954
18955        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
18956            assert!(
18957                declarations
18958                    .iter()
18959                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
18960                "missing recovered class {expected}: {declarations:#?}"
18961            );
18962        }
18963        let qgs_point = declarations
18964            .iter()
18965            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
18966            .expect("recovered QgsPoint class");
18967        assert_eq!(
18968            parsed.raw_supertypes.get(qgs_point),
18969            Some(&vec!["AbstractGeometry".to_string()]),
18970            "single-base export recovery must retain its displaced base"
18971        );
18972        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
18973        assert!(
18974            parsed
18975                .navigation_ranges
18976                .get(qgs_point)
18977                .is_some_and(|ranges| {
18978                    !ranges.is_empty()
18979                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
18980                }),
18981            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
18982            parsed.navigation_ranges.get(qgs_point)
18983        );
18984        let sender = declarations
18985            .iter()
18986            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
18987            .expect("recovered Sender class");
18988        assert_eq!(
18989            parsed.raw_supertypes.get(sender),
18990            Some(&vec!["Link".to_string()]),
18991            "post-declarator export recovery must retain its displaced base"
18992        );
18993        let recovered_member = declarations
18994            .iter()
18995            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
18996            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
18997        assert_eq!(
18998            parsed
18999                .signature_metadata
19000                .get(recovered_member)
19001                .and_then(|metadata| metadata.first())
19002                .and_then(SignatureMetadata::callable_linkage),
19003            Some(CallableLinkage::External),
19004            "a named recovered class's members have external linkage"
19005        );
19006        let ctx = declarations
19007            .iter()
19008            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
19009            .expect("recovered ctx_t class");
19010        assert_eq!(
19011            parsed.raw_supertypes.get(ctx),
19012            Some(&vec!["thread_ctx_t".to_string()]),
19013            "postfix export-macro recovery must retain its displaced base"
19014        );
19015        assert!(
19016            declarations.iter().any(|unit| {
19017                unit.is_function()
19018                    && unit.fq_name() == "QgsPoint.QgsPoint"
19019                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
19020            }),
19021            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
19022        );
19023        assert!(
19024            declarations.iter().all(|unit| {
19025                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
19026            }),
19027            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
19028        );
19029    }
19030
19031    #[test]
19032    fn function_like_export_macro_classes_keep_names_and_base_edges() {
19033        // Every sibling shape tree-sitter produces after the `class MACRO(2, 0)`
19034        // error: a plain body, a single base, `final` without a base, `final`
19035        // with one base, and `final` with a comma-separated base list.
19036        let source = r#"
19037namespace api {
19038class PROJECT_PUBLIC_API(2, 0) Prelude {
19039  public:
19040    Prelude();
19041};
19042class PROJECT_PUBLIC_API(2, 0) Base {
19043  public:
19044    Base(int value);
19045};
19046class PROJECT_PUBLIC_API(2, 0) Mixin {
19047  public:
19048    Mixin();
19049};
19050class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
19051  public:
19052    Adopted(int value);
19053};
19054class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
19055  public:
19056    Derived(int value);
19057};
19058class PROJECT_PUBLIC_API(2, 0) Solo final {
19059  public:
19060    Solo();
19061};
19062class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
19063  public:
19064    Blended(int value);
19065};
19066class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
19067  public:
19068    Woven(int value);
19069};
19070} // namespace api
19071"#;
19072        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
19073        let declarations = parsed.declarations();
19074        let class_named = |name: &str| {
19075            declarations
19076                .iter()
19077                .find(|unit| unit.is_class() && unit.fq_name() == name)
19078                .unwrap_or_else(|| {
19079                    panic!("missing function-like export macro class {name}: {declarations:#?}")
19080                })
19081        };
19082        let base = class_named("api.Base");
19083        class_named("api.Prelude");
19084        class_named("api.Mixin");
19085
19086        assert_eq!(
19087            parsed.raw_supertypes.get(class_named("api.Adopted")),
19088            Some(&vec!["Base".to_string()])
19089        );
19090        assert_eq!(
19091            parsed.raw_supertypes.get(class_named("api.Derived")),
19092            Some(&vec!["Base".to_string()])
19093        );
19094        assert_eq!(
19095            parsed.raw_supertypes.get(class_named("api.Solo")),
19096            None,
19097            "a final class without a base list must not invent a supertype"
19098        );
19099        assert_eq!(
19100            parsed.raw_supertypes.get(class_named("api.Blended")),
19101            Some(&vec!["Base".to_string(), "Mixin".to_string()])
19102        );
19103        assert_eq!(
19104            parsed.raw_supertypes.get(class_named("api.Woven")),
19105            Some(&vec!["Base".to_string(), "Mixin".to_string()])
19106        );
19107        assert!(
19108            declarations
19109                .iter()
19110                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19111            "the export macro must not become a declaration: {declarations:#?}"
19112        );
19113        assert!(
19114            declarations.iter().all(|unit| !matches!(
19115                unit.identifier(),
19116                "final" | "public" | "protected" | "private"
19117            )),
19118            "the head specifiers must not become declarations: {declarations:#?}"
19119        );
19120        assert!(
19121            parsed
19122                .navigation_ranges
19123                .get(base)
19124                .is_some_and(|ranges| !ranges.is_empty()),
19125            "the recovered base must retain a navigable declaration range"
19126        );
19127    }
19128
19129    #[test]
19130    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
19131        // #2557: the class name is the last identifier before the head ends
19132        // (`final`, the base clause `:`, or the body), whatever its spelling.
19133        // A class named in capitals (`X509_CA`) is a class, and an object-like
19134        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
19135        // the control whose spelling never mattered.
19136        let source = r#"
19137namespace api {
19138class PROJECT_PUBLIC_API(2, 0) Base {
19139  public:
19140    Base();
19141};
19142class PROJECT_PUBLIC_API(2, 0) Mixin {
19143  public:
19144    Mixin();
19145};
19146class PROJECT_PUBLIC_API(2, 0) Name {
19147  public:
19148    Name();
19149};
19150class PROJECT_PUBLIC_API(2, 0) X509_CA final {
19151  public:
19152    X509_CA();
19153};
19154class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
19155  public:
19156    HSS_LMS_KEY();
19157};
19158class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
19159  public:
19160    GOST_3410();
19161};
19162class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
19163  public:
19164    PKCS11_RSA();
19165};
19166class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
19167  public:
19168    Plain();
19169};
19170class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
19171  public:
19172    Decorated();
19173};
19174class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
19175  public:
19176    Layered();
19177};
19178} // namespace api
19179"#;
19180        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
19181        let declarations = parsed.declarations();
19182        let class_named = |name: &str| {
19183            declarations
19184                .iter()
19185                .find(|unit| unit.is_class() && unit.fq_name() == name)
19186                .unwrap_or_else(|| {
19187                    panic!("missing function-like export macro class {name}: {declarations:#?}")
19188                })
19189        };
19190        for (name, bases) in [
19191            ("api.Name", None),
19192            ("api.X509_CA", None),
19193            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
19194            ("api.GOST_3410", Some(vec!["Base"])),
19195            ("api.PKCS11_RSA", None),
19196            ("api.Plain", None),
19197            ("api.Decorated", Some(vec!["Base"])),
19198            ("api.Layered", Some(vec!["Base", "Mixin"])),
19199        ] {
19200            let expected =
19201                bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
19202            assert_eq!(
19203                parsed.raw_supertypes.get(class_named(name)),
19204                expected.as_ref(),
19205                "{name}"
19206            );
19207        }
19208        assert!(
19209            declarations.iter().all(|unit| !matches!(
19210                unit.identifier(),
19211                "PROJECT_PUBLIC_API"
19212                    | "OTHER_MACRO"
19213                    | "FIRST_MACRO"
19214                    | "SECOND_MACRO"
19215                    | "final"
19216                    | "public"
19217            )),
19218            "macros and head specifiers must not become declarations: {declarations:#?}"
19219        );
19220    }
19221
19222    #[test]
19223    fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
19224        // #2924: `class MACRO(2, 0) Name : public virtual Base {` leaves the
19225        // head, the body's `{` and its first member in one declaration-scope
19226        // ERROR, scatters the remaining members across the container's
19227        // siblings, and ends the container on the class's own `}`. The class
19228        // was lost and the macro minted a class of its own.
19229        let source = r#"
19230namespace api {
19231
19232/**
19233* Doc comment
19234*/
19235class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
19236   public:
19237      /**
19238      * Construct from a point.
19239      */
19240      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
19241
19242#if defined(PROJECT_HAS_LEGACY_POINT)
19243      /**
19244      * Construct from a legacy point.
19245      */
19246      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
19247#endif
19248
19249      std::string algo_name() const override;
19250
19251      AlgorithmIdentifier algorithm_identifier() const override;
19252};
19253
19254}
19255"#;
19256        let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
19257        let declarations = parsed.declarations();
19258        let class = declarations
19259            .iter()
19260            .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
19261            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19262        assert_eq!(
19263            parsed.raw_supertypes.get(class),
19264            Some(&vec!["BaseKey".to_string()]),
19265            "the virtual base is the class's base: {declarations:#?}"
19266        );
19267        for member in [
19268            "api.VirtualBased.algo_name",
19269            "api.VirtualBased.algorithm_identifier",
19270        ] {
19271            assert!(
19272                declarations
19273                    .iter()
19274                    .any(|unit| unit.is_function() && unit.fq_name() == member),
19275                "{member} must be owned by the recovered class: {declarations:#?}"
19276            );
19277        }
19278        assert!(
19279            declarations
19280                .iter()
19281                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19282            "an unrecovered head must not mint a macro-named class: {declarations:#?}"
19283        );
19284    }
19285
19286    #[test]
19287    fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
19288        // #2924: object-like macro lines before the head demote the `class`
19289        // keyword to a bare identifier and make the macro invocation an
19290        // `init_declarator`, so the head arrives as a `declaration` rather than
19291        // as an ERROR carrying a `class_specifier`. The name is still the last
19292        // identifier before `final`, and `virtual` is a base specifier rather
19293        // than a base.
19294        let source = r#"
19295namespace api {
19296
19297DIAGNOSTIC_PUSH
19298DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
19299
19300class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
19301                                               public virtual api::Inner::Key {
19302   public:
19303      std::string algo_name() const override;
19304};
19305
19306DIAGNOSTIC_POP
19307
19308}
19309"#;
19310        let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
19311        let declarations = parsed.declarations();
19312        let class = declarations
19313            .iter()
19314            .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
19315            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19316        assert_eq!(
19317            parsed.raw_supertypes.get(class),
19318            Some(&vec![
19319                "api::Outer::Key".to_string(),
19320                "api::Inner::Key".to_string()
19321            ]),
19322            "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
19323        );
19324        assert!(
19325            declarations
19326                .iter()
19327                .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
19328            "the member is owned by the recovered class: {declarations:#?}"
19329        );
19330        assert!(
19331            declarations
19332                .iter()
19333                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19334            "the macro invocation must not mint a declaration: {declarations:#?}"
19335        );
19336    }
19337
19338    #[test]
19339    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
19340        // The class embedded in a preceding malformed body follows the same
19341        // rule (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration.
19342        let fixture = |head: &str, name: &str| {
19343            format!(
19344                r#"
19345namespace api {{
19346class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
19347   public:
19348      /** Return a descriptive string. */
19349      const char* what() const noexcept override {{ return m_msg.c_str(); }}
19350
19351      /** Return the type of error. */
19352      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
19353
19354      /** Return an associated error code. */
19355      virtual int error_code() const noexcept {{ return 0; }}
19356
19357      /** Avoid throwing the base directly. */
19358      explicit Exception(std::string_view msg);
19359
19360      /** Avoid throwing the base directly. */
19361      Exception(const char* prefix, std::string_view msg);
19362
19363      /** Avoid throwing the base directly. */
19364      Exception(std::string_view msg, const std::exception& e);
19365
19366   private:
19367      std::string m_msg;
19368}};
19369
19370class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
19371   public:
19372      explicit {name}(std::string_view msg);
19373
19374      explicit {name}(std::string_view msg, std::string_view where);
19375
19376      {name}(std::string_view msg, const std::exception& e);
19377
19378      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
19379}};
19380}} // namespace api
19381"#
19382            )
19383        };
19384        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
19385            let source = fixture(head, name);
19386            let mut parser = Parser::new();
19387            parser
19388                .set_language(&tree_sitter_cpp::LANGUAGE.into())
19389                .expect("set C++ grammar");
19390            let tree = parser.parse(&source, None).expect("parse fixture");
19391            let mut embedded = Vec::new();
19392            let mut stack = vec![tree.root_node()];
19393            while let Some(node) = stack.pop() {
19394                embedded.extend(
19395                    recover_embedded_function_like_export_classes(node, &source)
19396                        .into_iter()
19397                        .map(|recovered| (recovered.name, recovered.raw_supertypes)),
19398                );
19399                let mut cursor = node.walk();
19400                stack.extend(node.named_children(&mut cursor));
19401            }
19402            assert!(
19403                embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
19404                "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
19405                tree.root_node().to_sexp()
19406            );
19407            assert!(
19408                embedded
19409                    .iter()
19410                    .all(|(recovered, _)| recovered != "OTHER_MACRO"),
19411                "{head}: the object-like macro is not a class: {embedded:#?}"
19412            );
19413
19414            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
19415            let declarations = parsed.declarations();
19416            let class = declarations
19417                .iter()
19418                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
19419                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
19420            assert_eq!(
19421                parsed.raw_supertypes.get(class),
19422                Some(&vec!["Exception".to_string()]),
19423                "{head}"
19424            );
19425            assert!(
19426                declarations
19427                    .iter()
19428                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
19429                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
19430            );
19431        }
19432    }
19433
19434    #[test]
19435    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
19436        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar keeps
19437        // `class MACRO(3, 6) Name final :` in the error, the first base as the
19438        // `type` of the following declaration, and closes that declaration with
19439        // a zero-width `MISSING identifier`. Reading the head by position must
19440        // not take that missing node for the class name: an empty name panics
19441        // at `FqName` construction, which took the Botan corpus replay down
19442        // (#2557). The head's tokens straddle the two nodes, so the positional
19443        // read spans both and recovers the class rather than leaving the
19444        // bodyless `class PROJECT_PUBLIC_API` specifier behind (#2924).
19445        let source = r#"
19446namespace api {
19447class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
19448                                                    public virtual Botan::EC_PublicKey {
19449   public:
19450      std::string algo_name() const override { return "ECDSA"; }
19451};
19452} // namespace api
19453"#;
19454        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
19455        let declarations = parsed.declarations();
19456        assert!(
19457            declarations
19458                .iter()
19459                .all(|unit| !unit.identifier().is_empty()),
19460            "no declaration may carry an empty name: {declarations:#?}"
19461        );
19462        assert!(
19463            declarations
19464                .iter()
19465                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
19466            "macros and head specifiers must not become declarations: {declarations:#?}"
19467        );
19468        let class = declarations
19469            .iter()
19470            .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
19471            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19472        assert_eq!(
19473            parsed.raw_supertypes.get(class),
19474            Some(&vec![
19475                "Botan::TPM2::PublicKey".to_string(),
19476                "Botan::EC_PublicKey".to_string()
19477            ]),
19478            "both qualified virtual bases are bases: {declarations:#?}"
19479        );
19480        assert!(
19481            declarations
19482                .iter()
19483                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19484            "the head must not mint a macro-named class: {declarations:#?}"
19485        );
19486    }
19487
19488    #[test]
19489    fn function_like_export_class_survives_a_preceding_malformed_body() {
19490        let source = r#"
19491namespace api {
19492class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
19493   public:
19494      /** Return a descriptive string. */
19495      const char* what() const noexcept override { return m_msg.c_str(); }
19496
19497      /** Return the type of error. */
19498      virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
19499
19500      /** Return an associated error code. */
19501      virtual int error_code() const noexcept { return 0; }
19502
19503      /** Avoid throwing the base directly. */
19504      explicit Exception(std::string_view msg);
19505
19506      /** Avoid throwing the base directly. */
19507      Exception(const char* prefix, std::string_view msg);
19508
19509      /** Avoid throwing the base directly. */
19510      Exception(std::string_view msg, const std::exception& e);
19511
19512   private:
19513      std::string m_msg;
19514};
19515
19516class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
19517   public:
19518      explicit Invalid_Argument(std::string_view msg);
19519
19520      explicit Invalid_Argument(std::string_view msg, std::string_view where);
19521
19522      Invalid_Argument(std::string_view msg, const std::exception& e);
19523
19524      ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
19525};
19526} // namespace api
19527"#;
19528        let mut parser = Parser::new();
19529        parser
19530            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19531            .expect("set C++ grammar");
19532        let tree = parser.parse(source, None).expect("parse fixture");
19533        let mut stack = vec![tree.root_node()];
19534        let mut saw_embedded_shape = false;
19535        while let Some(node) = stack.pop() {
19536            saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
19537                .iter()
19538                .any(|recovered| recovered.name == "Invalid_Argument");
19539            let mut cursor = node.walk();
19540            stack.extend(node.named_children(&mut cursor));
19541        }
19542        assert!(
19543            saw_embedded_shape,
19544            "fixture must retain the embedded error geometry: {}",
19545            tree.root_node().to_sexp()
19546        );
19547
19548        let parsed = parse_cpp_file(
19549            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
19550            source,
19551            &tree,
19552        );
19553        let declarations = parsed.declarations();
19554        let exception = declarations
19555            .iter()
19556            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
19557            .expect("qualified-base export class");
19558        let invalid = declarations
19559            .iter()
19560            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
19561            .expect("class embedded in the preceding malformed body");
19562
19563        assert_eq!(
19564            parsed.raw_supertypes.get(exception),
19565            Some(&vec!["std::exception".to_string()])
19566        );
19567        assert_eq!(
19568            parsed.raw_supertypes.get(invalid),
19569            Some(&vec!["Exception".to_string()])
19570        );
19571        assert!(
19572            parsed.materialization_records.iter().any(|record| matches!(
19573                record,
19574                MaterializationRecord::RecoveredDeclaration { unit, .. }
19575                    if unit == invalid
19576            )),
19577            "the embedded class must retain recovery provenance: {:#?}",
19578            parsed.materialization_records
19579        );
19580    }
19581
19582    #[test]
19583    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
19584        let source = r#"
19585public:
19586   explicit Lookup_Error(std::string_view err) : Exception(err) {}
19587
19588   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
19589"#;
19590        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
19591            .expect("reparse merged constructor body");
19592        let (range, body) =
19593            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
19594                .unwrap_or_else(|| {
19595                    panic!(
19596                        "the merged constructor must retain its structured declarator/body: {}",
19597                        tree.root_node().to_sexp()
19598                    )
19599                });
19600        assert_eq!(
19601            source.get(range).expect("constructor range"),
19602            "Lookup_Error(std::string_view err) : Exception(err) {}"
19603        );
19604        assert_eq!(node_text(body, source), "{}");
19605    }
19606
19607    #[test]
19608    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
19609        let positive_source =
19610            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
19611        let mut parser = tree_sitter::Parser::new();
19612        parser
19613            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19614            .unwrap();
19615        let positive_tree = parser.parse(positive_source, None).unwrap();
19616        assert!(cpp_reparsed_members_are_indexable(
19617            positive_tree.root_node(),
19618            positive_source
19619        ));
19620
19621        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
19622        let negative_tree = parser.parse(negative_source, None).unwrap();
19623        assert!(!cpp_reparsed_members_are_indexable(
19624            negative_tree.root_node(),
19625            negative_source
19626        ));
19627    }
19628
19629    #[test]
19630    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
19631        let copy_control_source = r#"
19632public:
19633    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
19634    explicit Token(const Token* tok);
19635    ~Token();
19636    Token* astOperand1() { return nullptr; }
19637"#;
19638        let constraint_source = r#"
19639private:
19640    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19641    static T *tokAtImpl(T *tok, int index) {
19642        return tok;
19643    }
19644
19645    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19646    static T *linkAtImpl(T *tok, int index) {
19647        return tok;
19648    }
19649
19650public:
19651    int late() const { return 1; }
19652"#;
19653        let mut parser = tree_sitter::Parser::new();
19654        parser
19655            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19656            .unwrap();
19657        let copy_control_tree = parser
19658            .parse(copy_control_source, None)
19659            .expect("parse copy-control fixture");
19660        assert!(
19661            copy_control_tree.root_node().has_error(),
19662            "fixture must exercise adjacent copy-control recovery"
19663        );
19664        assert!(
19665            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
19666            "a complete late getter must remain recoverable after adjacent copy-control declarations"
19667        );
19668        let mut cursor = copy_control_tree.root_node().walk();
19669        assert!(
19670            copy_control_tree
19671                .root_node()
19672                .named_children(&mut cursor)
19673                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
19674            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
19675            copy_control_tree.root_node().to_sexp()
19676        );
19677        let constraint_tree = parser
19678            .parse(constraint_source, None)
19679            .expect("parse constraint-macro fixture");
19680        assert!(constraint_tree.root_node().has_error());
19681        assert!(
19682            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
19683            "complete constraint-macro members must not hide a later ordinary member"
19684        );
19685        let mut cursor = constraint_tree.root_node().walk();
19686        assert!(
19687            constraint_tree
19688                .root_node()
19689                .named_children(&mut cursor)
19690                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
19691                    child,
19692                    constraint_source
19693                )),
19694            "fixture must retain the split constraint-macro prefix/function geometry"
19695        );
19696    }
19697
19698    #[test]
19699    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
19700        let source = r#"
19701struct Analyzer {
19702    struct Action {
19703        Action() = default;
19704        Action(const Action&) = default;
19705        Action& operator=(const Action& rhs) & = default;
19706
19707        template<class T,
19708                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
19709                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
19710        // NOLINTNEXTLINE(google-explicit-constructor)
19711        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
19712        {}
19713
19714        enum : std::uint16_t { None = 0, Read = (1 << 0) };
19715        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
19716
19717    private:
19718        unsigned int mFlag{};
19719    };
19720
19721    enum class Direction : unsigned char { Forward, Reverse };
19722    virtual Action analyze(Direction d) const = 0;
19723};
19724"#;
19725        let mut parser = tree_sitter::Parser::new();
19726        parser
19727            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19728            .unwrap();
19729        let tree = parser.parse(source, None).unwrap();
19730        assert!(tree.root_node().has_error());
19731        let root = tree.root_node();
19732        let outer = root
19733            .named_children(&mut root.walk())
19734            .find(|child| child.kind() == "ERROR")
19735            .expect("fragmented Analyzer prefix");
19736        let outer_recovered =
19737            fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
19738        assert_eq!(outer_recovered.name, "Analyzer");
19739        let outer_tree = cpp_reparse_fragmented_class_body(
19740            source,
19741            outer_recovered.body.reparse_start,
19742            outer_recovered.body.reparse_end,
19743        )
19744        .expect("reparse Analyzer body");
19745        let outer_root = outer_tree.root_node();
19746        let action_prefix = outer_root
19747            .named_children(&mut outer_root.walk())
19748            .find(|child| child.kind() == "ERROR")
19749            .expect("fragmented Action prefix");
19750        let action_recovered = fragmented_class_body(action_prefix, source)
19751            .expect("structured Action fragment boundary");
19752        assert_eq!(action_recovered.name, "Action");
19753        let action_tree = cpp_reparse_fragmented_class_body(
19754            source,
19755            action_recovered.body.reparse_start,
19756            action_recovered.body.reparse_end,
19757        )
19758        .expect("reparse Action body");
19759        let action_root = action_tree.root_node();
19760        let macro_prefix = action_root
19761            .named_children(&mut action_root.walk())
19762            .find(|child| child.kind() == "ERROR")
19763            .expect("constraint macro prefix");
19764        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
19765            .expect("structured template macro prefix");
19766        let macro_companion =
19767            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
19768        assert!(
19769            cpp_reparsed_template_macro_constructor_companion_is_indexable(
19770                macro_companion,
19771                macro_parameter,
19772                source,
19773            ),
19774            "split constrained constructor must be admitted: {}",
19775            macro_companion.to_sexp()
19776        );
19777        assert!(
19778            cpp_reparsed_members_are_indexable(action_root, source),
19779            "complete Action body must pass the recovery gate: {}",
19780            action_tree.root_node().to_sexp()
19781        );
19782        assert!(
19783            cpp_reparsed_members_are_indexable(outer_root, source),
19784            "complete Analyzer body must pass the recovery gate: {}",
19785            outer_tree.root_node().to_sexp()
19786        );
19787        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
19788        let parsed = parse_cpp_file(&file, source, &tree);
19789        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
19790            assert!(
19791                parsed
19792                    .declarations()
19793                    .iter()
19794                    .any(|unit| unit.fq_name() == expected),
19795                "missing recovered declaration {expected}: {:#?}",
19796                parsed.declarations()
19797            );
19798        }
19799        assert!(
19800            parsed
19801                .declarations()
19802                .iter()
19803                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
19804            "nested members must not remain flattened: {:#?}",
19805            parsed.declarations()
19806        );
19807    }
19808
19809    #[test]
19810    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
19811        let source = r#"
19812raw_hash_set& operator=(raw_hash_set&& that) {
19813  return move_assign(
19814      std::move(that),
19815      typename AllocTraits::propagate_on_container_move_assignment());
19816}
19817
19818iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
19819  return {};
19820}
19821
19822void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
19823
19824iterator insert(const_iterator hint, value_type&& value)
19825    ABSL_ATTRIBUTE_LIFETIME_BOUND {
19826  return {};
19827}
19828
19829friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
19830  return left.size() == right.size();
19831}
19832
19833static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
19834  return static_cast<slot_type*>(buffer);
19835}
19836
19837protected:
19838// Included-range recovery can attach this comment to the template prefix.
19839template <class K>
19840void AssertOnFind([[maybe_unused]] const K& key) {
19841  Check(key);
19842}
19843"#;
19844        let mut parser = tree_sitter::Parser::new();
19845        parser
19846            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19847            .unwrap();
19848        let tree = parser.parse(source, None).unwrap();
19849        assert!(
19850            tree.root_node().has_error(),
19851            "the fixture must exercise tree-sitter's errorful member shapes"
19852        );
19853        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
19854
19855        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
19856        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
19857        assert!(!cpp_reparsed_members_are_indexable(
19858            incomplete_tree.root_node(),
19859            incomplete_source
19860        ));
19861
19862        let outside_error_source = "int foo() stray_attribute {}\n";
19863        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
19864        assert!(outside_error_tree.root_node().has_error());
19865        assert!(!cpp_reparsed_members_are_indexable(
19866            outside_error_tree.root_node(),
19867            outside_error_source
19868        ));
19869
19870        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
19871        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
19872        assert!(!cpp_reparsed_members_are_indexable(
19873            variable_initializer_tree.root_node(),
19874            variable_initializer_source
19875        ));
19876    }
19877
19878    #[test]
19879    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
19880        let positive_source = r#"
19881std::pair<iterator, bool> insert(init_type&& value)
19882    ABSL_ATTRIBUTE_LIFETIME_BOUND
19883#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
19884  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
19885#endif
19886{
19887  return emplace(std::move(value));
19888}
19889"#;
19890        let mut parser = tree_sitter::Parser::new();
19891        parser
19892            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19893            .unwrap();
19894        let positive_tree = parser.parse(positive_source, None).unwrap();
19895        assert!(
19896            positive_tree.root_node().has_error(),
19897            "the fixture must exercise the split attribute/requires shape"
19898        );
19899        assert!(cpp_reparsed_members_are_indexable(
19900            positive_tree.root_node(),
19901            positive_source
19902        ));
19903
19904        let template_return_source = r#"
19905pair<int> insert(init_type&& value)
19906    ABSL_ATTRIBUTE_LIFETIME_BOUND
19907#if LANGUAGE_LEVEL >= 202002L
19908  requires(!Predicate<init_type>::value)
19909#endif
19910// Attributes and the function body may be separated by comments.
19911{
19912  return {};
19913}
19914"#;
19915        let template_return_tree = parser.parse(template_return_source, None).unwrap();
19916        assert!(
19917            cpp_reparsed_members_are_indexable(
19918                template_return_tree.root_node(),
19919                template_return_source
19920            ),
19921            "template-return attribute/requires tree: {}",
19922            template_return_tree.root_node().to_sexp()
19923        );
19924
19925        let no_body_source = r#"
19926std::pair<iterator, bool> insert(init_type&& value)
19927    ABSL_ATTRIBUTE_LIFETIME_BOUND
19928#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
19929  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
19930#endif
19931+ 0;
19932"#;
19933        let no_body_tree = parser.parse(no_body_source, None).unwrap();
19934        assert!(!cpp_reparsed_members_are_indexable(
19935            no_body_tree.root_node(),
19936            no_body_source
19937        ));
19938
19939        let extra_payload_source = r#"
19940pair<int> insert(init_type&& value)
19941    ABSL_ATTRIBUTE_LIFETIME_BOUND
19942#if LANGUAGE_LEVEL >= 202002L
19943  int unrelated;
19944  requires(Predicate<init_type>::value)
19945#endif
19946{
19947  return {};
19948}
19949"#;
19950        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
19951        assert!(!cpp_reparsed_members_are_indexable(
19952            extra_payload_tree.root_node(),
19953            extra_payload_source
19954        ));
19955
19956        let variable_initializer_source = r#"
19957int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
19958#if LANGUAGE_LEVEL >= 202002L
19959  requires(true)
19960#endif
19961{
19962  bad;
19963}
19964"#;
19965        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
19966        assert!(!cpp_reparsed_members_are_indexable(
19967            variable_initializer_tree.root_node(),
19968            variable_initializer_source
19969        ));
19970    }
19971
19972    #[test]
19973    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
19974        let source = r#"namespace absl {
19975ABSL_NAMESPACE_BEGIN namespace container_internal {
19976
19977class raw_hash_set : public Base {
19978 public:
19979  using value_type = int;
19980
19981  template <class U,
19982            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
19983  void insert(U value) { (void)value; }
19984
19985  struct InsertSlot {
19986    raw_hash_set& s;
19987  };
19988};
19989
19990}
19991ABSL_NAMESPACE_END
19992}"#;
19993        let mut parser = tree_sitter::Parser::new();
19994        parser
19995            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19996            .unwrap();
19997        let tree = parser.parse(source, None).unwrap();
19998        let root = tree.root_node();
19999        let outer_namespace = root
20000            .named_children(&mut root.walk())
20001            .find(|child| child.kind() == "namespace_definition")
20002            .expect("outer absl namespace");
20003        let declaration_list = outer_namespace
20004            .child_by_field_name("body")
20005            .expect("outer namespace body");
20006        let sentinel_function = declaration_list
20007            .named_children(&mut declaration_list.walk())
20008            .find(|child| child.kind() == "function_definition")
20009            .expect("malformed namespace sentinel function");
20010        let ancestry = ParentIndex::new(root);
20011        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
20012            .expect("structured nested namespace sentinel");
20013        let fragmented =
20014            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
20015                .expect("fragmented raw_hash_set class");
20016        assert_eq!(fragmented.class_node.kind(), "ERROR");
20017        assert_eq!(fragmented.name, "raw_hash_set");
20018        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
20019
20020        let outer_scope =
20021            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
20022        let mut outer_siblings = Vec::new();
20023        push_cpp_sentinel_sibling_classes(
20024            &mut outer_siblings,
20025            declaration_list,
20026            sentinel.function,
20027            &outer_scope,
20028            source,
20029            &ancestry,
20030        );
20031        let [outer_shadow] = outer_siblings.as_slice() else {
20032            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
20033        };
20034        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
20035        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
20036
20037        let field = "    raw_hash_set& s;";
20038        let start = source.find(field).expect("InsertSlot field") + 4;
20039        let node = root
20040            .descendant_for_byte_range(start, start + "raw_hash_set".len())
20041            .expect("raw_hash_set type node");
20042        let recovered = cpp_sentinel_recovered_classes(root, source);
20043        let [deep_class] = recovered.as_slice() else {
20044            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
20045        };
20046        assert_eq!(
20047            deep_class.namespace_scope_components,
20048            vec!["absl", "container_internal"]
20049        );
20050        assert_eq!(
20051            deep_class.scope_components,
20052            vec!["absl", "container_internal", "raw_hash_set"]
20053        );
20054        assert!(
20055            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
20056                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
20057        );
20058
20059        assert_eq!(
20060            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
20061            Some(vec![
20062                "absl".to_string(),
20063                "container_internal".to_string(),
20064                "raw_hash_set".to_string(),
20065                "InsertSlot".to_string(),
20066            ])
20067        );
20068
20069        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
20070        let parsed = parse_cpp_file(&file, source, &tree);
20071        let raw_hash_set = parsed
20072            .declarations()
20073            .iter()
20074            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
20075            .expect("recovered raw_hash_set class");
20076        assert_eq!(
20077            raw_hash_set.fq_name(),
20078            "absl::container_internal.raw_hash_set",
20079            "the recovered declaration must publish under the deeper sentinel namespace"
20080        );
20081        assert_eq!(
20082            parsed.raw_supertypes.get(raw_hash_set),
20083            Some(&vec!["Base".to_string()]),
20084            "the structured base clause on the fragmented ERROR prefix must survive publication"
20085        );
20086        assert!(
20087            parsed.materialization_records.iter().any(|record| matches!(
20088                record,
20089                MaterializationRecord::RecoveredDeclaration { recovery, unit }
20090                    if unit == raw_hash_set && *recovery == deep_class.class_range
20091            )),
20092            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
20093            parsed.materialization_records
20094        );
20095    }
20096
20097    /// Issue #2358: recording an aggregate definition must not walk the whole
20098    /// file.
20099    ///
20100    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
20101    /// class-like shape that has a body, so the removal step runs once per
20102    /// aggregate. It used to `retain` over `top_level_declarations` and over
20103    /// *every* child list in the file on each of those calls, comparing whole
20104    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
20105    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
20106    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
20107    /// tens of thousands of times over and the C forward differential never
20108    /// finished.
20109    ///
20110    /// A definition the file has not already declared removes nothing, so the
20111    /// honest cost is zero regardless of how many other aggregates surround it.
20112    /// Two sizes an order of magnitude apart pin that the count is not merely
20113    /// small but independent of the file.
20114    ///
20115    /// The declaration walk answers every ancestor question from a
20116    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
20117    /// the root for each one (#2361). Substituting the index is only safe
20118    /// because it answers the identical question, so pin that on the shapes
20119    /// this file's recovery paths care about: anonymous and named aggregates,
20120    /// nested namespaces, templates, macro-displaced declarations and the
20121    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
20122    /// too -- `Node::parent` walks the visible tree, not the named one.
20123    #[test]
20124    fn the_parent_index_answers_what_tree_sitter_answers() {
20125        const SHAPES: [&str; 5] = [
20126            "namespace outer { namespace inner { struct Tag { int field; }; } }",
20127            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
20128            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
20129            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
20130            "class API Broken : public First, public Second {\n  void member();\n",
20131        ];
20132        for source in SHAPES {
20133            let mut parser = tree_sitter::Parser::new();
20134            parser
20135                .set_language(&tree_sitter_cpp::LANGUAGE.into())
20136                .unwrap();
20137            let tree = parser.parse(source, None).unwrap();
20138            let root = tree.root_node();
20139            let ancestry = ParentIndex::new(root);
20140            let mut nodes = 0usize;
20141            let mut stack = vec![root];
20142            while let Some(node) = stack.pop() {
20143                nodes += 1;
20144                assert_eq!(
20145                    node.parent().map(|parent| parent.id()),
20146                    ancestry.parent(node).map(|parent| parent.id()),
20147                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
20148                );
20149                let mut cursor = node.walk();
20150                stack.extend(node.children(&mut cursor));
20151            }
20152            assert!(nodes > 1, "{source:?} produced no tree to compare");
20153        }
20154    }
20155
20156    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
20157    /// index for every ancestor edge. Asking tree-sitter directly makes each
20158    /// edge re-descend from the root, turning declaration extraction on a
20159    /// deeply nested generated header from quadratic output work into a cubic
20160    /// tree walk. Exact query counts pin the route without a machine-dependent
20161    /// wall-clock ceiling.
20162    #[test]
20163    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
20164        const DEPTH: usize = 64;
20165        let mut source = String::new();
20166        for level in 0..DEPTH {
20167            writeln!(source, "namespace n{level} {{").unwrap();
20168        }
20169        source.push_str("int deepest(int value);\n");
20170        for _ in 0..DEPTH {
20171            source.push_str("}\n");
20172        }
20173
20174        let mut parser = tree_sitter::Parser::new();
20175        parser
20176            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20177            .unwrap();
20178        let tree = parser.parse(&source, None).unwrap();
20179        let root = tree.root_node();
20180        let ancestry = ParentIndex::new(root);
20181        let mut function_declarator = None;
20182        walk_named_tree_preorder(root, true, |node| {
20183            if node.kind() == "function_declarator" {
20184                function_declarator = Some(node);
20185                WalkControl::Break
20186            } else {
20187                WalkControl::Continue
20188            }
20189        });
20190        let function_declarator = function_declarator.expect("deepest function declarator");
20191        let ancestor_count =
20192            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
20193
20194        ancestry.reset_parent_query_count_for_test();
20195        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
20196        assert_eq!(DEPTH, lexical_scope.len());
20197        assert_eq!(
20198            ancestor_count + 1,
20199            ancestry.parent_query_count_for_test(),
20200            "lexical-scope ancestry bypassed the parent index"
20201        );
20202
20203        ancestry.reset_parent_query_count_for_test();
20204        assert_eq!(
20205            DispatchExtensibility::Closed,
20206            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
20207        );
20208        assert_eq!(
20209            ancestor_count,
20210            ancestry.parent_query_count_for_test(),
20211            "dispatch ancestry bypassed the parent index"
20212        );
20213
20214        ancestry.reset_parent_query_count_for_test();
20215        assert_eq!(
20216            CallableLinkage::External,
20217            cpp_callable_linkage(function_declarator, &source, &ancestry)
20218        );
20219        assert_eq!(
20220            ancestor_count + 1,
20221            ancestry.parent_query_count_for_test(),
20222            "linkage ancestry bypassed the parent index"
20223        );
20224
20225        ancestry.reset_parent_query_count_for_test();
20226        assert!(!cpp_callable_is_structural_constructor(
20227            function_declarator,
20228            &source,
20229            &ancestry
20230        ));
20231        assert_eq!(
20232            ancestor_count + 1,
20233            ancestry.parent_query_count_for_test(),
20234            "constructor ancestry bypassed the parent index"
20235        );
20236    }
20237
20238    /// Forward declarations followed by definitions are compacted as one
20239    /// batch, without rescanning the shared namespace/top-level lists for each
20240    /// tag. Definitions are intentionally visited in reverse order so the
20241    /// assertion also pins eager remove-and-reappend ordering.
20242    #[test]
20243    fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
20244        for aggregates in [64usize, 512] {
20245            let mut source =
20246                String::from("typedef unsigned long long u64;\nnamespace generated {\n");
20247            for index in 0..aggregates {
20248                writeln!(source, "struct tag{index};").unwrap();
20249            }
20250            for index in (0..aggregates).rev() {
20251                writeln!(
20252                    source,
20253                    "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
20254                )
20255                .unwrap();
20256            }
20257            source.push_str("}\n");
20258
20259            start_code_unit_removal_scan_probe();
20260            let parsed = parse_cpp_declarations(&source, "vmlinux.h");
20261            let scanned = finish_code_unit_removal_scan_probe();
20262
20263            let expected_names: Vec<String> = (0..aggregates)
20264                .rev()
20265                .map(|index| format!("tag{index}"))
20266                .collect();
20267            let top_level_names: Vec<String> = parsed
20268                .top_level_declarations
20269                .iter()
20270                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20271                .map(|unit| unit.short_name().to_string())
20272                .collect();
20273            let namespace = parsed
20274                .declarations()
20275                .iter()
20276                .find(|unit| {
20277                    unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
20278                })
20279                .expect("generated namespace should be declared");
20280            let child_names: Vec<String> = parsed.children[namespace]
20281                .iter()
20282                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20283                .map(|unit| unit.short_name().to_string())
20284                .collect();
20285            assert_eq!(
20286                aggregates,
20287                parsed
20288                    .declarations()
20289                    .iter()
20290                    .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20291                    .count(),
20292                "every aggregate must still be declared at {aggregates} aggregates"
20293            );
20294            assert_eq!(expected_names, top_level_names);
20295            assert_eq!(expected_names, child_names);
20296            assert_eq!(
20297                0, scanned,
20298                "replacing {aggregates} forward declarations must compact their shared lists once"
20299            );
20300        }
20301    }
20302
20303    #[test]
20304    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
20305        const DISTINCT_PER_KIND: usize = 64;
20306        let mut source = String::new();
20307        for index in 0..DISTINCT_PER_KIND {
20308            writeln!(source, "typedef int Alias{index};").unwrap();
20309        }
20310        writeln!(source, "typedef long Alias0;").unwrap();
20311        for index in 0..DISTINCT_PER_KIND {
20312            writeln!(source, "#define MACRO_{index} {index}").unwrap();
20313        }
20314        writeln!(source, "#define MACRO_0 duplicate").unwrap();
20315        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
20316
20317        let mut parser = tree_sitter::Parser::new();
20318        parser
20319            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20320            .unwrap();
20321        let tree = parser.parse(&source, None).unwrap();
20322        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
20323
20324        start_declaration_identity_comparison_probe();
20325        let parsed = parse_cpp_file(&file, &source, &tree);
20326        let comparisons = finish_declaration_identity_comparison_probe();
20327
20328        assert_eq!(
20329            DISTINCT_PER_KIND + 1,
20330            parsed
20331                .declarations()
20332                .iter()
20333                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
20334                .count(),
20335            "every physical typedef alias declaration must be retained so \
20336             conditional branch guards stay available to the resolver"
20337        );
20338        assert_eq!(
20339            DISTINCT_PER_KIND + 1,
20340            parsed
20341                .declarations()
20342                .iter()
20343                .filter(|unit| {
20344                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
20345                })
20346                .count(),
20347            "distinct macro redefinitions must remain available to temporal lookup"
20348        );
20349        assert_eq!(
20350            2,
20351            parsed
20352                .declarations()
20353                .iter()
20354                .filter(|unit| {
20355                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
20356                })
20357                .count(),
20358            "function overloads must remain distinct"
20359        );
20360
20361        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
20362        assert!(
20363            comparisons <= dedup_inputs * 4,
20364            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
20365        );
20366    }
20367
20368    #[test]
20369    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
20370        let source = r#"namespace absl {
20371ABSL_NAMESPACE_BEGIN namespace container_internal {
20372template <typename T>
20373class broken {
20374 public:
20375  using value_type = T;
20376  T operator->() const { return &operator*(); }
20377  using alias = value_type;
20378};
20379}
20380}
20381"#;
20382        let mut parser = tree_sitter::Parser::new();
20383        parser
20384            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20385            .unwrap();
20386        let tree = parser.parse(source, None).unwrap();
20387        let broken = find_class_named(tree.root_node(), source, "broken")
20388            .expect("the positive fixture must expose the broken class node");
20389        assert!(
20390            broken.has_error(),
20391            "the positive fixture must retain an internal parser error"
20392        );
20393        assert!(
20394            cpp_complete_class_body_close(broken).is_some(),
20395            "the positive fixture must expose a real class body close"
20396        );
20397        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20398        assert!(
20399            recovered.iter().any(|class| {
20400                class.scope_components == ["absl", "container_internal", "broken"]
20401            }),
20402            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
20403        );
20404    }
20405
20406    #[test]
20407    fn sentinel_recovery_keeps_members_after_nested_body_close() {
20408        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
20409NLOHMANN_BASIC_JSON_TPL_DECLARATION
20410class basic_json {
20411 private:
20412  union storage {
20413    int value;
20414  } data;
20415 public:
20416  using late_alias = int;
20417  late_alias value() const;
20418};
20419NLOHMANN_JSON_NAMESPACE_END
20420"#;
20421        let mut parser = tree_sitter::Parser::new();
20422        parser
20423            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20424            .unwrap();
20425        let tree = parser.parse(source, None).unwrap();
20426        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20427        let basic_json = recovered
20428            .iter()
20429            .find(|class| {
20430                class
20431                    .scope_components
20432                    .last()
20433                    .is_some_and(|name| name == "basic_json")
20434            })
20435            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
20436        let late_alias = source
20437            .find("late_alias value")
20438            .expect("late alias reference");
20439        assert!(
20440            basic_json.class_range.start_byte < late_alias
20441                && late_alias < basic_json.class_range.end_byte,
20442            "the recovered class range must include members after a nested close: {basic_json:#?}"
20443        );
20444    }
20445
20446    #[test]
20447    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
20448        let source = r#"namespace absl {
20449ABSL_NAMESPACE_BEGIN namespace container_internal {
20450template <typename T>
20451class broken {
20452 public:
20453  using value_type = T;
20454  T operator->() const { return &operator*(); }
20455}
20456}
20457"#;
20458        let mut parser = tree_sitter::Parser::new();
20459        parser
20460            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20461            .unwrap();
20462        let tree = parser.parse(source, None).unwrap();
20463        let broken = find_class_named(tree.root_node(), source, "broken")
20464            .expect("the negative fixture must expose the malformed class node");
20465        assert!(
20466            broken.has_error(),
20467            "the negative fixture must retain a parser error"
20468        );
20469        assert!(
20470            cpp_complete_class_body_close(broken).is_none(),
20471            "the malformed class must not expose a real body close"
20472        );
20473        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20474        assert!(
20475            recovered
20476                .iter()
20477                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
20478            "an incomplete class must not borrow the namespace close: {recovered:#?}"
20479        );
20480    }
20481
20482    #[test]
20483    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
20484        let source = r#"namespace absl {
20485ABSL_NAMESPACE_BEGIN namespace container_internal {
20486template <typename T>
20487struct broken {
20488  using value_type = T;
20489};
20490}
20491
20492#ifdef OWNER_DEF
20493template <typename T>
20494typename broken<T>::value_type broken<T>::method() {
20495  value_type value{};
20496  return value;
20497}
20498#endif
20499
20500namespace sibling {
20501template <typename T>
20502typename broken<T>::value_type broken<T>::other() {
20503  value_type value{};
20504  return value;
20505}
20506}
20507
20508ABSL_NAMESPACE_END
20509}
20510"#;
20511        let mut parser = tree_sitter::Parser::new();
20512        parser
20513            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20514            .unwrap();
20515        let tree = parser.parse(source, None).unwrap();
20516        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20517        let broken = recovered
20518            .iter()
20519            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20520            .expect("the sentinel class must be recovered");
20521        let method_start = source
20522            .find("typename broken<T>::value_type broken<T>::method()")
20523            .expect("guarded sibling owner");
20524        let method_end = source[method_start..]
20525            .find("\n}")
20526            .map(|offset| method_start + offset + 2)
20527            .expect("guarded sibling owner close");
20528        assert!(
20529            broken
20530                .owner_ranges
20531                .iter()
20532                .any(|owner| owner.range.start_byte <= method_start
20533                    && method_end <= owner.range.end_byte),
20534            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
20535        );
20536        let sibling_start = source
20537            .find("typename broken<T>::value_type broken<T>::other()")
20538            .expect("nested namespace sibling owner");
20539        assert!(
20540            broken
20541                .owner_ranges
20542                .iter()
20543                .all(|owner| owner.range.start_byte > sibling_start
20544                    || owner.range.end_byte <= sibling_start),
20545            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
20546        );
20547    }
20548
20549    #[test]
20550    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
20551        let source = r#"#ifdef OUTER
20552namespace absl {
20553ABSL_NAMESPACE_BEGIN namespace container_internal {
20554template <typename T>
20555struct broken {
20556  using value_type = T;
20557};
20558}
20559}
20560
20561#ifdef OWNER_DEF
20562template <typename T>
20563typename broken<T>::value_type broken<T>::method() {
20564  value_type value{};
20565  return value;
20566}
20567#endif
20568#endif
20569"#;
20570        let mut parser = tree_sitter::Parser::new();
20571        parser
20572            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20573            .unwrap();
20574        let tree = parser.parse(source, None).unwrap();
20575        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20576        let broken = recovered
20577            .iter()
20578            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20579            .expect("the sentinel class must be recovered");
20580        let method_start = source
20581            .find("typename broken<T>::value_type broken<T>::method()")
20582            .expect("outer sibling owner");
20583        assert!(
20584            broken
20585                .owner_ranges
20586                .iter()
20587                .all(|owner| owner.range.start_byte > method_start
20588                    || owner.range.end_byte <= method_start),
20589            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
20590        );
20591    }
20592
20593    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
20594    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
20595        let mut signatures = parsed
20596            .declarations()
20597            .iter()
20598            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
20599            .filter_map(|unit| unit.signature().map(str::to_string))
20600            .collect::<Vec<_>>();
20601        signatures.sort();
20602        signatures.dedup();
20603        signatures
20604    }
20605
20606    #[test]
20607    fn callable_parameter_types_come_from_the_ast_parameter_list() {
20608        let source = r#"
20609template <typename T, ENABLE_BYTES(T)>
20610Vec256<T> DupOdd(Vec256<T> value) { return value; }
20611
20612struct Visitor {
20613  void fail(this auto const& self) {}
20614};
20615"#;
20616        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
20617        let dup_odd = parsed
20618            .declarations()
20619            .iter()
20620            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
20621            .expect("DupOdd declaration");
20622        assert_eq!(
20623            dup_odd.signature(),
20624            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
20625        );
20626        assert_eq!(
20627            parsed
20628                .signature_metadata
20629                .get(dup_odd)
20630                .and_then(|metadata| metadata.first())
20631                .and_then(SignatureMetadata::callable_parameter_types),
20632            Some(["Vec256<T>".to_string()].as_slice())
20633        );
20634
20635        let fail = parsed
20636            .declarations()
20637            .iter()
20638            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
20639            .expect("explicit-object member");
20640        assert_eq!(fail.signature(), Some("(const this auto &)"));
20641        let metadata = parsed
20642            .signature_metadata
20643            .get(fail)
20644            .and_then(|metadata| metadata.first())
20645            .expect("explicit-object signature metadata");
20646        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
20647        assert!(
20648            metadata
20649                .callable_arity()
20650                .is_some_and(|arity| arity.accepts(0))
20651        );
20652    }
20653
20654    #[test]
20655    fn trailing_qualifiers_survive_parameter_list_whitespace() {
20656        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
20657        // declarator's structure, so an out-of-line definition that spells its
20658        // parameter list with different whitespace than the declaration must
20659        // still carry it.
20660        let source = r#"
20661struct Widget {
20662  bool multiline(int settings, int supprs) const;
20663  bool doublespace(int settings, int supprs) const;
20664  bool noexcept_multiline(int settings, int supprs) noexcept;
20665  bool ref_multiline(int settings, int supprs) &&;
20666};
20667bool
20668Widget::multiline (int settings,
20669                   int supprs) const
20670{ return settings + supprs > 0; }
20671bool Widget::doublespace(int settings,  int supprs) const { return true; }
20672bool Widget::noexcept_multiline(int settings,
20673                                int supprs) noexcept { return true; }
20674bool Widget::ref_multiline(int settings,
20675                           int supprs) && { return true; }
20676"#;
20677        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
20678        assert_eq!(
20679            vec!["(int, int) const".to_string()],
20680            identity_signatures(&parsed, "Widget.multiline")
20681        );
20682        assert_eq!(
20683            vec!["(int, int) const".to_string()],
20684            identity_signatures(&parsed, "Widget.doublespace")
20685        );
20686        assert_eq!(
20687            vec!["(int, int) noexcept".to_string()],
20688            identity_signatures(&parsed, "Widget.noexcept_multiline")
20689        );
20690        assert_eq!(
20691            vec!["(int, int) &&".to_string()],
20692            identity_signatures(&parsed, "Widget.ref_multiline")
20693        );
20694    }
20695
20696    #[test]
20697    fn macro_fragmented_plain_class_keeps_following_member_signature() {
20698        let source = r#"
20699struct CString {};
20700class CMessage {
20701public:
20702  CString GetParams(unsigned int index, unsigned int length = -1) const
20703      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
20704    return GetParamsColon(index, length);
20705  }
20706  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
20707};
20708CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
20709  return {};
20710}
20711"#;
20712        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
20713        assert_eq!(
20714            vec!["(unsigned int, unsigned int) const".to_string()],
20715            identity_signatures(&parsed, "CMessage.GetParamsColon")
20716        );
20717    }
20718
20719    #[test]
20720    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
20721        let source = r#"
20722#pragma once
20723#define DEMO_DEPRECATED(message)
20724namespace demo {
20725struct Base {
20726    static int aligned(int value) { return value; }
20727    int legacy(int value) const
20728        DEMO_DEPRECATED("use replacement()") { return value; }
20729    int replacement() const;
20730    void run(int value);
20731};
20732struct OtherBase {
20733    void run(int value);
20734    static int aligned(int value) { return value; }
20735};
20736struct Derived : Base {};
20737struct Override : Base {
20738    void run(int value);
20739    static int aligned(int value) { return value; }
20740};
20741struct RecoveredOverride : Base {
20742    int legacy(int value) const
20743        DEMO_DEPRECATED("use replacement()") { return value; }
20744    void run(int value);
20745};
20746struct Hidden : Base {
20747    void run(int first, int second);
20748    static int aligned(int first, int second) { return first + second; }
20749};
20750struct Ambiguous : Base, OtherBase {};
20751}
20752struct Global {};
20753"#;
20754        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
20755        let declarations = parsed.declarations();
20756        let fq_names = declarations
20757            .iter()
20758            .map(|unit| unit.fq_name())
20759            .collect::<std::collections::BTreeSet<_>>();
20760
20761        for expected in [
20762            "demo.Base",
20763            "demo.Base.aligned",
20764            "demo.Base.legacy",
20765            "demo.Base.replacement",
20766            "demo.Base.run",
20767            "demo.Derived",
20768            "demo.OtherBase",
20769            "demo.Override",
20770            "demo.RecoveredOverride",
20771            "demo.Hidden",
20772            "demo.Ambiguous",
20773            "Global",
20774        ] {
20775            assert!(
20776                fq_names.contains(expected),
20777                "missing {expected} from namespaced macro fragment: {declarations:#?}"
20778            );
20779        }
20780        assert!(
20781            !fq_names.contains("Derived"),
20782            "following class escaped its namespace: {declarations:#?}"
20783        );
20784        assert!(
20785            !fq_names.contains("demo.Global"),
20786            "global class crossed the recovered namespace boundary: {declarations:#?}"
20787        );
20788    }
20789
20790    #[test]
20791    fn trailing_qualifiers_still_separate_genuine_overloads() {
20792        // The qualifier must keep distinguishing the real C++ overload sets it
20793        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
20794        let source = r#"
20795struct Widget {
20796  int* slot(int index);
20797  const int* slot(int index) const;
20798  int log(int severity) &;
20799  int log(int severity) &&;
20800};
20801"#;
20802        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
20803        assert_eq!(
20804            vec!["(int)".to_string(), "(int) const".to_string()],
20805            identity_signatures(&parsed, "Widget.slot")
20806        );
20807        assert_eq!(
20808            vec!["(int) &".to_string(), "(int) &&".to_string()],
20809            identity_signatures(&parsed, "Widget.log")
20810        );
20811    }
20812
20813    #[test]
20814    fn virtual_specifier_is_not_part_of_the_identity_signature() {
20815        // `override` never appears on the out-of-line definition, and C++ does
20816        // not make it part of the signature, so it must not split the identity.
20817        let source = r#"
20818struct Base {
20819  virtual void run(int value) const;
20820};
20821struct Widget : Base {
20822  void run(int value) const override;
20823};
20824void Widget::run(int value) const {}
20825"#;
20826        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
20827        assert_eq!(
20828            vec!["(int) const".to_string()],
20829            identity_signatures(&parsed, "Widget.run")
20830        );
20831    }
20832
20833    #[test]
20834    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
20835        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
20836        // the function type, so a declaration that spells `const int` and a
20837        // definition that spells `int` are one entity.
20838        let source = r#"
20839struct Widget {
20840  bool value_params(const int settings, const int supprs);
20841  void pointee_const(const int* p);
20842  void pointer_const(int* const p);
20843  void both_const(const int* const p);
20844  void reference_const(const int& p);
20845  void array_const(const int values[4]);
20846};
20847bool Widget::value_params(int settings, int supprs) { return true; }
20848void Widget::pointer_const(int* p) {}
20849void Widget::both_const(const int* p) {}
20850"#;
20851        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
20852        assert_eq!(
20853            vec!["(int, int)".to_string()],
20854            identity_signatures(&parsed, "Widget.value_params")
20855        );
20856        assert_eq!(
20857            vec!["(int *)".to_string()],
20858            identity_signatures(&parsed, "Widget.pointer_const")
20859        );
20860        assert_eq!(
20861            vec!["(const int *)".to_string()],
20862            identity_signatures(&parsed, "Widget.both_const")
20863        );
20864        // The const that is not top-level still distinguishes the type.
20865        assert_eq!(
20866            vec!["(const int *)".to_string()],
20867            identity_signatures(&parsed, "Widget.pointee_const")
20868        );
20869        assert_eq!(
20870            vec!["(const int &)".to_string()],
20871            identity_signatures(&parsed, "Widget.reference_const")
20872        );
20873        assert_eq!(
20874            vec!["(const int [4])".to_string()],
20875            identity_signatures(&parsed, "Widget.array_const")
20876        );
20877    }
20878
20879    #[test]
20880    fn top_level_parameter_const_still_separates_pointee_overloads() {
20881        let source = r#"
20882struct Widget {
20883  void take(const int* p);
20884  void take(int* p);
20885};
20886"#;
20887        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
20888        assert_eq!(
20889            vec!["(const int *)".to_string(), "(int *)".to_string()],
20890            identity_signatures(&parsed, "Widget.take")
20891        );
20892    }
20893
20894    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
20895        let mut parser = tree_sitter::Parser::new();
20896        parser
20897            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20898            .unwrap();
20899        let tree = parser.parse(source, None).unwrap();
20900        let start = source.find(callable_name).expect("callable declaration");
20901        let declarator =
20902            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
20903        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
20904    }
20905
20906    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
20907        let mut shapes = comparable_shapes(source, callable_name);
20908        assert_eq!(1, shapes.len(), "{shapes:?}");
20909        match shapes.remove(0) {
20910            CppComparableSlot::Shape(shape) => shape,
20911            other => panic!("expected a comparable shape, got {other:?}"),
20912        }
20913    }
20914
20915    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
20916        let mut current = shape.root();
20917        loop {
20918            match shape.node(current) {
20919                CppComparableNode::Named { .. } => return shape.node(current),
20920                CppComparableNode::Pointer { inner, .. }
20921                | CppComparableNode::Reference { inner }
20922                | CppComparableNode::Array { inner } => current = *inner,
20923                CppComparableNode::Generic { base, .. } => current = *base,
20924            }
20925        }
20926    }
20927
20928    #[test]
20929    fn comparable_shape_keeps_pointee_const() {
20930        assert_ne!(
20931            sole_comparable_shape("void f(const char* p);", "f("),
20932            sole_comparable_shape("void f(char* p);", "f(")
20933        );
20934    }
20935
20936    #[test]
20937    fn comparable_shape_keeps_inner_pointer_const() {
20938        assert_ne!(
20939            sole_comparable_shape("void f(int** p);", "f("),
20940            sole_comparable_shape("void f(int* const* p);", "f(")
20941        );
20942    }
20943
20944    #[test]
20945    fn comparable_shape_drops_top_level_pointer_const() {
20946        assert_eq!(
20947            sole_comparable_shape("void f(int* const p);", "f("),
20948            sole_comparable_shape("void f(int* p);", "f(")
20949        );
20950    }
20951
20952    #[test]
20953    fn comparable_shape_drops_top_level_base_const() {
20954        assert_eq!(
20955            sole_comparable_shape("void f(const int p);", "f("),
20956            sole_comparable_shape("void f(int p);", "f(")
20957        );
20958    }
20959
20960    #[test]
20961    fn comparable_shape_decays_top_level_array_to_pointer() {
20962        assert_eq!(
20963            sole_comparable_shape("void f(int a[3]);", "f("),
20964            sole_comparable_shape("void f(int* a);", "f(")
20965        );
20966        assert_eq!(
20967            sole_comparable_shape("void f(int* a[3]);", "f("),
20968            sole_comparable_shape("void f(int** a);", "f(")
20969        );
20970    }
20971
20972    #[test]
20973    fn comparable_shape_keeps_array_behind_pointer() {
20974        assert_ne!(
20975            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
20976            sole_comparable_shape("struct S { void f(int** a); };", "f(")
20977        );
20978    }
20979
20980    #[test]
20981    fn comparable_shape_records_written_name_and_lexical_scope() {
20982        let declared =
20983            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
20984        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
20985        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
20986            panic!("named leaf");
20987        };
20988        assert_eq!(["Msg".to_string()].as_slice(), name.path());
20989        assert_eq!(
20990            ["ns".to_string(), "S".to_string()].as_slice(),
20991            name.lexical_scope()
20992        );
20993        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
20994            panic!("named leaf");
20995        };
20996        assert_eq!(
20997            ["ns".to_string(), "Msg".to_string()].as_slice(),
20998            name.path()
20999        );
21000        assert!(name.lexical_scope().is_empty());
21001        assert_ne!(declared, defined);
21002    }
21003
21004    #[test]
21005    fn comparable_shape_marks_sized_primitive_leaf() {
21006        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
21007        let CppComparableNode::Named {
21008            name, primitive, ..
21009        } = comparable_named_leaf(&shape)
21010        else {
21011            panic!("named leaf");
21012        };
21013        assert!(primitive);
21014        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
21015        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
21016    }
21017
21018    #[test]
21019    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
21020        assert_eq!(
21021            vec![CppComparableSlot::Unstructured],
21022            comparable_shapes("void f(void (*cb)(int));", "f(")
21023        );
21024    }
21025
21026    #[test]
21027    fn comparable_shape_reports_ellipsis_slot() {
21028        let shapes = comparable_shapes("void f(int a, ...);", "f(");
21029        assert_eq!(2, shapes.len(), "{shapes:?}");
21030        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
21031    }
21032
21033    #[test]
21034    fn comparable_shape_keeps_template_argument_const() {
21035        assert_ne!(
21036            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
21037            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
21038        );
21039    }
21040
21041    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
21042    /// file-scope tag that a later `struct inner *` at file scope may name.
21043    #[test]
21044    fn c_file_mints_aggregate_member_tag_at_file_scope() {
21045        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21046        let parsed = parse_cpp_declarations(source, "x.c");
21047        let declarations = parsed.declarations();
21048
21049        assert!(
21050            declarations
21051                .iter()
21052                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21053            "expected a file-scope inner tag, got {declarations:?}"
21054        );
21055        assert!(
21056            declarations
21057                .iter()
21058                .all(|unit| unit.fq_name() != "outer$inner"),
21059            "expected no nested identity, got {declarations:?}"
21060        );
21061        assert!(
21062            declarations
21063                .iter()
21064                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
21065        );
21066        // Members still belong to their own aggregate.
21067        assert!(
21068            declarations
21069                .iter()
21070                .any(|unit| unit.fq_name() == "inner.value")
21071        );
21072        assert!(
21073            declarations
21074                .iter()
21075                .any(|unit| unit.fq_name() == "outer.item")
21076        );
21077
21078        let outer = declarations
21079            .iter()
21080            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
21081            .expect("outer");
21082        assert!(
21083            parsed
21084                .children
21085                .get(outer)
21086                .into_iter()
21087                .flatten()
21088                .all(|child| child.fq_name() != "inner"),
21089            "the tag must not hang off the aggregate it is written inside: {:?}",
21090            parsed.children
21091        );
21092    }
21093
21094    /// A header carries no compilation language of its own, and a `.cpp`
21095    /// translation unit really does declare a nested class. Both keep exactly
21096    /// the C++ extraction they had before the C dialect existed.
21097    #[test]
21098    fn header_and_cpp_files_keep_nested_tag_identity() {
21099        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21100        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
21101            let parsed = parse_cpp_declarations(source, name);
21102            let declarations = parsed.declarations();
21103            assert!(
21104                declarations
21105                    .iter()
21106                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21107                "{name} must keep the nested identity, got {declarations:?}"
21108            );
21109            assert!(
21110                declarations.iter().all(|unit| unit.fq_name() != "inner"),
21111                "{name} must not mint a file-scope tag, got {declarations:?}"
21112            );
21113            assert!(
21114                declarations
21115                    .iter()
21116                    .any(|unit| unit.fq_name() == "outer$inner.value")
21117            );
21118        }
21119    }
21120
21121    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
21122    #[test]
21123    fn uppercase_c_extension_keeps_cpp_tag_scope() {
21124        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21125        let parsed = parse_cpp_declarations(source, "x.C");
21126        assert!(
21127            parsed
21128                .declarations()
21129                .iter()
21130                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
21131        );
21132    }
21133
21134    /// There is no such thing as a partially nested tag in C: every level of a
21135    /// nested aggregate chain lands at the same enclosing scope.
21136    #[test]
21137    fn c_file_mints_every_nesting_level_at_file_scope() {
21138        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
21139        let parsed = parse_cpp_declarations(source, "z.c");
21140        let declarations = parsed.declarations();
21141
21142        for tag in ["a", "b", "c"] {
21143            assert!(
21144                declarations
21145                    .iter()
21146                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
21147                "expected a file-scope {tag}, got {declarations:?}"
21148            );
21149        }
21150        assert!(
21151            declarations
21152                .iter()
21153                .all(|unit| !unit.fq_name().contains('$')),
21154            "no level may keep a nested identity, got {declarations:?}"
21155        );
21156        // Each member still belongs to the aggregate that declares it.
21157        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
21158        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
21159        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
21160    }
21161
21162    /// An enum tag is a tag; its enumerators stay members of the enum, which is
21163    /// what makes them ordinary identifiers at the enum's own (file) scope.
21164    #[test]
21165    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
21166        let source = "struct outer { enum color { RED, GREEN } c; };\n";
21167        let parsed = parse_cpp_declarations(source, "e.c");
21168        let declarations = parsed.declarations();
21169
21170        let color = declarations
21171            .iter()
21172            .find(|unit| unit.is_class() && unit.fq_name() == "color")
21173            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
21174        assert!(
21175            declarations
21176                .iter()
21177                .all(|unit| unit.fq_name() != "outer$color")
21178        );
21179        for enumerator in ["color.RED", "color.GREEN"] {
21180            assert!(
21181                declarations.iter().any(|unit| unit.fq_name() == enumerator),
21182                "expected {enumerator}, got {declarations:?}"
21183            );
21184        }
21185        let children = parsed
21186            .children
21187            .get(color)
21188            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
21189        assert!(
21190            ["color.RED", "color.GREEN"]
21191                .iter()
21192                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
21193            "enumerators must hang off their enum: {children:?}"
21194        );
21195    }
21196
21197    #[test]
21198    fn c_file_mints_member_list_union_at_file_scope() {
21199        let source = "struct outer { union inner { int a; float b; } item; };\n";
21200        let parsed = parse_cpp_declarations(source, "u.c");
21201        let declarations = parsed.declarations();
21202        assert!(
21203            declarations
21204                .iter()
21205                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21206            "expected a file-scope inner union, got {declarations:?}"
21207        );
21208        assert!(
21209            declarations
21210                .iter()
21211                .all(|unit| unit.fq_name() != "outer$inner")
21212        );
21213        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
21214        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
21215    }
21216
21217    /// A tag declared in a namespace member list is not a file-scope tag: the
21218    /// nearest enclosing non-aggregate scope is the namespace.
21219    #[test]
21220    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
21221        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
21222        let parsed = parse_cpp_declarations(source, "n.c");
21223        let declarations = parsed.declarations();
21224        let inner = declarations
21225            .iter()
21226            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
21227            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
21228        assert_eq!(inner.package_name(), "ns");
21229        assert!(
21230            declarations
21231                .iter()
21232                .all(|unit| unit.fq_name() != "ns.outer$inner")
21233        );
21234    }
21235
21236    /// Pins today's treatment of a tag declared inside a function body: the
21237    /// declaration walk does not descend into statement bodies, so no unit is
21238    /// minted for it in either dialect. C block scope is out of scope for the
21239    /// dialect change, and this test proves the change did not disturb it.
21240    #[test]
21241    fn function_local_tags_are_unchanged_in_both_dialects() {
21242        let source =
21243            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
21244        for name in ["y.c", "y.cpp"] {
21245            let parsed = parse_cpp_declarations(source, name);
21246            let declarations = parsed.declarations();
21247            assert!(
21248                declarations
21249                    .iter()
21250                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
21251                "{name}: {declarations:?}"
21252            );
21253            for tag in ["localtag", "deeper", "localtag$deeper"] {
21254                assert!(
21255                    declarations.iter().all(|unit| unit.fq_name() != tag),
21256                    "{name} must not mint {tag}, got {declarations:?}"
21257                );
21258            }
21259        }
21260    }
21261
21262    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
21263    /// re-scope: the typedef name is identical in both dialects.
21264    #[test]
21265    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
21266        let source = "typedef struct { int v; } T;\n";
21267        for name in ["t.c", "t.cpp"] {
21268            let parsed = parse_cpp_declarations(source, name);
21269            let declarations = parsed.declarations();
21270            assert!(
21271                declarations
21272                    .iter()
21273                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
21274                "{name}: {declarations:?}"
21275            );
21276        }
21277    }
21278
21279    #[test]
21280    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
21281        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
21282        let parsed = parse_cpp_declarations(source, "socket.c");
21283        let declarations = parsed.declarations();
21284        assert_eq!(
21285            declarations
21286                .iter()
21287                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
21288                .count(),
21289            1,
21290            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
21291        );
21292        for expected in [
21293            "PAL_HANDLE",
21294            "PAL_HANDLE.sock",
21295            "PAL_HANDLE$sock",
21296            "PAL_HANDLE$sock.ops",
21297        ] {
21298            assert!(
21299                declarations.iter().any(|unit| unit.fq_name() == expected),
21300                "expected {expected}, got {declarations:?}"
21301            );
21302        }
21303    }
21304
21305    /// `class` is not C. Source that spells one in a `.c` file is not C code,
21306    /// so it keeps the C++ reading rather than acquiring a half-C identity.
21307    #[test]
21308    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
21309        let source = "class outer { class inner { int v; }; };\n";
21310        let c_parsed = parse_cpp_declarations(source, "k.c");
21311        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
21312        let c_declarations = c_parsed.declarations();
21313        let cpp_declarations = cpp_parsed.declarations();
21314        assert!(
21315            c_declarations
21316                .iter()
21317                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21318            "{c_declarations:?}"
21319        );
21320        assert_eq!(
21321            c_declarations
21322                .iter()
21323                .map(|unit| unit.fq_name())
21324                .collect::<std::collections::BTreeSet<_>>(),
21325            cpp_declarations
21326                .iter()
21327                .map(|unit| unit.fq_name())
21328                .collect::<std::collections::BTreeSet<_>>()
21329        );
21330    }
21331
21332    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
21333    /// every (node, class-like name) pair a tree offers, and require the same
21334    /// answer from both.
21335    ///
21336    /// The release build has no `debug_assertions` agreement check, so this is
21337    /// what pins the two together there.  Both query orders are exercised:
21338    /// document order is what the walk does, and reverse order proves that a
21339    /// question about an earlier byte than one already answered is still
21340    /// filtered back to its own prefix rather than answered from the wider
21341    /// fold.
21342    ///
21343    /// Returns how many questions were answered with a namespace, so a fixture
21344    /// can assert it actually reached the path (#2754).
21345    fn namespace_forward_scan_agreement(source: &str) -> usize {
21346        let mut parser = tree_sitter::Parser::new();
21347        parser
21348            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21349            .unwrap();
21350        let tree = parser.parse(source, None).unwrap();
21351        let root = tree.root_node();
21352        let ancestry = ParentIndex::new(root);
21353
21354        let mut nodes = Vec::new();
21355        let mut names = std::collections::BTreeSet::new();
21356        let mut cursor = root.walk();
21357        let mut stack = vec![root];
21358        while let Some(node) = stack.pop() {
21359            if matches!(
21360                node.kind(),
21361                "class_specifier" | "struct_specifier" | "union_specifier"
21362            ) && let Some(name) = class_like_name(node, source, &ancestry)
21363            {
21364                names.insert(name);
21365            }
21366            nodes.push(node);
21367            stack.extend(node.named_children(&mut cursor));
21368        }
21369        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
21370        assert!(!names.is_empty(), "fixture declares no class-like name");
21371
21372        let mut answered = 0usize;
21373        for reversed in [false, true] {
21374            let mut scan = CppNamespaceForwardScan::default();
21375            let ordered: Vec<_> = if reversed {
21376                nodes.iter().rev().copied().collect()
21377            } else {
21378                nodes.clone()
21379            };
21380            answered = 0;
21381            for node in ordered {
21382                for name in &names {
21383                    scan.advance_to(root, node.start_byte(), source, &ancestry);
21384                    let carried = scan.unique_earlier_forward(name, node);
21385                    assert_eq!(
21386                        carried,
21387                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
21388                        "carried-forward scan and prefix scan disagree about {name} at \
21389                         {} node starting at byte {} (reversed order: {reversed})",
21390                        node.kind(),
21391                        node.start_byte()
21392                    );
21393                    answered += usize::from(carried.is_some());
21394                }
21395            }
21396        }
21397        answered
21398    }
21399
21400    /// A malformed namespace whose forward declarations are the only identity
21401    /// signal left for the class definitions tree-sitter pushed out to file
21402    /// scope.  Both recovered classes open the guard; only the first one is
21403    /// separated from the namespace by nothing but recovery trivia, so only the
21404    /// first one borrows.  The carried-forward scan has to reproduce both
21405    /// answers.
21406    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
21407namespace ns {
21408class Widget;
21409class Gadget;
21410int x = ;
21411}
21412class API Widget {
21413public:
21414    void first();
21415};
21416class API Gadget {
21417public:
21418    void second();
21419};
21420"#;
21421
21422    #[test]
21423    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
21424        assert!(
21425            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
21426            "the fixture must actually reach the namespace-borrow path"
21427        );
21428
21429        // Nothing here may be answered, and the two paths have to agree about
21430        // that too: a clean namespace is not an identity proof, two forwards of
21431        // one name are ambiguous rather than a guess, and a forward inside a
21432        // function body is not at namespace scope.
21433        for source in [
21434            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
21435            r#"#define API
21436namespace ns {
21437class Widget;
21438class Widget;
21439int x = ;
21440}
21441class API Widget {
21442public:
21443    void method();
21444};
21445"#,
21446            r#"#define API
21447namespace ns {
21448void host() {
21449    class Widget;
21450}
21451int x = ;
21452}
21453class API Widget {
21454public:
21455    void method();
21456};
21457"#,
21458        ] {
21459            assert_eq!(
21460                namespace_forward_scan_agreement(source),
21461                0,
21462                "no borrow is justified here: {source}"
21463            );
21464        }
21465    }
21466
21467    /// The fold is incremental, so a walk that asks about steadily later bytes
21468    /// must never re-fold a node an earlier question already folded, and must
21469    /// never skip one that lies between two questions.
21470    #[test]
21471    fn carried_forward_namespace_scan_folds_each_node_once() {
21472        let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
21473        let mut parser = tree_sitter::Parser::new();
21474        parser
21475            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21476            .unwrap();
21477        let tree = parser.parse(source, None).unwrap();
21478        let root = tree.root_node();
21479        let ancestry = ParentIndex::new(root);
21480
21481        let mut incremental = CppNamespaceForwardScan::default();
21482        for cutoff in 0..=source.len() {
21483            incremental.advance_to(root, cutoff, source, &ancestry);
21484        }
21485        let mut whole = CppNamespaceForwardScan::default();
21486        whole.advance_to(root, source.len(), source, &ancestry);
21487
21488        let mut incremental_shape: Vec<_> = incremental
21489            .forwards
21490            .iter()
21491            .map(|(name, forwards)| {
21492                (
21493                    name.clone(),
21494                    forwards
21495                        .iter()
21496                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
21497                        .collect::<Vec<_>>(),
21498                )
21499            })
21500            .collect();
21501        let mut whole_shape: Vec<_> = whole
21502            .forwards
21503            .iter()
21504            .map(|(name, forwards)| {
21505                (
21506                    name.clone(),
21507                    forwards
21508                        .iter()
21509                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
21510                        .collect::<Vec<_>>(),
21511                )
21512            })
21513            .collect();
21514        incremental_shape.sort();
21515        whole_shape.sort();
21516        for (_, forwards) in &mut incremental_shape {
21517            forwards.sort();
21518        }
21519        for (_, forwards) in &mut whole_shape {
21520            forwards.sort();
21521        }
21522
21523        assert!(!whole_shape.is_empty(), "fixture folds no forward");
21524        assert_eq!(
21525            incremental_shape, whole_shape,
21526            "one byte at a time must fold exactly what one whole pass folds"
21527        );
21528    }
21529
21530    /// Drive the region reparse and the whitespace-padded reparse it replaced
21531    /// over the same region and require identical trees.
21532    ///
21533    /// The release build has no `debug_assertions` agreement check, so this is
21534    /// what pins the two together there. Each body is reparsed at its own
21535    /// offset and again after a long prefix, because the prefix is the whole
21536    /// difference between the two techniques: the padded parse lexes it as
21537    /// whitespace, the included-range parse never sees it, and the tree has to
21538    /// come out the same either way (#2788).
21539    fn fragmented_class_reparse_agreement(body: &str) {
21540        for prefix in [
21541            String::new(),
21542            "// leading comment\n".to_string(),
21543            // A body starts just after its class head's `{`, which is normally
21544            // mid-line: the padded parse then has spaces before the region on
21545            // the region's own line, and the included-range parse has nothing
21546            // at all before it.
21547            "class Widget : public Base { ".to_string(),
21548            "namespace filler {\n".to_string()
21549                + &"struct Filler { int member; };\n".repeat(200)
21550                + "}\n",
21551            "namespace filler {\n".to_string()
21552                + &"struct Filler { int member; };\n".repeat(200)
21553                + "}\nclass Widget : public Base { ",
21554        ] {
21555            let source = format!("{prefix}{body}");
21556            let start = prefix.len();
21557            let end = source.len();
21558            let region = cpp_reparse_fragmented_class_body(&source, start, end)
21559                .expect("the region reparse must produce a tree");
21560            let padded = cpp_reparse_padded_class_body(&source, start, end)
21561                .expect("the padded reparse must produce a tree");
21562            assert_eq!(
21563                cpp_tree_shape(&region),
21564                cpp_tree_shape(&padded),
21565                "region and padded reparse disagree at offset {start} of {end} bytes"
21566            );
21567            assert_eq!(
21568                region.root_node().start_byte(),
21569                start,
21570                "the reparsed region keeps its original offsets"
21571            );
21572        }
21573    }
21574
21575    #[test]
21576    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
21577        // A conditional immediately after an access label: the shape the padded
21578        // technique was kept for, because the directive and its macro name
21579        // become an ERROR plus the following declaration's apparent type.
21580        fragmented_class_reparse_agreement(
21581            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
21582        );
21583        fragmented_class_reparse_agreement(
21584            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
21585        );
21586        // The merged inline constructor and the nested fragmented bodies the
21587        // #938 recovery reads out of a reparse.
21588        fragmented_class_reparse_agreement(
21589            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
21590        );
21591        fragmented_class_reparse_agreement(
21592            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
21593        );
21594        // A body that is not member-shaped at all still has to reparse the same
21595        // way, because the admission gate reads the tree to reject it.
21596        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
21597    }
21598
21599    /// A header shaped like the generated ones this walk is slow on: many
21600    /// enums, classes whose members share the enums' names, nested enums, an
21601    /// ownerless enumerator, and namespaced repeats of all of it.
21602    fn many_enums_and_mixed_declarations() -> String {
21603        let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
21604        for index in 0..40 {
21605            let _ = write!(
21606                source,
21607                "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
21608                 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
21609                 class Color{index}Like {{ public: int member{index}; }};\n"
21610            );
21611        }
21612        source.push_str("namespace outer {\n");
21613        for index in 0..20 {
21614            let _ = write!(
21615                source,
21616                "enum Shade{index} {{ DARK{index} }};\n\
21617                 struct Shade{index}Holder {{ int field{index}; }};\n"
21618            );
21619        }
21620        source.push_str("}\n");
21621        source
21622    }
21623
21624    /// Drive [`CppFieldOwnerIndex`] and the declaration scan it replaced over
21625    /// every question a fixture's declarations can ask, and require the same
21626    /// answer from both.
21627    ///
21628    /// The release build has no `debug_assertions` agreement check, so this is
21629    /// what pins the two together there. The index is fed one declaration at a
21630    /// time and every question is re-asked after each one, which is what proves
21631    /// the incremental record agrees -- a whole-set rebuild would pass a weaker
21632    /// test. Each of the fixture's own fields is also fed in restated as a
21633    /// declaration of another file: the scan ignores those because it asks
21634    /// about the asking unit's own source, and the index has to ignore them for
21635    /// the same reason.
21636    ///
21637    /// Returns how many questions the fixture answered `true`, so a caller can
21638    /// assert that it actually reached the path (#2786).
21639    fn field_owner_index_agreement(source: &str, name: &str) -> usize {
21640        let parsed = parse_cpp_declarations(source, name);
21641        let file = ProjectFile::new(std::env::temp_dir(), name);
21642        let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
21643
21644        let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
21645        declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
21646
21647        let foreign: Vec<CodeUnit> = declarations
21648            .iter()
21649            .filter(|unit| unit.kind() == CodeUnitType::Field)
21650            .map(|unit| {
21651                CodeUnit::new_fq(
21652                    elsewhere.clone(),
21653                    unit.kind(),
21654                    unit.package_name().to_string(),
21655                    unit.short_name().to_string(),
21656                    unit.fq().clone(),
21657                )
21658            })
21659            .collect();
21660
21661        // One owner chain deeper than any C++ short name reaches today
21662        // (`cpp_member_fq`: at most one `.`, separating the owner chain from
21663        // the member). The scan asks `starts_with("owner.")`, so a field like
21664        // this answers for every owner in its chain, and the index has to
21665        // record every one of them rather than only the innermost.
21666        let mut packages: Vec<String> = declarations
21667            .iter()
21668            .map(|unit| unit.package_name().to_string())
21669            .collect();
21670        packages.push(String::new());
21671        packages.sort();
21672        packages.dedup();
21673        let deeper: Vec<CodeUnit> = packages
21674            .iter()
21675            .map(|package_name| {
21676                CodeUnit::new_fq(
21677                    file.clone(),
21678                    CodeUnitType::Field,
21679                    package_name.clone(),
21680                    "SynthOwner.middle.leaf".to_string(),
21681                    cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
21682                )
21683            })
21684            .collect();
21685
21686        // Every (package, owner) pair anything could ask about: each unit's own
21687        // short name, each dotted prefix of it, and the empty owner an
21688        // anonymous enum asks with (#2140).
21689        let mut questions: Vec<(String, String)> = Vec::new();
21690        for unit in declarations.iter().chain(deeper.iter()) {
21691            let package_name = unit.package_name().to_string();
21692            let short_name = unit.short_name();
21693            questions.push((package_name.clone(), short_name.to_string()));
21694            questions.push((package_name.clone(), String::new()));
21695            for (offset, _) in short_name.match_indices('.') {
21696                questions.push((package_name.clone(), short_name[..offset].to_string()));
21697            }
21698        }
21699        questions.sort();
21700        questions.dedup();
21701
21702        let mut index = CppFieldOwnerIndex::default();
21703        let mut recorded: Vec<&CodeUnit> = Vec::new();
21704        let mut answered = 0usize;
21705        for unit in foreign
21706            .iter()
21707            .chain(declarations.iter())
21708            .chain(deeper.iter())
21709        {
21710            index.record(unit, &file);
21711            recorded.push(unit);
21712            for (package_name, owner_short_name) in &questions {
21713                let carried = index.owns_fields(package_name, owner_short_name);
21714                assert_eq!(
21715                    carried,
21716                    cpp_declarations_hold_owned_fields(
21717                        recorded.iter().copied(),
21718                        &file,
21719                        package_name,
21720                        owner_short_name
21721                    ),
21722                    "the carried field index and the declaration scan disagree about \
21723                     {package_name:?}/{owner_short_name:?} after recording {}",
21724                    unit.fq_name()
21725                );
21726                answered += usize::from(carried);
21727            }
21728        }
21729
21730        // The whole-set build the first question performs must land on the same
21731        // index the incremental record built.
21732        let rebuilt = CppFieldOwnerIndex::of(
21733            foreign
21734                .iter()
21735                .chain(declarations.iter())
21736                .chain(deeper.iter()),
21737            &file,
21738        );
21739        for (package_name, owner_short_name) in &questions {
21740            assert_eq!(
21741                rebuilt.owns_fields(package_name, owner_short_name),
21742                index.owns_fields(package_name, owner_short_name),
21743                "a rebuilt index must answer what the incremental one answers for \
21744                 {package_name:?}/{owner_short_name:?}"
21745            );
21746        }
21747        answered
21748    }
21749
21750    /// The one thing the field index cannot absorb by addition: a deferred
21751    /// replacement of a declaration that owns children removes those children.
21752    ///
21753    /// The first enum builds the index, the struct records `Color.RED` into it,
21754    /// the body-less second `Color` replaces the first and takes `Color.RED`
21755    /// with it, and the last enum then asks about owner `Color`. An index that
21756    /// survived that removal answers `true` where the declarations say `false`,
21757    /// which is exactly what the in-walk agreement assertion catches (#2786).
21758    #[test]
21759    fn a_replacement_that_removes_children_drops_the_field_index() {
21760        let source =
21761            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
21762        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
21763        let mut names: Vec<_> = parsed
21764            .declarations()
21765            .iter()
21766            .map(|unit| unit.fq_name())
21767            .collect();
21768        names.sort();
21769        assert_eq!(
21770            names,
21771            vec![
21772                "Color".to_string(),
21773                "First".to_string(),
21774                "First.A".to_string()
21775            ],
21776            "the replaced Color owns no field any more"
21777        );
21778    }
21779
21780    /// A recovery that re-declares what the file already declared mints
21781    /// nothing.
21782    ///
21783    /// The reparse walk replaces the outer `Widget`, which removes its method,
21784    /// and then re-creates that method from the region. Creation alone would
21785    /// call the method recovered; it was there before the recovery opened, so
21786    /// the recovered set is empty and only the region's own reparse window is
21787    /// recorded (#2787).
21788    #[test]
21789    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
21790        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
21791                      BEGIN_NS\n\
21792                      namespace demo { struct Widget { void doWork(); }; }\n\
21793                      END_NS\n";
21794        let parsed = parse_cpp_declarations(source, "restored.cpp");
21795        let recovered: Vec<String> = parsed
21796            .materialization_records
21797            .iter()
21798            .filter_map(|record| match record {
21799                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
21800                _ => None,
21801            })
21802            .collect();
21803        assert!(
21804            recovered.is_empty(),
21805            "the region declares nothing the file did not already declare: {recovered:?}"
21806        );
21807        let mut names: Vec<String> = parsed
21808            .declarations()
21809            .iter()
21810            .map(|unit| unit.fq_name())
21811            .collect();
21812        names.sort();
21813        assert_eq!(
21814            names,
21815            vec![
21816                "demo".to_string(),
21817                "demo.Widget".to_string(),
21818                "demo.Widget.doWork".to_string(),
21819            ]
21820        );
21821    }
21822
21823    /// Four macro-sentinel recoveries in one file (#941). Each one must record
21824    /// exactly the declarations it minted -- not the ones an earlier recovery
21825    /// minted, and not the file's other declarations -- in start-byte order,
21826    /// and each record must carry its own reparse window (#2787).
21827    #[test]
21828    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
21829        let mut source = String::new();
21830        for index in 0..4 {
21831            let _ = write!(
21832                source,
21833                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
21834            );
21835        }
21836        source.push_str("void outside() {}\n");
21837        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
21838
21839        let recovered: Vec<(String, (usize, usize))> = parsed
21840            .materialization_records
21841            .iter()
21842            .filter_map(|record| match record {
21843                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
21844                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
21845                }
21846                _ => None,
21847            })
21848            .collect();
21849
21850        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
21851        for index in 0..4 {
21852            // The window the reparse covers: everything after the opening
21853            // sentinel token up to the newline before the closing one.
21854            let region = format!("namespace demo{index}");
21855            let region_start = source.find(&region).expect("each region is in the source");
21856            let start = source[..region_start]
21857                .rfind("BEGIN_NS")
21858                .expect("each region opens with a sentinel")
21859                + "BEGIN_NS".len();
21860            let end = start
21861                + source[start..]
21862                    .find("END_NS")
21863                    .expect("each region closes with a sentinel")
21864                - 1;
21865            let window = (start, end);
21866            for name in [
21867                format!("demo{index}"),
21868                format!("demo{index}.Widget{index}"),
21869                format!("demo{index}.Widget{index}.doWork{index}"),
21870            ] {
21871                expected.push((name, window));
21872            }
21873        }
21874        assert_eq!(
21875            recovered, expected,
21876            "each recovery records its own minted declarations, in order"
21877        );
21878        assert!(
21879            parsed
21880                .declarations()
21881                .iter()
21882                .any(|unit| unit.fq_name() == "outside"),
21883            "the declaration outside every region stays parsed and unrecovered"
21884        );
21885    }
21886
21887    #[test]
21888    fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
21889        assert!(
21890            field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
21891            "the fixture must actually own fields"
21892        );
21893
21894        // The shapes that make the two implementations diverge if the index
21895        // records the wrong keys: a nested enum's owner is its whole dotted
21896        // chain, a class named like an enum owns fields under that same name, a
21897        // `$` in a short name is an owner separator the dotted prefix rule must
21898        // not split on, and the same enum name in two namespaces is two owners.
21899        for (source, name) in [
21900            ("struct S { enum E { V }; };\n", "nested.hpp"),
21901            (
21902                "enum Color { RED };\nstruct Color { int RED; };\n",
21903                "class-like.c",
21904            ),
21905            ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
21906            (
21907                "enum E { V };\nnamespace ns { enum E { V }; }\n",
21908                "repeated.hpp",
21909            ),
21910            ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
21911        ] {
21912            field_owner_index_agreement(source, name);
21913        }
21914    }
21915
21916    /// `blocks` copies of one plain class-with-methods template. The class the
21917    /// question is about is always the first one, so the only thing that
21918    /// changes between two of these sources is how much unrelated tree
21919    /// surrounds it.
21920    fn repeated_class_blocks(blocks: usize) -> String {
21921        let mut source = String::from("namespace demo {\n");
21922        for index in 0..blocks {
21923            let _ = write!(
21924                source,
21925                "\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"
21926            );
21927        }
21928        source.push_str("\n}\n");
21929        source
21930    }
21931
21932    /// #1496: asking whether a recovered class shape claims one range must cost
21933    /// the path to that range, not a pass over the whole translation unit.
21934    ///
21935    /// The C++ inverse scan asks this once per candidate type reference, so a
21936    /// whole-tree walk makes one file's scan quadratic in its own size. Both
21937    /// sources here answer `None` -- these are plain classes that no recovery
21938    /// shape claims -- which is exactly the case that used to pay full price.
21939    #[test]
21940    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
21941        let mut answers = Vec::new();
21942        let mut visits = Vec::new();
21943        let mut node_counts = Vec::new();
21944        for blocks in [200usize, 400] {
21945            let source = repeated_class_blocks(blocks);
21946            let mut parser = tree_sitter::Parser::new();
21947            parser
21948                .set_language(&tree_sitter_cpp::LANGUAGE.into())
21949                .unwrap();
21950            let tree = parser.parse(&source, None).unwrap();
21951            let start_byte = source.find("class Widget0 ").expect("first class");
21952            let end_byte = start_byte
21953                + source[start_byte..]
21954                    .find("};")
21955                    .expect("first class terminator")
21956                + "};".len();
21957            let range = Range {
21958                start_byte,
21959                end_byte,
21960                start_line: 0,
21961                end_line: 0,
21962            };
21963            reset_recovered_class_body_node_visits_for_test();
21964            let recovered_export_classes =
21965                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
21966            answers.push(recovered_class_body_at(
21967                &recovered_export_classes,
21968                tree.root_node(),
21969                &source,
21970                "Widget0",
21971                &range,
21972            ));
21973            visits.push(recovered_class_body_node_visits_for_test());
21974            let mut nodes = 0usize;
21975            let mut stack = vec![tree.root_node()];
21976            while let Some(node) = stack.pop() {
21977                nodes += 1;
21978                let mut cursor = node.walk();
21979                stack.extend(node.named_children(&mut cursor));
21980            }
21981            node_counts.push(nodes);
21982        }
21983
21984        assert_eq!(
21985            answers,
21986            vec![None, None],
21987            "no recovered shape claims a plain class"
21988        );
21989        assert_eq!(
21990            visits[0], visits[1],
21991            "the walk must follow the range's own path, so doubling the unrelated \
21992             classes must not change the node count: {visits:?} over trees of \
21993             {node_counts:?} nodes"
21994        );
21995        assert!(
21996            visits[1] * 20 < node_counts[1],
21997            "the walk must stay far below one pass over the tree: {visits:?} over \
21998             trees of {node_counts:?} nodes"
21999        );
22000    }
22001
22002    #[test]
22003    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
22004        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
22005        let mut parser = tree_sitter::Parser::new();
22006        parser
22007            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22008            .expect("C++ grammar");
22009        let tree = parser.parse(source, None).expect("fixture tree");
22010        let mut stack = vec![tree.root_node()];
22011        let mut recovered = None;
22012        while let Some(node) = stack.pop() {
22013            if node.kind() == "field_declaration"
22014                && let Some(field) = recovered_function_like_field_declarator(node, source)
22015            {
22016                recovered = Some((node, field.name));
22017                break;
22018            }
22019            let mut cursor = node.walk();
22020            stack.extend(node.named_children(&mut cursor));
22021        }
22022        let (declaration, name) = recovered.unwrap_or_else(|| {
22023            panic!(
22024                "pointer-wrapped macro field was not recovered: {}",
22025                tree.root_node().to_sexp()
22026            )
22027        });
22028        assert_eq!(node_text(name, source), "handshake");
22029        let recovered =
22030            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
22031        assert_eq!(recovered.pointer_depth(), 1);
22032        assert_eq!(
22033            render_cpp_field_signature(declaration, name, source),
22034            "struct handshake * handshake;"
22035        );
22036    }
22037
22038    #[test]
22039    fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
22040        let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
22041        let mut parser = tree_sitter::Parser::new();
22042        parser
22043            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22044            .expect("C++ grammar");
22045        let tree = parser.parse(source, None).expect("fixture tree");
22046        let mut stack = vec![tree.root_node()];
22047        let mut recovered = None;
22048        while let Some(node) = stack.pop() {
22049            if let Some(field) = recovered_pyobject_head_field(node, source) {
22050                recovered = Some((node, field));
22051                break;
22052            }
22053            let mut cursor = node.walk();
22054            stack.extend(node.named_children(&mut cursor));
22055        }
22056        let (declaration, recovered) = recovered.unwrap_or_else(|| {
22057            panic!(
22058                "pointer field after PyObject_HEAD was not recovered: {}",
22059                tree.root_node().to_sexp()
22060            )
22061        });
22062        assert_eq!(node_text(recovered.name, source), "image");
22063        assert_eq!(recovered.pointer_depth(), 1);
22064        assert_eq!(
22065            render_cpp_field_signature(declaration, recovered.declarator, source),
22066            "ImagingObject *image;"
22067        );
22068    }
22069}