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 brokk_bifrost_core::analyzer::common::{node_source_text, parse_source_region};
8use brokk_bifrost_core::analyzer::fq_name::{
9    FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
10};
11use brokk_bifrost_core::analyzer::model::{
12    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
13    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
14    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
15    ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
16    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
17};
18use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
19use brokk_bifrost_core::analyzer::structural::materialization::{
20    GenerationKind, MaterializationRecord,
21};
22use brokk_bifrost_core::analyzer::tree_walk::{ParentIndex, WalkControl, walk_named_tree_preorder};
23use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
24use brokk_bifrost_core::hash::{HashMap, HashSet};
25use regex::Regex;
26use tree_sitter::{Node, Parser, Tree};
27
28/// Intern one qualified-name segment in the process-global interner.
29fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
30    segment_interner().intern(text, kind)
31}
32
33/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
34/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
35/// `::` head is exactly the mixed-separator store issue #1163 is about; the
36/// structured form records each namespace component, and the equivalence check
37/// renders it natively (with `::` between adjacent Package segments) so it
38/// round-trips to the legacy string. Splitting the already-joined string here is
39/// the M1 bridge — the legacy strings stay authoritative until M3.
40fn cpp_push_package(fq: &mut FqName, package_name: &str) {
41    for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
42        fq.push(cpp_segment(component, SegmentKind::Package));
43    }
44}
45
46/// C++ namespace paths are stored `::`-joined in `package_name` (issue #1163).
47const CPP_PACKAGE_SEPARATOR: &str = "::";
48
49/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
50/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
51/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
52/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
53/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
54/// cpp-specific native rendering rule is needed for this chain.
55fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
56    let mut first = true;
57    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
58    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
59    // entry point, not re-inference of an already-structured name.
60    for component in chain.split('$').filter(|c| !c.is_empty()) {
61        let kind = if first {
62            SegmentKind::Type
63        } else {
64            SegmentKind::Nested
65        };
66        fq.push(cpp_segment(component, kind));
67        first = false;
68    }
69}
70
71/// Structured name for a C++ namespace module: every `::`-separated component is
72/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
73/// `short_name` with an empty `package_name`).
74fn cpp_namespace_fq(full_name: &str) -> FqName {
75    let mut fq = FqName::new();
76    cpp_push_package(&mut fq, full_name);
77    fq
78}
79
80/// The per-level namespace components a `namespace_definition`'s `name` field
81/// declares.
82///
83/// A C++17 nested definition (`namespace a::b::c`) parses as a
84/// `nested_namespace_specifier` whose named children are the per-level
85/// `namespace_identifier`s plus, for three or more levels, a further
86/// `nested_namespace_specifier`; the `::` separators, the optional per-level
87/// `inline`, and the leading global `::` are all anonymous tokens the walk
88/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
89/// segment path as the expanded `namespace a { namespace b { } }` form.
90///
91/// A shape outside that grammar is the deliberately ill-formed source the
92/// diagnostic corpora carry. Those keep their historical single-component
93/// reading of the raw name text, which the caller still joins to the lexical
94/// namespace exactly as before.
95fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
96    let mut components = Vec::new();
97    let mut stack = vec![node];
98    while let Some(current) = stack.pop() {
99        match current.kind() {
100            "namespace_identifier" | "identifier" => {
101                components.push(normalize_cpp_whitespace(node_text(current, source)));
102            }
103            "nested_namespace_specifier" => {
104                for index in (0..current.named_child_count()).rev() {
105                    stack.push(
106                        current
107                            .named_child(index)
108                            .expect("index below the node's own named child count"),
109                    );
110                }
111            }
112            _ => return cpp_raw_namespace_name_components(node, source),
113        }
114    }
115    if components.iter().any(String::is_empty) {
116        return cpp_raw_namespace_name_components(node, source);
117    }
118    components
119}
120
121/// The historical reading of a namespace name node: its whole source text as
122/// one component, with a leading global `::` marker dropped so the caller's
123/// global-scope handling stays the AST boundary rather than a text prefix.
124///
125/// This is the recovery path for source outside the C++ grammar, so the text it
126/// returns can carry a separator that names nothing. react-native-windows
127/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
128/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
129/// trailing separator with no tail. The caller stores this component in
130/// `short_name` and derives the fq by splitting it back apart, so an empty tail
131/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
132/// here, at the one place the malformed text enters, rather than leaving each
133/// consumer to guard (#2353, a variant of #1878).
134fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
135    let start = node
136        .child(0)
137        .filter(|child| !child.is_named() && child.kind() == "::")
138        .map_or(node.start_byte(), |marker| marker.end_byte());
139    let text = normalize_cpp_whitespace(
140        source
141            .get(start..node.end_byte())
142            .expect("namespace name node covers one source range"),
143    );
144    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
145    if text.is_empty() {
146        return Vec::new();
147    }
148    vec![text]
149}
150
151/// Return the named namespace path that structurally encloses `node`.
152///
153/// This intentionally follows namespace AST ancestors rather than inspecting
154/// source text. Anonymous namespaces are not representable in the legacy C++
155/// package field, so a path containing one fails closed.
156fn cpp_lexical_namespace_name<'tree>(
157    node: Node<'tree>,
158    source: &str,
159    ancestry: &ParentIndex<'tree>,
160) -> Option<String> {
161    let mut components = Vec::new();
162    let mut ancestor = ancestry.parent(node);
163    while let Some(current) = ancestor {
164        if current.kind() == "namespace_definition" {
165            let name_node = current.child_by_field_name("name")?;
166            let name = normalize_cpp_whitespace(node_text(name_node, source));
167            if name.is_empty() {
168                return None;
169            }
170            components.push(name);
171        }
172        ancestor = ancestry.parent(current);
173    }
174    if components.is_empty() {
175        return None;
176    }
177    components.reverse();
178    // Same shared empty-component decision as `cpp_push_package`, which splits
179    // this string back apart: an enclosing namespace whose own name node is
180    // malformed contributes a component carrying its own separator (#2353).
181    Some(
182        normalize_joined(
183            &components.join(CPP_PACKAGE_SEPARATOR),
184            CPP_PACKAGE_SEPARATOR,
185        )
186        .into_owned(),
187    )
188}
189
190/// Nested-class `$` join for short names. An anonymous parent class (empty
191/// short_name) contributes no segment: the FqName bridge drops empty
192/// components, so a bare `parent$child` join would desync `short_name` from
193/// the fq and trip the package/short boundary assert in
194/// `CodeUnit::with_signature_and_fq` (#2140).
195fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
196    if parent_short.is_empty() {
197        name.to_string()
198    } else {
199        format!("{parent_short}${name}")
200    }
201}
202
203/// Member `.` join for short names; same anonymous-parent guard as
204/// [`cpp_join_nested_short`] (#2140).
205fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
206    if parent_short.is_empty() {
207        name.to_string()
208    } else {
209        format!("{parent_short}.{name}")
210    }
211}
212
213/// Structural fq for a leaf declaration: the parent unit's fq plus this
214/// declaration's own name as one segment (or the package segments plus the
215/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
216/// chain, so a literal `$` inside a source identifier (Cython template
217/// substitution points, gcc `$`-identifiers) survives instead of corrupting
218/// the chain and tripping the package/short boundary assert (#2140).
219fn cpp_leaf_fq(
220    package_name: &str,
221    parent: Option<&CodeUnit>,
222    name: &str,
223    kind_if_nested: SegmentKind,
224    kind_if_top: SegmentKind,
225) -> FqName {
226    if let Some(parent) = parent {
227        parent
228            .fq()
229            .clone()
230            .with_pushed(cpp_segment(name, kind_if_nested))
231    } else {
232        let mut fq = FqName::new();
233        cpp_push_package(&mut fq, package_name);
234        fq.push(cpp_segment(name, kind_if_top));
235        fq
236    }
237}
238
239/// Structured name for a member unit (function, field, enumerator). The
240/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
241/// the member has an owner, by `.member`; free functions and globals have no
242/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
243/// C++ member names never contain a literal `.`, so the single `.` (if any)
244/// separates the owner chain from the member.
245pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
246    let mut fq = FqName::new();
247    cpp_push_package(&mut fq, package_name);
248    match short_name.rsplit_once('.') {
249        Some((owner_chain, member)) => {
250            cpp_push_type_chain(&mut fq, owner_chain);
251            fq.push(cpp_segment(member, SegmentKind::Member));
252        }
253        None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
254    }
255    fq
256}
257
258#[derive(Clone)]
259pub struct ScopeInfo {
260    package_name: String,
261    module: Option<CodeUnit>,
262    class_unit: Option<CodeUnit>,
263    template_signature: Option<String>,
264    template_metadata: Option<CppTemplateMetadata>,
265    declarations_are_fields: bool,
266    recovered_specialization_member_scope: bool,
267    /// Namespace targets of every `using namespace X;` directive lexically
268    /// visible at this point in the file (declaration order), threaded
269    /// forward sibling-by-sibling by the sequential container walk (see
270    /// `CppWork::Siblings`). An out-of-line member definition written as a
271    /// bare `Class::method` at file/namespace scope with no enclosing
272    /// `namespace {}` block (issue #1093, e.g. log4cxx's
273    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
274    /// const { ... }`) has no other structural signal for which namespace
275    /// actually owns `Class`; this is the best-effort candidate list used to
276    /// recover it so the definition's indexed identity matches its header
277    /// declaration's.
278    visible_using_namespaces: Vec<String>,
279}
280
281struct CppContainer<'tree> {
282    node: Node<'tree>,
283    scope: ScopeInfo,
284}
285
286struct CppNodeWork<'tree> {
287    node: Node<'tree>,
288    scope: ScopeInfo,
289}
290
291/// Cursor over one container's remaining named children, processed one at a
292/// time (rather than all at once) so a `using namespace X;` sibling can
293/// update `scope.visible_using_namespaces` for the siblings that follow it,
294/// matching real C++ using-directive semantics. Nested container work is
295/// still pushed and fully drained before the cursor resumes (stack LIFO
296/// order), preserving the original left-to-right visitation order.
297struct CppSiblingsWork<'tree> {
298    children: std::vec::IntoIter<Node<'tree>>,
299    scope: ScopeInfo,
300}
301
302enum CppWork<'tree> {
303    Container(CppContainer<'tree>),
304    Node(CppNodeWork<'tree>),
305    Siblings(CppSiblingsWork<'tree>),
306}
307
308fn class_like_name<'tree>(
309    node: Node<'tree>,
310    source: &str,
311    ancestry: &ParentIndex<'tree>,
312) -> Option<String> {
313    let best = class_like_name_from_children(node, source);
314    if let Some(parent) = ancestry.parent(node)
315        && matches!(
316            parent.kind(),
317            "declaration" | "field_declaration" | "function_definition"
318        )
319        // A class_specifier carrying its own body proves the grammar name is
320        // the real class name: a sibling declarator then declares an object
321        // (`class X {} x;`), never a displaced class name. The gate matters
322        // when the class name is itself an all-caps token (`X`, `API`) --
323        // without it the export-macro re-read below steals the object
324        // declarator's name for the class (#2283). The genuine export-macro
325        // shapes leave the class_specifier bodyless, the same invariant
326        // recover_malformed_exported_multiple_base_class already gates on.
327        && cpp_body_node(node).is_none()
328        && node
329            .child_by_field_name("name")
330            .map(|name_node| {
331                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
332            })
333            .unwrap_or(false)
334        && let Some(recovered) = exported_class_name_from_node(parent, source)
335        && best.as_deref() != Some(recovered.as_str())
336    {
337        return Some(recovered);
338    }
339    best.or_else(|| {
340        node.child_by_field_name("name")
341            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
342            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
343    })
344}
345
346fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
347    let mut grammar_name = None;
348    if let Some(name_node) = node.child_by_field_name("name") {
349        let name = normalize_cpp_whitespace(node_text(name_node, source));
350        if name.is_empty() {
351            return None;
352        }
353        if !cpp_export_macro_token(&name) {
354            return Some(name);
355        }
356        grammar_name = Some(name);
357    }
358
359    let mut best = None;
360    let mut cursor = node.walk();
361    let mut stack = Vec::new();
362    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
363        if matches!(
364            child.kind(),
365            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
366        ) {
367            break;
368        }
369        stack.push(child);
370    }
371
372    while let Some(current) = stack.pop() {
373        if matches!(current.kind(), "type_identifier" | "identifier") {
374            let name = normalize_cpp_whitespace(node_text(current, source));
375            if !name.is_empty() && !cpp_export_macro_token(&name) {
376                best = Some(name);
377            }
378            continue;
379        }
380
381        for index in (0..current.named_child_count()).rev() {
382            if let Some(child) = current.named_child(index) {
383                stack.push(child);
384            }
385        }
386    }
387    best.or(grammar_name)
388}
389
390pub fn cpp_export_macro_token(token: &str) -> bool {
391    token
392        .chars()
393        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
394}
395
396struct RecoveredExportedClass<'tree> {
397    declaration_node: Node<'tree>,
398    name: String,
399    body: Option<Node<'tree>>,
400    raw_supertypes: Option<Vec<String>>,
401    uses_initializer_body: bool,
402    /// Present only for the fragmented multiple-base export shape (issue #938).
403    /// Carries the true class-body byte region -- the members tree-sitter scattered
404    /// out of the recovered node -- so they can be reparsed and re-owned as members
405    /// rather than lost inside the truncated `initializer_list` stand-in.
406    fragmented_body: Option<FragmentedExportBody>,
407}
408
409struct RecoveredFunctionLikeExportClassPair {
410    name: String,
411    range: Range,
412    raw_supertypes: Option<Vec<String>>,
413    fragmented_body: FragmentedExportBody,
414}
415
416struct RecoveredEmbeddedFunctionLikeExportClass {
417    name: String,
418    range: Range,
419    raw_supertypes: Vec<String>,
420    fragmented_body: FragmentedExportBody,
421}
422
423/// The recovered class-body geometry for a fragmented multiple-base export class.
424/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
425/// verbatim for a region reparse (issue #941 machinery) so every recovered member
426/// keeps its exact original byte/line position. `class_range` is the full class
427/// navigation range spanning to the displaced closing brace.
428struct FragmentedExportBody {
429    reparse_start: usize,
430    reparse_end: usize,
431    class_range: Range,
432}
433
434fn recovered_fragmented_export_body(
435    body: Node<'_>,
436    class_range: Range,
437) -> Option<FragmentedExportBody> {
438    let open = body.child(0).filter(|child| child.kind() == "{")?;
439    let close = body
440        .child(body.child_count().saturating_sub(1))
441        .filter(|child| child.kind() == "}" && !child.is_missing());
442    Some(FragmentedExportBody {
443        reparse_start: open.end_byte(),
444        // A zero-width missing `}` contributes no source byte. Keep the whole
445        // body range in that case; subtracting one byte would discard the last
446        // member's semicolon and make the otherwise valid region unsafe to
447        // index. A real close token is excluded by its structured start.
448        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
449        class_range,
450    })
451}
452
453struct DisplacedFragmentNamespaceBoundary<'tree> {
454    class_close: Node<'tree>,
455    class_semicolon: Node<'tree>,
456    namespace_items: Vec<Node<'tree>>,
457}
458
459/// Result of validating a reparsed fragmented class body.  A complete tree can
460/// safely consume the whole region.  A partial tree may contain only the exact
461/// class-named constructor that tree-sitter merged into an access label; its
462/// remaining siblings must stay on the ordinary outer walk.
463enum FragmentedExportMembers {
464    Complete(Tree),
465    ConditionalConstructor(Tree),
466}
467
468#[derive(Clone, Copy)]
469struct DisplacedMacroClassTail {
470    split_index: usize,
471    class_range: Range,
472}
473
474fn recover_exported_class_declaration<'tree>(
475    node: Node<'tree>,
476    source: &str,
477) -> Option<RecoveredExportedClass<'tree>> {
478    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
479        return Some(recovered);
480    }
481
482    let class_node = first_class_like_child(node)?;
483    if let Some(name_node) = class_node.child_by_field_name("name") {
484        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
485        if cpp_export_macro_token(&class_name) {
486            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
487            // Name declarator. Only a bare declarator can be the displaced class name;
488            // wrappers describe an object whose type merely happens to look macro-like.
489            let mut cursor = node.walk();
490            if node
491                .children_by_field_name("declarator", &mut cursor)
492                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
493            {
494                return None;
495            }
496        } else if has_direct_cpp_declarator(node) {
497            return None;
498        }
499    }
500    let name = exported_class_name_from_node(class_node, source)?;
501    Some(RecoveredExportedClass {
502        declaration_node: class_node,
503        name,
504        body: cpp_body_node(class_node),
505        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
506            .then(|| extract_cpp_supertypes(class_node, source)),
507        uses_initializer_body: false,
508        fragmented_body: None,
509    })
510}
511
512fn recover_malformed_exported_base_class<'tree>(
513    node: Node<'tree>,
514    source: &str,
515) -> Option<RecoveredExportedClass<'tree>> {
516    if node.kind() != "declaration" {
517        return None;
518    }
519    let class_node = node.child_by_field_name("type")?;
520    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
521        return None;
522    }
523    let macro_name = class_node
524        .child_by_field_name("name")
525        .and_then(|name| direct_identifier_name(name, source))?;
526    if !cpp_export_macro_token(&macro_name) {
527        return None;
528    }
529
530    let mut named_cursor = node.walk();
531    let mut named = node.named_children(&mut named_cursor);
532    if named
533        .next()
534        .is_none_or(|child| !same_node(child, class_node))
535    {
536        return None;
537    }
538    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
539    if displaced.kind() != "ERROR" {
540        return None;
541    }
542    let name = displaced_exported_class_name(displaced, source)?;
543
544    let remaining = named.collect::<Vec<_>>();
545    let init = *remaining.last()?;
546    if init.kind() != "init_declarator" {
547        return None;
548    }
549    let final_base = init
550        .child_by_field_name("declarator")
551        .and_then(|base| recovered_malformed_base_name(base, source))?;
552    let body = init.child_by_field_name("value")?;
553    // A complete reduction has a real closing brace here. In Chromium's Widget
554    // declaration, tree-sitter instead emits the same direct `}` slot as a
555    // zero-width missing node where the first body macro truncates the prefix.
556    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
557        return None;
558    }
559
560    if remaining[..remaining.len() - 1]
561        .iter()
562        .any(|child| match child.kind() {
563            "qualified_identifier"
564            | "scoped_type_identifier"
565            | "type_identifier"
566            | "identifier" => false,
567            "ERROR" => !is_malformed_inheritance_access(*child, source),
568            _ => true,
569        })
570    {
571        return None;
572    }
573
574    let mut raw_supertypes = Vec::new();
575    for base in &remaining[..remaining.len() - 1] {
576        if base.kind() == "ERROR" {
577            continue;
578        }
579        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
580    }
581    raw_supertypes.push(final_base);
582
583    Some(RecoveredExportedClass {
584        declaration_node: node,
585        name,
586        body: Some(body),
587        raw_supertypes: Some(raw_supertypes),
588        uses_initializer_body: true,
589        fragmented_body: fragmented_export_body_region(node, body, source),
590    })
591}
592
593/// Locate the true class-body region for a fragmented multiple-base export class.
594///
595/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
596/// emits in place of the real class body. Tree-sitter reduces that body in one of
597/// two shapes, both of which lose the members from the recovered node:
598///
599/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
600///   a real closing brace and holds the whole body text inline. The interior between
601///   the braces reparses to the members directly.
602/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
603///   first member with a zero-width MISSING `}`; every later member -- and the real
604///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
605///   siblings. The interior runs from the opening brace to that displaced `}`.
606///
607/// Returns the interior byte range to reparse plus the full class navigation range.
608fn fragmented_export_body_region(
609    node: Node<'_>,
610    body: Node<'_>,
611    source: &str,
612) -> Option<FragmentedExportBody> {
613    let reparse_start = body.start_byte() + 1;
614    let close = direct_close_brace(body)?;
615    if close.end_byte() > close.start_byte() {
616        return Some(FragmentedExportBody {
617            reparse_start,
618            reparse_end: close.start_byte(),
619            class_range: cpp_declaration_range(node),
620        });
621    }
622    // The closing brace was displaced past the recovered node. A balanced nested
623    // class keeps its own braces, so the first lone-`}` sibling is this class's.
624    let mut sibling = node.next_named_sibling();
625    let displaced_close = loop {
626        let Some(current) = sibling else {
627            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
628        };
629        if cpp_is_stray_close_brace(current, source) {
630            break current;
631        }
632        sibling = current.next_named_sibling();
633    };
634    Some(FragmentedExportBody {
635        reparse_start,
636        reparse_end: displaced_close.start_byte(),
637        class_range: Range {
638            start_byte: node.start_byte(),
639            end_byte: displaced_close.end_byte(),
640            start_line: node.start_position().row + 1,
641            end_line: displaced_close.end_position().row + 1,
642        },
643    })
644}
645
646/// Locate the true class-body region for the export-macro class shape that
647/// tree-sitter promotes to a `function_definition`.
648///
649/// In this shape the synthetic function body closes at the first inline
650/// method, while the class's real members continue as root-level siblings until
651/// a stray `}` followed by the displaced class `;`. Reparse the complete
652/// interior so those siblings are visited with the recovered class scope.
653fn fragmented_export_function_body_region(
654    node: Node<'_>,
655    body: Node<'_>,
656    source: &str,
657    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
658) -> Option<FragmentedExportBody> {
659    let reparse_start = body.start_byte().checked_add(1)?;
660    if let Some(boundary) = displaced_namespace {
661        return Some(FragmentedExportBody {
662            reparse_start,
663            reparse_end: boundary.class_close.start_byte(),
664            class_range: Range {
665                start_byte: node.start_byte(),
666                end_byte: boundary.class_semicolon.end_byte(),
667                start_line: node.start_position().row + 1,
668                end_line: boundary.class_semicolon.end_position().row + 1,
669            },
670        });
671    }
672    let siblings = cpp_following_named_siblings(node, source);
673    let boundary = fragmented_export_sibling_class_boundary(node, source);
674    let boundary_index = boundary.and_then(|boundary| {
675        siblings
676            .iter()
677            .position(|candidate| same_node(*candidate, boundary))
678    });
679    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
680    let mut sibling_index = 0;
681    // A complete recovered class's synthetic wrapper is immediately followed
682    // by its displaced semicolon (comments and a trailing attribute macro --
683    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
684    // may sit between the body and that semicolon). Only scan for a later
685    // stray close when real member siblings intervene; otherwise every earlier
686    // complete class would borrow the next malformed class's close and claim
687    // its members. The trailing-attribute case is the gtest shape: the scan
688    // borrowed a close ~1900 lines later and re-owned a following
689    // `namespace testing { namespace internal {` block as class members,
690    // doubling the package path ("testing::internal::testing::internal") and
691    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
692    // boundary assert (#2297).
693    while let Some(current) = siblings.get(sibling_index).copied() {
694        if current.kind() == "comment" {
695            sibling_index += 1;
696            continue;
697        }
698        if is_trailing_attribute_macro_sibling(current) {
699            sibling_index += 1;
700            continue;
701        }
702        if cpp_is_stray_semicolon(current, source) {
703            return None;
704        }
705        break;
706    }
707    while let Some(current) = siblings.get(sibling_index).copied() {
708        let next = siblings.get(sibling_index + 1).copied();
709        if cpp_is_stray_close_brace(current, source)
710            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
711        {
712            let semicolon = next.expect("checked above");
713            return Some(FragmentedExportBody {
714                reparse_start,
715                reparse_end: current.start_byte(),
716                class_range: Range {
717                    start_byte: node.start_byte(),
718                    end_byte: semicolon.end_byte(),
719                    start_line: node.start_position().row + 1,
720                    end_line: semicolon.end_position().row + 1,
721                },
722            });
723        }
724        // When the final access label keeps the class close in its malformed
725        // declaration body, tree-sitter nests the lone `}` ERROR below the
726        // label instead of exposing it as a direct sibling. Search only the
727        // scattered siblings after the synthetic wrapper. The first such
728        // close is the class terminator because nested class bodies retain
729        // their own balanced class_specifier nodes.
730        if current.start_byte() >= body.end_byte()
731            && let Some(close) = cpp_nested_stray_close_brace(current, source)
732        {
733            return Some(FragmentedExportBody {
734                reparse_start,
735                reparse_end: close.start_byte(),
736                class_range: Range {
737                    start_byte: node.start_byte(),
738                    end_byte: current.end_byte(),
739                    start_line: node.start_position().row + 1,
740                    end_line: current.end_position().row + 1,
741                },
742            });
743        }
744        sibling_index += 1;
745    }
746    boundary.map(|boundary| FragmentedExportBody {
747        reparse_start,
748        reparse_end: boundary.start_byte(),
749        class_range: Range {
750            start_byte: node.start_byte(),
751            end_byte: boundary.start_byte(),
752            start_line: node.start_position().row + 1,
753            end_line: boundary.start_position().row + 1,
754        },
755    })
756}
757
758/// Find a later macro-export class that tree-sitter lifted through an enclosing
759/// preprocessor container. A class that is still a direct sibling can be a
760/// nested member of the current fragmented class, so only a changed parent is
761/// a proven boundary between the two recovered class envelopes.
762fn fragmented_export_sibling_class_boundary<'tree>(
763    node: Node<'tree>,
764    source: &str,
765) -> Option<Node<'tree>> {
766    let node_parent = node.parent()?;
767    cpp_following_named_siblings(node, source)
768        .into_iter()
769        .find(|candidate| {
770            recover_exported_class_function_definition(*candidate, source).is_some()
771                && candidate
772                    .parent()
773                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
774        })
775}
776
777/// A trailing attribute macro after a recovered class's closing brace, spelled
778/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
779/// bare identifier is never a class member (members need a type), so this
780/// sibling can only be the class's own tail (#2297).
781fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
782    if node.kind() != "expression_statement" {
783        return false;
784    }
785    let mut cursor = node.walk();
786    let mut children = node.named_children(&mut cursor);
787    children
788        .next()
789        .is_some_and(|child| child.kind() == "identifier")
790        && children.next().is_none()
791}
792
793/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
794/// export-class wrapper can place the class close inside an access-label node,
795/// so direct-sibling checks alone miss the boundary.  Walk named CST children
796/// only; the helper does not inspect source text beyond the existing structured
797/// stray-brace predicate.
798fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
799    let mut stack = vec![node];
800    while let Some(current) = stack.pop() {
801        if cpp_is_stray_close_brace(current, source) {
802            return Some(current);
803        }
804        let mut cursor = current.walk();
805        stack.extend(current.named_children(&mut cursor));
806    }
807    None
808}
809
810/// Return named siblings that follow `node`, including siblings that tree-sitter
811/// attached to an enclosing container after malformed recovery split the local
812/// declaration list. Stop at the first structurally visible class close so a
813/// later namespace or exported class cannot supply the recovery boundary.
814fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
815    let mut siblings = Vec::new();
816    let mut anchor = node;
817    while let Some(parent) = anchor.parent() {
818        let at_translation_unit = parent.kind() == "translation_unit";
819        let mut sibling = anchor.next_named_sibling();
820        while let Some(current) = sibling {
821            if at_translation_unit
822                && (current.kind() == "namespace_definition"
823                    || (current.kind() == "function_definition"
824                        && first_class_like_child(current).is_some()))
825            {
826                return siblings;
827            }
828            siblings.push(current);
829            if cpp_is_stray_close_brace(current, source) {
830                if let Some(semicolon) = current
831                    .next_named_sibling()
832                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
833                {
834                    siblings.push(semicolon);
835                }
836                return siblings;
837            }
838            if current.start_byte() >= node.end_byte()
839                && matches!(current.kind(), "ERROR" | "labeled_statement")
840                && cpp_nested_stray_close_brace(current, source).is_some()
841            {
842                return siblings;
843            }
844            sibling = current.next_named_sibling();
845        }
846        anchor = parent;
847    }
848    siblings
849}
850
851fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
852    if node.start_byte() >= class_end {
853        return false;
854    }
855    node.end_byte() <= class_end
856        || cpp_nested_stray_close_brace(node, source)
857            .is_some_and(|close| close.start_byte() == class_end)
858}
859
860/// Recover a plain class whose opening prefix is retained in one ERROR node
861/// while one or more nested class closes and the outer close are displaced to
862/// sibling `}`/`;` nodes. This is the non-export counterpart to the fragmented
863/// export-class recovery above. All boundaries come from tree-sitter nodes: the
864/// direct class tokens establish nesting depth and the displaced close nodes
865/// terminate it.
866fn fragmented_plain_class_body<'tree>(
867    node: Node<'tree>,
868    source: &str,
869) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
870    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
871        return Some(recovered);
872    }
873    let supported_container = node.kind() == "ERROR"
874        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
875    if !supported_container {
876        return None;
877    }
878    let mut cursor = node.walk();
879    let children = node.children(&mut cursor).collect::<Vec<_>>();
880    let keyword = children.first()?;
881    if !matches!(keyword.kind(), "class" | "struct" | "union") {
882        return None;
883    }
884    let name_node = children
885        .iter()
886        .copied()
887        .skip(1)
888        .find(|child| child.is_named())?;
889    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
890        return None;
891    }
892    let name = normalize_cpp_whitespace(node_text(name_node, source));
893    if name.is_empty() || cpp_export_macro_token(&name) {
894        return None;
895    }
896    let open_index = children.iter().position(|child| child.kind() == "{")?;
897    let open = children[open_index];
898    let nested_class_opens = children[open_index + 1..]
899        .iter()
900        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
901        .count();
902    let mut closes_remaining = 1 + nested_class_opens;
903    let mut sibling = node.next_named_sibling();
904    while let Some(candidate) = sibling {
905        let next = candidate.next_named_sibling();
906        if cpp_is_stray_close_brace(candidate, source) {
907            closes_remaining -= 1;
908            if closes_remaining == 0 {
909                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
910                if open.end_byte() >= candidate.start_byte() {
911                    return None;
912                }
913                return Some((
914                    node,
915                    name,
916                    FragmentedExportBody {
917                        reparse_start: open.end_byte(),
918                        reparse_end: candidate.start_byte(),
919                        class_range: Range {
920                            start_byte: node.start_byte(),
921                            end_byte: semicolon.end_byte(),
922                            start_line: node.start_position().row + 1,
923                            end_line: semicolon.end_position().row + 1,
924                        },
925                    },
926                ));
927            }
928        }
929        sibling = next;
930    }
931    None
932}
933
934pub(crate) fn recovered_fragmented_plain_class_has_body(
935    node: Node<'_>,
936    source: &str,
937    expected_name: &str,
938    expected_range: &Range,
939) -> bool {
940    fragmented_plain_class_body(node, source).is_some_and(|(_, name, fragmented)| {
941        name == expected_name
942            && fragmented.class_range.start_byte == expected_range.start_byte
943            && fragmented.class_range.end_byte == expected_range.end_byte
944    })
945}
946
947/// Recover a plain class whose parser-visible body ends inside a malformed
948/// inline member. Tree-sitter then attaches either the next real member
949/// declarator or the unfinished `else` branch directly to the outer function
950/// definition and leaves the class's actual `};` among later siblings. Those
951/// structured continuations and the close/semicolon siblings establish the
952/// complete body envelope without interpreting source text.
953fn fragmented_plain_class_declaration_body<'tree>(
954    node: Node<'tree>,
955    source: &str,
956) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
957    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
958        return None;
959    }
960    let class_node = node.child_by_field_name("type")?;
961    if !matches!(
962        class_node.kind(),
963        "class_specifier" | "struct_specifier" | "union_specifier"
964    ) {
965        return None;
966    }
967    let name_node = class_node.child_by_field_name("name")?;
968    let name = normalize_cpp_whitespace(node_text(name_node, source));
969    if name.is_empty() || cpp_export_macro_token(&name) {
970        return None;
971    }
972    let body = cpp_body_node(class_node)?;
973    if body.kind() != "field_declaration_list" {
974        return None;
975    }
976    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
977        if declarator.start_byte() < class_node.end_byte() {
978            return None;
979        }
980        let mut cursor = node.walk();
981        node.named_children(&mut cursor).any(|child| {
982            if child.kind() != "ERROR"
983                || child.start_byte() < class_node.end_byte()
984                || child.end_byte() > declarator.start_byte()
985            {
986                return false;
987            }
988            let mut cursor = child.walk();
989            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
990            let Some((return_type, attributes)) = components.split_last() else {
991                return false;
992            };
993            matches!(
994                return_type.kind(),
995                "identifier"
996                    | "type_identifier"
997                    | "primitive_type"
998                    | "decltype"
999                    | "placeholder_type_specifier"
1000            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1001                && attributes.iter().all(|attribute| {
1002                    matches!(attribute.kind(), "identifier" | "type_identifier")
1003                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1004                            *attribute, source,
1005                        )))
1006                })
1007        })
1008    } else {
1009        let mut cursor = node.walk();
1010        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1011        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1012            if same_node(*candidate_class, class_node)
1013                && continuation.kind() == "identifier"
1014                && node_text(*continuation, source) == "else"
1015                && continuation_body.kind() == "compound_statement"
1016                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1017                && continuation_body
1018                    .child(continuation_body.child_count().saturating_sub(1))
1019                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1020    };
1021    if !displaced_member {
1022        return None;
1023    }
1024    let open = body
1025        .children(&mut body.walk())
1026        .find(|child| child.kind() == "{")?;
1027    let siblings = cpp_following_named_siblings(node, source);
1028    let ordinary_boundary =
1029        siblings
1030            .iter()
1031            .copied()
1032            .enumerate()
1033            .find_map(|(close_index, close)| {
1034                cpp_is_stray_close_brace(close, source)
1035                    .then(|| {
1036                        siblings
1037                            .get(close_index + 1)
1038                            .copied()
1039                            .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1040                            .map(|semicolon| (close, semicolon))
1041                    })
1042                    .flatten()
1043            });
1044    let (close, semicolon) =
1045        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1046            (boundary.class_close, boundary.class_semicolon)
1047        } else {
1048            ordinary_boundary?
1049        };
1050    if open.end_byte() >= close.start_byte() {
1051        return None;
1052    }
1053    Some((
1054        class_node,
1055        name,
1056        FragmentedExportBody {
1057            reparse_start: open.end_byte(),
1058            reparse_end: close.start_byte(),
1059            class_range: Range {
1060                start_byte: class_node.start_byte(),
1061                end_byte: semicolon.end_byte(),
1062                start_line: class_node.start_position().row + 1,
1063                end_line: semicolon.end_position().row + 1,
1064            },
1065        },
1066    ))
1067}
1068
1069fn displaced_export_function_namespace_shape<'tree>(
1070    declaration: Node<'tree>,
1071    source: &str,
1072) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1073    let mut nested = Vec::new();
1074    for index in (0..declaration.named_child_count()).rev() {
1075        nested.push(declaration.named_child(index)?);
1076    }
1077    while let Some(current) = nested.pop() {
1078        // A recovered export class nested in this class can consume the first
1079        // parser-visible namespace close itself. In that shape the existing
1080        // later-class boundary logic already distinguishes the nested and
1081        // namespace-sibling owners; do not mistake the nested close for this
1082        // class's terminator.
1083        if recover_exported_class_function_definition(current, source).is_some() {
1084            return None;
1085        }
1086        for index in (0..current.named_child_count()).rev() {
1087            nested.push(current.named_child(index)?);
1088        }
1089    }
1090    let mut same_envelope_sibling = declaration.next_named_sibling();
1091    while let Some(current) = same_envelope_sibling {
1092        if recover_exported_class_function_definition(current, source).is_some() {
1093            return None;
1094        }
1095        same_envelope_sibling = current.next_named_sibling();
1096    }
1097    let declaration_list = declaration.parent()?;
1098    if declaration_list.kind() != "declaration_list" {
1099        return None;
1100    }
1101    let namespace = declaration_list.parent()?;
1102    if namespace.kind() != "namespace_definition"
1103        || namespace.child_by_field_name("body") != Some(declaration_list)
1104    {
1105        return None;
1106    }
1107    let class_close = direct_close_brace(declaration_list)?;
1108    let trailing_semicolon = namespace.next_named_sibling()?;
1109    if trailing_semicolon.kind() != "expression_statement"
1110        || trailing_semicolon.named_child_count() != 0
1111    {
1112        return None;
1113    }
1114    // A chain of malformed export classes can consume one parser-visible
1115    // namespace close per class. Walk through the enclosing sibling levels so
1116    // the later real namespace close remains the structural boundary; a
1117    // direct next-sibling walk stops at the first collapsed namespace and
1118    // incorrectly makes its intervening items members of this class.
1119    let siblings = cpp_following_named_siblings(namespace, source);
1120    let trailing_index = siblings
1121        .iter()
1122        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1123    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1124        recover_exported_class_function_definition(*candidate, source).is_some()
1125    }) {
1126        // Consecutive recovered classes already have an exact sibling-class
1127        // boundary. Preserve that established path, including nested export
1128        // classes, instead of interpreting the first class close as a
1129        // collapsed namespace boundary.
1130        return None;
1131    }
1132    let mut namespace_items = Vec::new();
1133    let mut nested_fragment_end = 0;
1134    for current in siblings.into_iter().skip(trailing_index + 1) {
1135        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1136        {
1137            return Some(DisplacedFragmentNamespaceBoundary {
1138                class_close,
1139                class_semicolon: trailing_semicolon,
1140                namespace_items,
1141            });
1142        }
1143        if current.start_byte() >= nested_fragment_end
1144            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1145        {
1146            nested_fragment_end = fragmented.class_range.end_byte;
1147        } else if current.start_byte() >= nested_fragment_end
1148            && recover_exported_class_function_definition(current, source).is_some()
1149            && let Some(body) = cpp_body_node(current)
1150            && let Some(fragmented) =
1151                fragmented_export_function_body_region(current, body, source, None)
1152        {
1153            nested_fragment_end = fragmented.class_range.end_byte;
1154        }
1155        namespace_items.push(current);
1156    }
1157    None
1158}
1159
1160fn displaced_fragment_namespace_boundary<'tree>(
1161    declaration: Node<'tree>,
1162    body: Node<'tree>,
1163    source: &str,
1164) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1165    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1166    let reparse_start = body.start_byte() + 1;
1167    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1168    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1169}
1170
1171/// Recover the class/namespace brace geometry for a declaration whose class
1172/// close tree-sitter consumed as the enclosing namespace close. This proof is
1173/// independent of whether every member in the class body can be reparsed: the
1174/// ordinary-tree fallback can still re-own bounded sibling declarations when
1175/// an unknown macro makes the complete body reparse unsafe.
1176fn displaced_fragment_namespace_geometry<'tree>(
1177    declaration: Node<'tree>,
1178    source: &str,
1179) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1180    // A templated class's malformed function wrapper remains beneath the
1181    // template node even though its later members have escaped to the
1182    // enclosing declaration list. Lift only that exact declaration child.
1183    let envelope = declaration
1184        .parent()
1185        .filter(|parent| {
1186            parent.kind() == "template_declaration"
1187                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1188        })
1189        .unwrap_or(declaration);
1190    let declaration_list = envelope.parent()?;
1191    if declaration_list.kind() != "declaration_list" {
1192        return None;
1193    }
1194    let namespace = declaration_list.parent()?;
1195    if namespace.kind() != "namespace_definition"
1196        || namespace.child_by_field_name("body") != Some(declaration_list)
1197    {
1198        return None;
1199    }
1200    let class_close = direct_close_brace(declaration_list)?;
1201    let trailing_semicolon = namespace.next_named_sibling()?;
1202    if trailing_semicolon.kind() != "expression_statement"
1203        || trailing_semicolon.named_child_count() != 0
1204    {
1205        return None;
1206    }
1207    let mut namespace_items = Vec::new();
1208    let mut sibling = trailing_semicolon.next_named_sibling();
1209    let mut nested_fragment_end = 0;
1210    loop {
1211        let current = sibling?;
1212        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1213        {
1214            break;
1215        }
1216        if current.start_byte() >= nested_fragment_end
1217            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1218        {
1219            nested_fragment_end = fragmented.class_range.end_byte;
1220        }
1221        namespace_items.push(current);
1222        sibling = current.next_named_sibling();
1223    }
1224    Some(DisplacedFragmentNamespaceBoundary {
1225        class_close,
1226        class_semicolon: trailing_semicolon,
1227        namespace_items,
1228    })
1229}
1230
1231/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1232fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1233    (0..node.child_count())
1234        .filter_map(|index| node.child(index))
1235        .find(|child| !child.is_named() && child.kind() == "}")
1236}
1237
1238/// A displaced lone closing brace: the class close that the fragmented multiple-base
1239/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1240fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1241    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1242}
1243
1244/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1245/// text while skipping line/block comments and string/char literals. The
1246/// exported-class recovery needs this when tree-sitter's bogus
1247/// `function_definition` body runs past the class's true closing brace and
1248/// swallows following siblings (issue #1524): the grammar tree carries no
1249/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1250/// close is located textually. Returns `None` when the text is unbalanced or
1251/// contains a construct the scanner deliberately does not interpret (raw
1252/// strings) -- callers treat that as "cannot partition" and keep the
1253/// un-split recovery.
1254fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1255    let bytes = source.as_bytes();
1256    if bytes.get(open_byte) != Some(&b'{') {
1257        return None;
1258    }
1259    let mut depth = 0usize;
1260    let mut i = open_byte;
1261    while i < bytes.len() {
1262        match bytes[i] {
1263            b'{' => depth += 1,
1264            b'}' => {
1265                depth = depth.checked_sub(1)?;
1266                if depth == 0 {
1267                    return Some(i);
1268                }
1269            }
1270            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1271                while i < bytes.len() && bytes[i] != b'\n' {
1272                    i += 1;
1273                }
1274                continue;
1275            }
1276            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1277                i += 2;
1278                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1279                    i += 1;
1280                }
1281                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1282                continue;
1283            }
1284            quote @ (b'"' | b'\'') => {
1285                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1286                // bail out rather than mis-count.
1287                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1288                    return None;
1289                }
1290                i += 1;
1291                while i < bytes.len() && bytes[i] != quote {
1292                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1293                }
1294                if i >= bytes.len() {
1295                    return None;
1296                }
1297            }
1298            _ => {}
1299        }
1300        i += 1;
1301    }
1302    None
1303}
1304
1305fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1306    let mut name = None;
1307    let mut colon_count = 0;
1308    let mut access_count = 0;
1309    for index in 0..node.child_count() {
1310        let child = node.child(index)?;
1311        match child.kind() {
1312            "identifier" | "type_identifier" if child.is_named() => {
1313                if name.is_some() {
1314                    return None;
1315                }
1316                let candidate = normalize_cpp_whitespace(node_text(child, source));
1317                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1318                    return None;
1319                }
1320                name = Some(candidate);
1321            }
1322            "template_function" | "template_type" if child.is_named() => {
1323                if name.is_some() {
1324                    return None;
1325                }
1326                let candidate = child
1327                    .child_by_field_name("name")
1328                    .and_then(|name| direct_identifier_name(name, source))?;
1329                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1330                    return None;
1331                }
1332                name = Some(candidate);
1333            }
1334            ":" if !child.is_named() => colon_count += 1,
1335            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1336            _ => return None,
1337        }
1338    }
1339    (colon_count == 1 && access_count == 1)
1340        .then_some(name)
1341        .flatten()
1342}
1343
1344fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1345    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1346        return false;
1347    }
1348    node.named_child(0)
1349        .and_then(|child| direct_identifier_name(child, source))
1350        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1351}
1352
1353fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1354    (0..node.child_count()).any(|index| {
1355        node.child(index)
1356            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1357    })
1358}
1359
1360fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1361    match node.kind() {
1362        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1363            recovered_base_atom(node, source)
1364        }
1365        "template_type" | "template_function" => node
1366            .child_by_field_name("name")
1367            .and_then(|name| recovered_malformed_base_name(name, source)),
1368        "ERROR" => None,
1369        "qualified_identifier" | "scoped_type_identifier" => {
1370            let suffix = node
1371                .child_by_field_name("name")
1372                .and_then(|name| recovered_malformed_base_name(name, source))?;
1373            let scope = node
1374                .child_by_field_name("scope")
1375                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1376            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1377                malformed_qualified_prefix(node, source)?
1378            } else {
1379                if malformed_qualified_prefix(node, source).is_some() {
1380                    return None;
1381                }
1382                scope
1383            };
1384            Some(format!("{prefix}::{suffix}"))
1385        }
1386        _ => None,
1387    }
1388}
1389
1390fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1391    if !matches!(
1392        node.kind(),
1393        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1394    ) {
1395        return None;
1396    }
1397    let name = normalize_cpp_whitespace(node_text(node, source));
1398    (!name.is_empty()).then_some(name)
1399}
1400
1401fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1402    let mut prefix = None;
1403    let mut cursor = node.walk();
1404    for error in node
1405        .named_children(&mut cursor)
1406        .filter(|child| child.kind() == "ERROR")
1407    {
1408        if error.named_child_count() != 1 || prefix.is_some() {
1409            return None;
1410        }
1411        prefix = error
1412            .named_child(0)
1413            .and_then(|child| recovered_base_atom(child, source));
1414        prefix.as_ref()?;
1415    }
1416    prefix
1417}
1418
1419fn recover_exported_class_function_definition<'tree>(
1420    node: Node<'tree>,
1421    source: &str,
1422) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1423    if node.kind() != "function_definition" {
1424        return None;
1425    }
1426    if let Some(prefix) = node.prev_named_sibling()
1427        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
1428        && recovered.range.end_byte == node.end_byte()
1429    {
1430        return Some((node, recovered.name, recovered.raw_supertypes));
1431    }
1432    let type_node = node.child_by_field_name("type")?;
1433    let declarator = node.child_by_field_name("declarator")?;
1434
1435    if matches!(
1436        type_node.kind(),
1437        "class_specifier" | "struct_specifier" | "union_specifier"
1438    ) {
1439        let type_name = type_node
1440            .child_by_field_name("name")
1441            .and_then(|name| direct_identifier_name(name, source));
1442        let exported_macro_type = type_name
1443            .as_ref()
1444            .is_some_and(|name| cpp_export_macro_token(name));
1445        if exported_macro_type {
1446            let mut cursor = node.walk();
1447            let errors_before_declarator = node
1448                .named_children(&mut cursor)
1449                .filter(|child| {
1450                    child.kind() == "ERROR"
1451                        && child.start_byte() >= type_node.end_byte()
1452                        && child.end_byte() <= declarator.start_byte()
1453                })
1454                .collect::<Vec<_>>();
1455            if let Some(name) = errors_before_declarator
1456                .iter()
1457                .find_map(|error| displaced_exported_class_name(*error, source))
1458            {
1459                let raw_supertypes = errors_before_declarator
1460                    .iter()
1461                    .any(|error| malformed_inheritance_syntax(*error))
1462                    .then(|| recovered_malformed_base_name(declarator, source))
1463                    .flatten()
1464                    .map(|base| vec![base]);
1465                return Some((node, name, raw_supertypes));
1466            }
1467            if errors_before_declarator
1468                .iter()
1469                .any(|error| malformed_inheritance_syntax(*error))
1470            {
1471                return None;
1472            }
1473        }
1474        if !exported_macro_type
1475            && let Some(name) = type_name
1476            && !cpp_export_macro_token(&name)
1477            && let Some(base) =
1478                recovered_postfix_export_macro_base(node, type_node, declarator, source)
1479        {
1480            return Some((node, name, Some(vec![base])));
1481        }
1482        if let Some(name) = direct_identifier_name(declarator, source)
1483            && exported_macro_type
1484            && !cpp_export_macro_token(&name)
1485        {
1486            let raw_supertypes = exported_macro_type
1487                .then(|| recovered_single_base_after_declarator(node, declarator, source))
1488                .flatten()
1489                .map(|base| vec![base]);
1490            return Some((node, name, raw_supertypes));
1491        }
1492        if declarator.kind() == "parenthesized_declarator"
1493            && type_node
1494                .child_by_field_name("name")
1495                .and_then(|name| direct_identifier_name(name, source))
1496                .is_some_and(|name| cpp_export_macro_token(&name))
1497        {
1498            if let Some((name, base)) =
1499                recovered_function_like_export_class_owner(declarator, source)
1500            {
1501                return Some((node, name, Some(vec![base])));
1502            }
1503            let body_start = node
1504                .child_by_field_name("body")
1505                .map(|body| body.start_byte())
1506                .unwrap_or(node.end_byte());
1507            let mut cursor = node.walk();
1508            if let Some(name) = node
1509                .named_children(&mut cursor)
1510                .filter(|child| {
1511                    child.kind() == "ERROR"
1512                        && child.start_byte() >= declarator.end_byte()
1513                        && child.end_byte() <= body_start
1514                })
1515                .find_map(|error| declarator_name_from_node(error, source))
1516            {
1517                return Some((node, name, None));
1518            }
1519        }
1520    }
1521
1522    let declarator_text = direct_identifier_name(declarator, source)?;
1523    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1524        return None;
1525    }
1526    class_identifier_before_body(node, source).map(|name| (node, name, None))
1527}
1528
1529fn recovered_function_like_export_class_owner(
1530    declarator: Node<'_>,
1531    source: &str,
1532) -> Option<(String, String)> {
1533    if declarator.kind() != "parenthesized_declarator" {
1534        return None;
1535    }
1536    let mut cursor = declarator.walk();
1537    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
1538    let [prefix, base] = children.as_slice() else {
1539        return None;
1540    };
1541    if prefix.kind() != "ERROR"
1542        || !matches!(
1543            base.kind(),
1544            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
1545        )
1546    {
1547        return None;
1548    }
1549    let mut identifiers = Vec::new();
1550    let mut prefix_cursor = prefix.walk();
1551    for child in prefix.named_children(&mut prefix_cursor) {
1552        match child.kind() {
1553            "number_literal" | "string_literal" | "char_literal" => {}
1554            "identifier" | "type_identifier" => {
1555                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
1556            }
1557            _ => return None,
1558        }
1559    }
1560    let name = match identifiers.as_slice() {
1561        [name] => name.clone(),
1562        [name, final_token] if final_token == "final" => name.clone(),
1563        _ => return None,
1564    };
1565    if name.is_empty() || cpp_export_macro_token(&name) {
1566        return None;
1567    }
1568    let base = recovered_malformed_base_name(*base, source)?;
1569    Some((name, base))
1570}
1571
1572fn recover_function_like_export_class_pair(
1573    node: Node<'_>,
1574    source: &str,
1575) -> Option<RecoveredFunctionLikeExportClassPair> {
1576    if node.kind() != "ERROR" {
1577        return None;
1578    }
1579    let class_node = first_class_like_child(node)?;
1580    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1581        return None;
1582    }
1583    let macro_name = class_node
1584        .child_by_field_name("name")
1585        .and_then(|name| direct_identifier_name(name, source))?;
1586    if !cpp_export_macro_token(&macro_name) {
1587        return None;
1588    }
1589    let sibling = node.next_named_sibling()?;
1590    let (name, raw_supertypes, body) = match sibling.kind() {
1591        "expression_statement" => {
1592            let compound = sibling.named_child(0)?;
1593            if compound.kind() != "compound_literal_expression" {
1594                return None;
1595            }
1596            let body = compound.child_by_field_name("value")?;
1597            if body.kind() != "initializer_list" {
1598                return None;
1599            }
1600            (
1601                compound
1602                    .child_by_field_name("type")
1603                    .and_then(|name| direct_identifier_name(name, source))?,
1604                None,
1605                body,
1606            )
1607        }
1608        "labeled_statement" => {
1609            let label = sibling.child_by_field_name("label")?;
1610            if label.kind() != "statement_identifier" {
1611                return None;
1612            }
1613            let name = normalize_cpp_whitespace(node_text(label, source));
1614            let declaration = sibling
1615                .named_children(&mut sibling.walk())
1616                .find(|child| child.kind() == "declaration")?;
1617            let access = declaration.child_by_field_name("type")?;
1618            if !matches!(
1619                node_text(access, source),
1620                "public" | "protected" | "private"
1621            ) {
1622                return None;
1623            }
1624            let init = declaration.child_by_field_name("declarator")?;
1625            if init.kind() != "init_declarator" {
1626                return None;
1627            }
1628            let body = init.child_by_field_name("value")?;
1629            if body.kind() != "initializer_list" {
1630                return None;
1631            }
1632            let base = init
1633                .child_by_field_name("declarator")
1634                .and_then(|base| recovered_malformed_base_name(base, source))?;
1635            (name, Some(vec![base]), body)
1636        }
1637        "function_definition" => {
1638            let type_node = sibling.child_by_field_name("type")?;
1639            let body = sibling.child_by_field_name("body")?;
1640            if body.kind() != "compound_statement" {
1641                return None;
1642            }
1643            let name = direct_identifier_name(type_node, source)?;
1644            let mut bases = Vec::new();
1645            let mut sibling_cursor = sibling.walk();
1646            for child in sibling.named_children(&mut sibling_cursor) {
1647                if same_node(child, type_node) || same_node(child, body) {
1648                    continue;
1649                }
1650                if child.kind() == "ERROR" {
1651                    let mut error_cursor = child.walk();
1652                    bases.extend(
1653                        child
1654                            .named_children(&mut error_cursor)
1655                            .filter_map(|part| recovered_malformed_base_name(part, source)),
1656                    );
1657                } else if let Some(base) = recovered_malformed_base_name(child, source) {
1658                    bases.push(base);
1659                }
1660            }
1661            bases.retain(|base| {
1662                !matches!(base.as_str(), "final" | "public" | "protected" | "private")
1663            });
1664            let [base] = bases.as_slice() else {
1665                return None;
1666            };
1667            (name, Some(vec![base.clone()]), body)
1668        }
1669        _ => return None,
1670    };
1671    if name.is_empty() || cpp_export_macro_token(&name) {
1672        return None;
1673    }
1674    let range = Range {
1675        start_byte: node.start_byte(),
1676        end_byte: sibling.end_byte(),
1677        start_line: node.start_position().row + 1,
1678        end_line: sibling.end_position().row + 1,
1679    };
1680    Some(RecoveredFunctionLikeExportClassPair {
1681        name,
1682        raw_supertypes,
1683        range,
1684        fragmented_body: recovered_fragmented_export_body(body, range)?,
1685    })
1686}
1687
1688/// Recover a function-like export-macro class that tree-sitter embedded in a
1689/// larger error after an earlier malformed class body. The grammar still
1690/// preserves every part of the class head: the `class` token, export macro
1691/// identifier and argument list, displaced class identifier, access specifier,
1692/// base field, and initializer-list-shaped body. Match only that complete
1693/// structured sequence and keep each recovered class's exact byte envelope.
1694fn recover_embedded_function_like_export_classes(
1695    node: Node<'_>,
1696    source: &str,
1697) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
1698    if node.kind() != "ERROR" {
1699        return Vec::new();
1700    }
1701
1702    let mut nodes = Vec::new();
1703    let mut stack = vec![node];
1704    while let Some(current) = stack.pop() {
1705        nodes.push(current);
1706        for index in (0..current.child_count()).rev() {
1707            stack.push(
1708                current
1709                    .child(index)
1710                    .expect("index below the node's own child count"),
1711            );
1712        }
1713    }
1714    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
1715
1716    let mut recovered = Vec::new();
1717    for class_token in nodes
1718        .iter()
1719        .copied()
1720        .filter(|child| !child.is_named() && child.kind() == "class")
1721    {
1722        let row = class_token.start_position().row;
1723        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
1724            candidate.start_byte() >= class_token.end_byte()
1725                && candidate.start_position().row == row
1726                && matches!(
1727                    candidate.kind(),
1728                    "identifier" | "type_identifier" | "field_identifier"
1729                )
1730                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*candidate, source)))
1731        }) else {
1732            continue;
1733        };
1734        let Some(arguments) = nodes.iter().copied().find(|candidate| {
1735            candidate.kind() == "argument_list"
1736                && candidate.start_byte() >= macro_name.end_byte()
1737                && candidate.start_position().row == row
1738        }) else {
1739            continue;
1740        };
1741        let Some(name_node) = nodes.iter().copied().find(|candidate| {
1742            candidate.kind() == "identifier"
1743                && candidate.start_byte() >= arguments.end_byte()
1744                && candidate.start_position().row == row
1745        }) else {
1746            continue;
1747        };
1748        let name = normalize_cpp_whitespace(node_text(name_node, source));
1749        if name.is_empty() || cpp_export_macro_token(&name) {
1750            continue;
1751        }
1752        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
1753            candidate.kind() == "field_initializer"
1754                && candidate.start_byte() >= name_node.end_byte()
1755                && candidate
1756                    .child_by_field_name("field")
1757                    .or_else(|| candidate.named_child(0))
1758                    .is_some()
1759                && candidate
1760                    .child_by_field_name("value")
1761                    .or_else(|| {
1762                        let mut cursor = candidate.walk();
1763                        candidate
1764                            .named_children(&mut cursor)
1765                            .find(|child| child.kind() == "initializer_list")
1766                    })
1767                    .is_some_and(|value| value.kind() == "initializer_list")
1768        }) else {
1769            continue;
1770        };
1771        let has_access = nodes.iter().copied().any(|candidate| {
1772            candidate.start_byte() >= name_node.end_byte()
1773                && candidate.end_byte() <= base_initializer.start_byte()
1774                && matches!(
1775                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
1776                    "public" | "protected" | "private"
1777                )
1778        });
1779        if !has_access {
1780            continue;
1781        }
1782        let Some(base_node) = base_initializer
1783            .child_by_field_name("field")
1784            .or_else(|| base_initializer.named_child(0))
1785        else {
1786            continue;
1787        };
1788        let Some(base) = recovered_malformed_base_name(base_node, source) else {
1789            continue;
1790        };
1791        let body = base_initializer
1792            .child_by_field_name("value")
1793            .or_else(|| {
1794                let mut cursor = base_initializer.walk();
1795                base_initializer
1796                    .named_children(&mut cursor)
1797                    .find(|child| child.kind() == "initializer_list")
1798            })
1799            .expect("initializer-list value checked above");
1800        let range = Range {
1801            start_byte: class_token.start_byte(),
1802            end_byte: body.end_byte(),
1803            start_line: class_token.start_position().row + 1,
1804            end_line: body.end_position().row + 1,
1805        };
1806        if recovered
1807            .iter()
1808            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
1809                existing.name == name && existing.range == range
1810            })
1811        {
1812            continue;
1813        }
1814        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
1815            name,
1816            range,
1817            raw_supertypes: vec![base],
1818            fragmented_body: match recovered_fragmented_export_body(body, range) {
1819                Some(fragmented) => fragmented,
1820                None => continue,
1821            },
1822        });
1823    }
1824    recovered
1825}
1826
1827fn lifted_function_like_export_class_namespace<'tree>(
1828    node: Node<'tree>,
1829    source: &str,
1830    ancestry: &ParentIndex<'tree>,
1831) -> Option<String> {
1832    // A long malformed body can embed the next exported class several levels
1833    // below a bogus top-level function_definition. Compare namespace evidence
1834    // against that top-level envelope, not only the recovered ERROR's direct
1835    // parent. The source tree still proves the same boundary: one earlier
1836    // malformed namespace and one later standalone closing brace.
1837    let mut anchor = node;
1838    let parent = loop {
1839        let parent = ancestry.parent(anchor)?;
1840        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
1841            break parent;
1842        }
1843        anchor = parent;
1844    };
1845    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
1846        sibling.start_byte() > anchor.end_byte()
1847            && sibling.kind() == "ERROR"
1848            && sibling.named_child_count() == 0
1849            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
1850    });
1851    if !has_later_close {
1852        return None;
1853    }
1854    let candidates = parent
1855        .named_children(&mut parent.walk())
1856        .filter(|sibling| {
1857            sibling.kind() == "namespace_definition"
1858                && sibling.has_error()
1859                && sibling.end_byte() < anchor.start_byte()
1860        })
1861        .filter_map(|namespace| {
1862            namespace
1863                .child_by_field_name("name")
1864                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
1865                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
1866        })
1867        .collect::<Vec<_>>();
1868    let [namespace] = candidates.as_slice() else {
1869        return None;
1870    };
1871    Some(namespace.clone())
1872}
1873
1874pub(crate) fn recovered_function_like_export_class_pair_has_body(
1875    node: Node<'_>,
1876    source: &str,
1877    identifier: &str,
1878    range: &Range,
1879) -> bool {
1880    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
1881        recovered.name == identifier
1882            && recovered.range.start_byte == range.start_byte
1883            && recovered.range.end_byte == range.end_byte
1884    })
1885}
1886
1887pub(crate) fn recovered_embedded_function_like_export_class_has_body(
1888    node: Node<'_>,
1889    source: &str,
1890    identifier: &str,
1891    range: &Range,
1892) -> bool {
1893    recover_embedded_function_like_export_classes(node, source)
1894        .into_iter()
1895        .any(|recovered| {
1896            recovered.name == identifier
1897                && recovered.range.start_byte == range.start_byte
1898                && recovered.range.end_byte == range.end_byte
1899        })
1900}
1901
1902/// Whether `node` is the base type displaced into the declarator field of an
1903/// export-macro class that tree-sitter represented as a declaration or
1904/// function definition.
1905///
1906/// Declaration extraction already recovers this exact malformed envelope as a
1907/// class and records the declarator as its base. Reference extraction must use
1908/// the same structural fact instead of treating the node as a function name.
1909pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
1910    if !matches!(
1911        node.kind(),
1912        "qualified_identifier" | "scoped_type_identifier" | "template_type"
1913    ) {
1914        return false;
1915    }
1916    if let Some(function) = node.parent().filter(|parent| {
1917        parent.kind() == "function_definition"
1918            && parent
1919                .child_by_field_name("declarator")
1920                .is_some_and(|declarator| same_node(declarator, node))
1921    }) {
1922        return recover_exported_class_function_definition(function, source)
1923            .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
1924    }
1925    let Some(initializer) = node.parent().filter(|parent| {
1926        parent.kind() == "init_declarator"
1927            && parent
1928                .child_by_field_name("declarator")
1929                .is_some_and(|declarator| same_node(declarator, node))
1930    }) else {
1931        return false;
1932    };
1933    initializer
1934        .parent()
1935        .filter(|parent| parent.kind() == "declaration")
1936        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
1937        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
1938}
1939
1940/// Recover the class item from a region reparse that still carries the
1941/// sentinel's synthetic function envelope.  An unknown class attribute can
1942/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
1943/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
1944/// then nested below that function, so direct class-child lookup is not enough.
1945struct CppSentinelReparsedClass<'tree> {
1946    declaration_node: Node<'tree>,
1947    name: String,
1948    body: Node<'tree>,
1949    raw_supertypes: Option<Vec<String>>,
1950}
1951
1952fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1953    let mut cursor = root.walk();
1954    root.named_children(&mut cursor)
1955        .find(|child| child.kind() != "comment")
1956        .filter(|child| child.kind() == "template_declaration")
1957}
1958
1959fn cpp_sentinel_reparsed_class<'tree>(
1960    root: Node<'tree>,
1961    template_node: Option<Node<'tree>>,
1962    source: &str,
1963    ancestry: &ParentIndex<'tree>,
1964) -> Option<CppSentinelReparsedClass<'tree>> {
1965    let container = template_node.unwrap_or(root);
1966    let mut cursor = container.walk();
1967    for child in container.named_children(&mut cursor) {
1968        if matches!(
1969            child.kind(),
1970            "class_specifier" | "struct_specifier" | "union_specifier"
1971        ) {
1972            let name = class_like_name(child, source, ancestry)?;
1973            let body = cpp_body_node(child)?;
1974            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1975                .then(|| extract_cpp_supertypes(child, source));
1976            return Some(CppSentinelReparsedClass {
1977                declaration_node: child,
1978                name,
1979                body,
1980                raw_supertypes,
1981            });
1982        }
1983        if child.kind() == "declaration"
1984            && let Some(class_node) = first_class_like_child(child)
1985        {
1986            let name = class_like_name(class_node, source, ancestry)?;
1987            let body = cpp_body_node(class_node)?;
1988            let raw_supertypes =
1989                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1990                    .then(|| extract_cpp_supertypes(class_node, source));
1991            return Some(CppSentinelReparsedClass {
1992                declaration_node: class_node,
1993                name,
1994                body,
1995                raw_supertypes,
1996            });
1997        }
1998        // Only when the nested class item carries its own body. A bodyless
1999        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
2000        // a function definition -- is the export-macro shape recovered by the
2001        // next arm, and must fall through to it rather than abort the search.
2002        if child.kind() == "function_definition"
2003            && let Some(class_node) = first_class_like_child(child)
2004            && let Some(body) = cpp_body_node(class_node)
2005            && let Some(name) = class_like_name(class_node, source, ancestry)
2006        {
2007            let raw_supertypes =
2008                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
2009                    .then(|| extract_cpp_supertypes(class_node, source));
2010            return Some(CppSentinelReparsedClass {
2011                declaration_node: class_node,
2012                name,
2013                body,
2014                raw_supertypes,
2015            });
2016        }
2017        if child.kind() == "function_definition"
2018            && let Some((_, name, raw_supertypes)) =
2019                recover_exported_class_function_definition(child, source)
2020        {
2021            let body = cpp_body_node(child)?;
2022            return Some(CppSentinelReparsedClass {
2023                declaration_node: child,
2024                name,
2025                body,
2026                raw_supertypes,
2027            });
2028        }
2029    }
2030    None
2031}
2032
2033fn recovered_postfix_export_macro_base(
2034    node: Node<'_>,
2035    type_node: Node<'_>,
2036    declarator: Node<'_>,
2037    source: &str,
2038) -> Option<String> {
2039    let mut cursor = node.walk();
2040    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
2041        child.kind() == "ERROR"
2042            && child.start_byte() >= type_node.end_byte()
2043            && child.end_byte() <= declarator.start_byte()
2044            && postfix_export_macro_inheritance(*child, source)
2045    });
2046    malformed_clauses.next()?;
2047    if malformed_clauses.next().is_some() {
2048        return None;
2049    }
2050    recovered_malformed_base_name(declarator, source)
2051}
2052
2053fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
2054    let mut macro_count = 0;
2055    let mut colon_count = 0;
2056    let mut access_count = 0;
2057    for index in 0..node.child_count() {
2058        let Some(child) = node.child(index) else {
2059            return false;
2060        };
2061        match child.kind() {
2062            "identifier" | "type_identifier" if child.is_named() => {
2063                let candidate = normalize_cpp_whitespace(node_text(child, source));
2064                if !cpp_export_macro_token(&candidate) {
2065                    return false;
2066                }
2067                macro_count += 1;
2068            }
2069            ":" if !child.is_named() => colon_count += 1,
2070            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
2071            _ => return false,
2072        }
2073    }
2074    macro_count == 1 && colon_count == 1 && access_count == 1
2075}
2076
2077fn recovered_single_base_after_declarator(
2078    node: Node<'_>,
2079    declarator: Node<'_>,
2080    source: &str,
2081) -> Option<String> {
2082    let body_start = node
2083        .child_by_field_name("body")
2084        .map(|body| body.start_byte())
2085        .unwrap_or(node.end_byte());
2086    let mut cursor = node.walk();
2087    let mut bases = node
2088        .named_children(&mut cursor)
2089        .filter(|child| {
2090            child.kind() == "ERROR"
2091                && child.start_byte() >= declarator.end_byte()
2092                && child.end_byte() <= body_start
2093        })
2094        .filter_map(|error| displaced_exported_class_name(error, source));
2095    let base = bases.next()?;
2096    bases.next().is_none().then_some(base)
2097}
2098
2099fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
2100    (0..node.child_count()).any(|index| {
2101        node.child(index)
2102            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
2103    })
2104}
2105
2106pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
2107    recover_exported_class_function_definition(node, source).is_some()
2108}
2109
2110fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
2111    matches!(
2112        kind,
2113        "ERROR"
2114            | "preproc_if"
2115            | "preproc_ifdef"
2116            | "preproc_ifndef"
2117            | "preproc_else"
2118            | "preproc_elif"
2119    ) || (kind == "labeled_statement" && in_class_scope)
2120}
2121
2122pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
2123    if node.kind() != "declaration" {
2124        return false;
2125    }
2126    let mut ancestor = node.parent();
2127    while let Some(container) = ancestor {
2128        match container.kind() {
2129            "compound_statement" => {
2130                return container.parent().is_some_and(|class_container| {
2131                    is_recovered_exported_class_container(class_container, source)
2132                });
2133            }
2134            // These containers preserve ScopeInfo in visit_node. declaration_list is
2135            // the body container selected for a linkage specification.
2136            "template_declaration" | "linkage_specification" | "declaration_list" => {}
2137            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
2138            _ => return false,
2139        }
2140        ancestor = container.parent();
2141    }
2142    false
2143}
2144
2145pub fn recovered_exported_class_has_body(
2146    node: Node<'_>,
2147    source: &str,
2148    expected_name: &str,
2149) -> Option<bool> {
2150    match node.kind() {
2151        "function_definition" => {
2152            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
2153            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
2154        }
2155        "declaration" | "field_declaration" => {
2156            let recovered = recover_exported_class_declaration(node, source)?;
2157            (recovered.name == expected_name).then(|| recovered.body.is_some())
2158        }
2159        _ => None,
2160    }
2161}
2162
2163fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
2164    let body_start = node
2165        .child_by_field_name("body")
2166        .map(|body| body.start_byte())
2167        .unwrap_or(node.end_byte());
2168    let mut stack = Vec::new();
2169    for index in (0..node.named_child_count()).rev() {
2170        let Some(child) = node.named_child(index) else {
2171            continue;
2172        };
2173        if child.start_byte() >= body_start {
2174            continue;
2175        }
2176        stack.push(child);
2177    }
2178
2179    let mut best = None;
2180    while let Some(current) = stack.pop() {
2181        if matches!(current.kind(), "identifier" | "type_identifier") {
2182            let name = normalize_cpp_whitespace(node_text(current, source));
2183            if !name.is_empty()
2184                && !cpp_export_macro_token(&name)
2185                && !matches!(name.as_str(), "class" | "struct" | "union")
2186            {
2187                best = Some(name);
2188            }
2189            continue;
2190        }
2191
2192        for index in (0..current.named_child_count()).rev() {
2193            if let Some(child) = current.named_child(index)
2194                && child.start_byte() < body_start
2195            {
2196                stack.push(child);
2197            }
2198        }
2199    }
2200    best
2201}
2202
2203fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
2204    if node.kind() == "declaration"
2205        && node
2206            .child_by_field_name("type")
2207            .or_else(|| first_class_like_child(node))
2208            .is_some_and(|type_node| {
2209                matches!(
2210                    type_node.kind(),
2211                    "class_specifier" | "struct_specifier" | "union_specifier"
2212                )
2213            })
2214        && let Some(name) = node
2215            .child_by_field_name("declarator")
2216            .and_then(|declarator| declarator_name_from_node(declarator, source))
2217        && !cpp_export_macro_token(&name)
2218    {
2219        return Some(name);
2220    }
2221
2222    if node.kind() == "function_definition"
2223        && node.child_by_field_name("type").is_some_and(|type_node| {
2224            matches!(
2225                type_node.kind(),
2226                "class_specifier" | "struct_specifier" | "union_specifier"
2227            )
2228        })
2229        && let Some(name) = node
2230            .child_by_field_name("declarator")
2231            .and_then(|declarator| direct_identifier_name(declarator, source))
2232        && !cpp_export_macro_token(&name)
2233    {
2234        return Some(name);
2235    }
2236
2237    let class_node = if matches!(
2238        node.kind(),
2239        "class_specifier" | "struct_specifier" | "union_specifier"
2240    ) {
2241        node
2242    } else {
2243        first_class_like_child(node)?
2244    };
2245    class_like_name_from_children(class_node, source)
2246}
2247
2248fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
2249    if !matches!(
2250        node.kind(),
2251        "identifier" | "field_identifier" | "type_identifier"
2252    ) {
2253        return None;
2254    }
2255    let name = normalize_cpp_whitespace(node_text(node, source));
2256    (!name.is_empty()).then_some(name)
2257}
2258
2259fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
2260    match node.kind() {
2261        "identifier" | "field_identifier" | "type_identifier" => {
2262            let name = normalize_cpp_whitespace(node_text(node, source));
2263            (!name.is_empty()).then_some(name)
2264        }
2265        _ => {
2266            let mut cursor = node.walk();
2267            node.named_children(&mut cursor)
2268                .find_map(|child| declarator_name_from_node(child, source))
2269        }
2270    }
2271}
2272
2273fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
2274    let mut cursor = node.walk();
2275    node.named_children(&mut cursor).find(|child| {
2276        matches!(
2277            child.kind(),
2278            "class_specifier" | "struct_specifier" | "union_specifier"
2279        )
2280    })
2281}
2282
2283/// Push a container's children as a `Siblings` cursor rather than snapshotting
2284/// them all with one shared scope: children are visited one at a time so a
2285/// `using namespace X;` sibling can affect the scope threaded to the siblings
2286/// that textually follow it (issue #1093).
2287fn push_cpp_container_work<'tree>(
2288    node: Node<'tree>,
2289    scope: ScopeInfo,
2290    stack: &mut Vec<CppWork<'tree>>,
2291) {
2292    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
2293}
2294
2295/// Materialize one selected named-child range with a tree-sitter cursor. The
2296/// cursor advances linearly across the parent's concrete children; repeatedly
2297/// asking for `named_child(index)` is quadratic on very wide generated nodes.
2298fn push_cpp_sibling_range<'tree>(
2299    parent: Node<'tree>,
2300    start_index: usize,
2301    end_index: usize,
2302    scope: ScopeInfo,
2303    stack: &mut Vec<CppWork<'tree>>,
2304) {
2305    let mut cursor = parent.walk();
2306    let children = parent
2307        .named_children(&mut cursor)
2308        .skip(start_index)
2309        .take(end_index.saturating_sub(start_index))
2310        .collect::<Vec<_>>()
2311        .into_iter();
2312    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
2313}
2314
2315/// Advance a `Siblings` cursor by one child: dispatch the current child under
2316/// the scope accumulated from its *earlier* siblings, then push a
2317/// continuation for the remaining siblings carrying the scope updated for
2318/// *this* child (only `using namespace X;` directives change it). Pushing the
2319/// continuation before the current child's own node work means the current
2320/// child's subtree fully drains (LIFO) before the next sibling is visited,
2321/// preserving left-to-right order.
2322fn advance_cpp_siblings<'tree>(
2323    mut siblings: CppSiblingsWork<'tree>,
2324    source: &str,
2325    stack: &mut Vec<CppWork<'tree>>,
2326) {
2327    let Some(child) = siblings.children.next() else {
2328        return;
2329    };
2330    let current_scope = siblings.scope.clone();
2331    if let Some(namespace) = cpp_using_namespace_target(child, source) {
2332        siblings.scope.visible_using_namespaces.push(namespace);
2333    }
2334    if !siblings.children.as_slice().is_empty() {
2335        stack.push(CppWork::Siblings(siblings));
2336    }
2337    stack.push(CppWork::Node(CppNodeWork {
2338        node: child,
2339        scope: current_scope,
2340    }));
2341}
2342
2343/// The namespace target of a `using namespace X;` directive, or `None` for
2344/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
2345/// kind. Distinguished structurally by the presence of the grammar's literal
2346/// `namespace` keyword token among the node's children -- not by inspecting
2347/// source text -- so it never misreads a member-importing using-declaration
2348/// as a namespace directive.
2349fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
2350    if node.kind() != "using_declaration" {
2351        return None;
2352    }
2353    let mut cursor = node.walk();
2354    let is_namespace_directive = node
2355        .children(&mut cursor)
2356        .any(|child| child.kind() == "namespace");
2357    if !is_namespace_directive {
2358        return None;
2359    }
2360    let target = node.named_child(0)?;
2361    // A leading `::` is the explicit-global marker, not part of the namespace
2362    // path (`using namespace ::std::chrono;`). Drop that AST token before
2363    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
2364    // keeps: storing the marker verbatim desynced the legacy package string from
2365    // the FqName bridge, which splits on `::` and drops the empty leading
2366    // component, tripping the package/short boundary assert when a bare-owner
2367    // out-of-line definition borrowed the directive's namespace (#1093 path).
2368    let start = target
2369        .child(0)
2370        .filter(|child| !child.is_named() && child.kind() == "::")
2371        .map_or(target.start_byte(), |marker| marker.end_byte());
2372    let text = normalize_cpp_whitespace(
2373        source
2374            .get(start..target.end_byte())
2375            .expect("using-directive target covers one source range"),
2376    );
2377    (!text.is_empty()).then_some(text)
2378}
2379
2380/// Every `using namespace X;` directive target in a file, in source order, for
2381/// resolution-time consumers that need the file's using-directives without the
2382/// per-position scope threading extraction does. Parses `source` fresh and
2383/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
2384/// keys on the grammar's `namespace` keyword token, not source text), so it
2385/// never misreads a member-importing `using X::Y;` as a namespace directive.
2386///
2387/// This is a whole-file over-approximation of what is in scope at any one point
2388/// (a directive nested inside a `namespace {}` block or a function body is still
2389/// reported), which is exactly what the #1134 identity reconciler wants: extra
2390/// candidate namespaces that no visible class confirms are harmless, and two
2391/// that both confirm are treated as a genuine ambiguity by the reconciler.
2392pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
2393    let mut parser = Parser::new();
2394    if parser
2395        .set_language(&tree_sitter_cpp::LANGUAGE.into())
2396        .is_err()
2397    {
2398        return Vec::new();
2399    }
2400    let Some(tree) = parser.parse(source, None) else {
2401        return Vec::new();
2402    };
2403    let mut namespaces = Vec::new();
2404    let mut seen = std::collections::HashSet::new();
2405    let mut stack = vec![tree.root_node()];
2406    while let Some(node) = stack.pop() {
2407        if let Some(namespace) = cpp_using_namespace_target(node, source)
2408            && seen.insert(namespace.clone())
2409        {
2410            namespaces.push(namespace);
2411        }
2412        let mut cursor = node.walk();
2413        stack.extend(node.named_children(&mut cursor));
2414    }
2415    namespaces
2416}
2417
2418pub struct CppVisitor<'a> {
2419    pub file: &'a ProjectFile,
2420    pub source: &'a str,
2421    pub parsed: &'a mut ParsedFile,
2422    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
2423    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
2424    ///
2425    /// C has no nested tag scope: a struct/union/enum tag declared inside
2426    /// another aggregate's member list has the scope of the outer declaration
2427    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
2428    /// therefore declares a file-scope `inner` that a later file-scope
2429    /// `struct inner *p;` legitimately references, where C++ would make the
2430    /// same shape a nested class `outer::inner`. Headers carry no compilation
2431    /// language of their own and keep the conservative C++ interpretation.
2432    pub c_tag_semantics: bool,
2433    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
2434    /// Byte regions whose contents were re-owned by a fragmented export-class
2435    /// recovery (#938): the scattered members between the fragmented
2436    /// declaration and its displaced closing brace are indexed as members of
2437    /// the recovered class by the region reparse, so the ordinary sibling walk
2438    /// must not ALSO index them as top-level declarations (that double-indexing
2439    /// made a scattered nested class ambiguous between `Inner` and
2440    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
2441    /// linear scan at visit time is fine.
2442    pub consumed_fragment_regions: Vec<(usize, usize)>,
2443    /// The namespace forward declarations already folded out of each tree this
2444    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
2445    /// Empty until the first question, which the overwhelming majority of files
2446    /// never ask.
2447    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
2448    /// Which owners already have field declarations in the parse product, as
2449    /// [`CppVisitor::has_enum_enumerator_units`] needs to know. `None` until
2450    /// the first enum asks, which most files never do (#2786).
2451    pub field_owners: Option<CppFieldOwnerIndex>,
2452    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
2453    /// happen to the declaration set, innermost last. Empty outside a recovery
2454    /// reparse, which is almost always (#2787).
2455    pub recovery_captures: Vec<CppRecoveryCapture>,
2456}
2457
2458impl<'a> CppVisitor<'a> {
2459    /// Records `code_unit` with the answers the walk carries forward, then adds
2460    /// it to the parse product.
2461    ///
2462    /// Every declaration this walk publishes goes through this family, so the
2463    /// carried-forward answers see each one exactly once: the field ownership
2464    /// index behind [`Self::has_enum_enumerator_units`] (#2786) and the minted
2465    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
2466    fn add_declaration(
2467        &mut self,
2468        code_unit: CodeUnit,
2469        node: Node<'_>,
2470        parent: Option<CodeUnit>,
2471        top_level: Option<CodeUnit>,
2472    ) {
2473        self.note_declaration(&code_unit);
2474        let source = self.source;
2475        self.parsed
2476            .add_code_unit(code_unit, node, source, parent, top_level);
2477    }
2478
2479    /// Range-based form of [`Self::add_declaration`].
2480    fn add_declaration_with_range(
2481        &mut self,
2482        code_unit: CodeUnit,
2483        range: Range,
2484        parent: Option<CodeUnit>,
2485        top_level: Option<CodeUnit>,
2486    ) {
2487        self.note_declaration(&code_unit);
2488        self.parsed
2489            .add_code_unit_with_range(code_unit, range, parent, top_level);
2490    }
2491
2492    /// Deferred-replacement form of [`Self::add_declaration`].
2493    fn replace_declaration_deferred(
2494        &mut self,
2495        code_unit: CodeUnit,
2496        node: Node<'_>,
2497        parent: Option<CodeUnit>,
2498        top_level: Option<CodeUnit>,
2499    ) {
2500        self.note_replaced_declaration(&code_unit);
2501        let source = self.source;
2502        self.parsed
2503            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
2504    }
2505
2506    /// Range-based form of [`Self::replace_declaration_deferred`].
2507    fn replace_declaration_with_range_deferred(
2508        &mut self,
2509        code_unit: CodeUnit,
2510        range: Range,
2511        parent: Option<CodeUnit>,
2512        top_level: Option<CodeUnit>,
2513    ) {
2514        self.note_replaced_declaration(&code_unit);
2515        self.parsed
2516            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
2517    }
2518
2519    /// Notes one declaration about to enter the parse product.
2520    ///
2521    /// A declaration the product already holds is not a creation, so an open
2522    /// recovery capture ignores it -- which is the membership test the set
2523    /// difference it replaces performed. A creation inside a nested recovery
2524    /// belongs to the recoveries around it too, so every open capture takes it.
2525    fn note_declaration(&mut self, code_unit: &CodeUnit) {
2526        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
2527            for capture in &mut self.recovery_captures {
2528                if capture.removed_pre_existing.contains(code_unit) {
2529                    continue;
2530                }
2531                if capture.created_units.insert(code_unit.clone()) {
2532                    capture.created.push(code_unit.clone());
2533                }
2534            }
2535        }
2536        if let Some(field_owners) = self.field_owners.as_mut() {
2537            field_owners.record(code_unit, self.file);
2538        }
2539    }
2540
2541    /// Notes one declaration about to replace an existing one.
2542    ///
2543    /// A deferred replacement of a declaration that already owns children
2544    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
2545    /// a removal is the one thing the field index cannot absorb by addition.
2546    /// Drop it; the next question rebuilds it from the declarations that
2547    /// survive. A replacement of a unit with no children, and a "replacement"
2548    /// of a unit that is not there at all, remove nothing.
2549    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
2550        let removes_children = self.parsed.contains_declaration(code_unit)
2551            && self
2552                .parsed
2553                .children
2554                .get(code_unit)
2555                .is_some_and(|children| !children.is_empty());
2556        if removes_children {
2557            if !self.recovery_captures.is_empty() {
2558                let removed = self.declarations_a_replacement_removes(code_unit);
2559                for capture in &mut self.recovery_captures {
2560                    for unit in &removed {
2561                        // A declaration this capture watched being created is
2562                        // its own; one it did not is a declaration that was
2563                        // already there when the capture opened, so creating it
2564                        // again is a restoration and not a mint.
2565                        if !capture.created_units.contains(unit) {
2566                            capture.removed_pre_existing.insert(unit.clone());
2567                        }
2568                    }
2569                }
2570            }
2571            self.field_owners = None;
2572        }
2573        self.note_declaration(code_unit);
2574    }
2575
2576    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
2577    /// when `code_unit` is replaced: its children, transitively.
2578    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
2579        let mut removed = Vec::new();
2580        let mut seen = HashSet::default();
2581        let mut pending: Vec<CodeUnit> = self
2582            .parsed
2583            .children
2584            .get(code_unit)
2585            .cloned()
2586            .unwrap_or_default();
2587        while let Some(unit) = pending.pop() {
2588            if !seen.insert(unit.clone()) {
2589                continue;
2590            }
2591            if let Some(children) = self.parsed.children.get(&unit) {
2592                pending.extend(children.iter().cloned());
2593            }
2594            removed.push(unit);
2595        }
2596        removed
2597    }
2598
2599    fn visit_function_like_export_class_pair<'tree>(
2600        &mut self,
2601        node: Node<'tree>,
2602        scope: &ScopeInfo,
2603        stack: &mut Vec<CppWork<'tree>>,
2604        ancestry: &ParentIndex<'tree>,
2605    ) -> bool {
2606        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
2607            return false;
2608        };
2609        let member_outcome = self
2610            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
2611        // A malformed class body can escape into several following siblings
2612        // before the next export-macro class head appears. Inspect siblings in
2613        // order and stop at the first envelope that contains such a head. One
2614        // envelope can contain several following classes, all recovered in a
2615        // single bounded traversal.
2616        let mut displaced = node.next_named_sibling();
2617        while let Some(candidate) = displaced {
2618            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
2619                break;
2620            }
2621            displaced = candidate.next_named_sibling();
2622        }
2623        let class_unit = self.visit_named_class_like_shape(
2624            node,
2625            recovered.name,
2626            // The adjacent initializer_list proves the class body envelope,
2627            // but its children are expression-shaped rather than declaration-
2628            // preserving. Index the class identity here; callable definitions
2629            // remain available from their ordinary out-of-line declarations.
2630            None,
2631            true,
2632            Some(recovered.range),
2633            recovered.raw_supertypes,
2634            scope,
2635            stack,
2636            ancestry,
2637        );
2638        self.parsed
2639            .record_materialization(MaterializationRecord::RecoveredDeclaration {
2640                recovery: recovered.range,
2641                unit: class_unit.clone(),
2642            });
2643        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
2644            && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
2645                tree.root_node(),
2646                class_unit.identifier(),
2647                self.source,
2648            )
2649        {
2650            self.visit_recovered_fragment_constructor(
2651                range,
2652                body,
2653                node,
2654                &class_unit,
2655                scope,
2656                ancestry,
2657            );
2658        }
2659        if let Some(outcome) = member_outcome {
2660            self.visit_fragmented_export_class_members(outcome, class_unit, scope);
2661        }
2662        self.consumed_fragment_regions
2663            .push((node.start_byte(), recovered.range.end_byte));
2664        true
2665    }
2666
2667    fn visit_embedded_function_like_export_classes<'tree>(
2668        &mut self,
2669        node: Node<'tree>,
2670        scope: &ScopeInfo,
2671        stack: &mut Vec<CppWork<'tree>>,
2672        ancestry: &ParentIndex<'tree>,
2673    ) -> bool {
2674        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
2675        let found = !recovered_classes.is_empty();
2676        for recovered in recovered_classes {
2677            let member_outcome = self.reparse_fragmented_export_class_members(
2678                &recovered.fragmented_body,
2679                &recovered.name,
2680            );
2681            let class_unit = self.visit_named_class_like_shape(
2682                node,
2683                recovered.name,
2684                None,
2685                true,
2686                Some(recovered.range),
2687                Some(recovered.raw_supertypes),
2688                scope,
2689                stack,
2690                ancestry,
2691            );
2692            self.parsed
2693                .record_materialization(MaterializationRecord::RecoveredDeclaration {
2694                    recovery: recovered.range,
2695                    unit: class_unit.clone(),
2696                });
2697            if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
2698                && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
2699                    tree.root_node(),
2700                    class_unit.identifier(),
2701                    self.source,
2702                )
2703            {
2704                self.visit_recovered_fragment_constructor(
2705                    range,
2706                    body,
2707                    node,
2708                    &class_unit,
2709                    scope,
2710                    ancestry,
2711                );
2712            }
2713            if let Some(outcome) = member_outcome {
2714                self.visit_fragmented_export_class_members(outcome, class_unit, scope);
2715            }
2716        }
2717        found
2718    }
2719
2720    /// Walk `node`'s container, answering every ancestor question from
2721    /// `ancestry`.
2722    ///
2723    /// `ancestry` must index the tree `node` belongs to. The caller owns it
2724    /// because one tree can be walked more than once -- a header's C and C++
2725    /// readings are the same tree under different tag semantics -- and the
2726    /// parent relation is a property of the tree, not of the reading.
2727    #[allow(clippy::too_many_arguments)]
2728    pub fn visit_container<'tree>(
2729        &mut self,
2730        node: Node<'tree>,
2731        ancestry: &ParentIndex<'tree>,
2732        package_name: &str,
2733        module: Option<CodeUnit>,
2734        class_unit: Option<CodeUnit>,
2735        template_signature: Option<String>,
2736        visible_using_namespaces: Vec<String>,
2737    ) {
2738        let scope = ScopeInfo {
2739            package_name: package_name.to_string(),
2740            module,
2741            class_unit,
2742            template_signature,
2743            template_metadata: None,
2744            declarations_are_fields: false,
2745            recovered_specialization_member_scope: false,
2746            visible_using_namespaces,
2747        };
2748        self.run_container_work(node, scope, ancestry);
2749    }
2750
2751    /// Whether a work node lies entirely inside a byte region consumed by a
2752    /// fragmented export-class recovery (#938); such nodes were already indexed
2753    /// as members of the recovered class by the region reparse.
2754    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
2755        self.consumed_fragment_regions
2756            .iter()
2757            .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
2758    }
2759
2760    /// Drive the container work loop from an explicit seed scope to completion. The
2761    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
2762    /// stays alive for the whole traversal.
2763    ///
2764    /// Every ancestor question this walk asks is answered from `ancestry`, which
2765    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
2766    /// node's position in the tree, which made a generated header with thousands
2767    /// of top-level declarations quadratic (#2361). The index is the caller's
2768    /// because it outlives any one walk: the file's tree is walked twice when a
2769    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
2770    /// builds its own index for its own tree.
2771    fn run_container_work<'tree>(
2772        &mut self,
2773        node: Node<'tree>,
2774        scope: ScopeInfo,
2775        ancestry: &ParentIndex<'tree>,
2776    ) {
2777        let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
2778        while let Some(work) = stack.pop() {
2779            match work {
2780                CppWork::Container(container) => {
2781                    push_cpp_container_work(container.node, container.scope, &mut stack);
2782                }
2783                CppWork::Siblings(siblings) => {
2784                    advance_cpp_siblings(siblings, self.source, &mut stack);
2785                }
2786                CppWork::Node(work) => {
2787                    if self.node_is_inside_consumed_fragment(work.node) {
2788                        continue;
2789                    }
2790                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
2791                }
2792            }
2793        }
2794    }
2795
2796    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
2797    /// it only when the entire region is member-shaped. This validation must happen
2798    /// before registering the recovered class because a rejected speculative range
2799    /// must not leak into the ordinary recovery path.
2800    fn reparse_fragmented_export_class_members(
2801        &self,
2802        fragmented: &FragmentedExportBody,
2803        class_name: &str,
2804    ) -> Option<FragmentedExportMembers> {
2805        if fragmented.reparse_start >= fragmented.reparse_end {
2806            return None;
2807        }
2808        let tree = cpp_reparse_fragmented_class_body(
2809            self.source,
2810            fragmented.reparse_start,
2811            fragmented.reparse_end,
2812        )?;
2813        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
2814            return Some(FragmentedExportMembers::Complete(tree));
2815        }
2816        let has_conditional_constructor = {
2817            let root = tree.root_node();
2818            let mut cursor = root.walk();
2819            root.named_children(&mut cursor).any(|child| {
2820                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
2821            })
2822        };
2823        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
2824    }
2825
2826    /// Index an already validated fragmented body as members of `class_unit`. The
2827    /// region reparse keeps each member's exact original byte and line positions.
2828    fn visit_fragmented_export_class_members(
2829        &mut self,
2830        outcome: FragmentedExportMembers,
2831        class_unit: CodeUnit,
2832        scope: &ScopeInfo,
2833    ) -> bool {
2834        let (tree, complete) = match outcome {
2835            FragmentedExportMembers::Complete(tree) => (tree, true),
2836            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
2837        };
2838        let root = tree.root_node();
2839        let class_name = class_unit.identifier().to_string();
2840        let member_scope = ScopeInfo {
2841            // A recovered export-macro class may borrow its namespace from an
2842            // earlier forward declaration even when the malformed node itself
2843            // sits at file scope. Use the recovered class identity as the
2844            // authoritative package for reparsed members as well.
2845            package_name: class_unit.package_name().to_string(),
2846            module: scope.module.clone(),
2847            class_unit: Some(class_unit),
2848            template_signature: scope.template_signature.clone(),
2849            template_metadata: None,
2850            declarations_are_fields: true,
2851            recovered_specialization_member_scope: false,
2852            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2853        };
2854        if !complete {
2855            // A conditional beginning immediately after an access label can
2856            // fragment one constructor declaration while leaving the rest of
2857            // the class body as unsafe statement soup. Recover only that
2858            // structurally proven constructor and leave the outer-tree
2859            // siblings unconsumed for their ordinary walk.
2860            let mut cursor = root.walk();
2861            let constructors = root
2862                .named_children(&mut cursor)
2863                .filter_map(|child| {
2864                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
2865                })
2866                .collect::<Vec<_>>();
2867            // The reparsed region is its own tree, so this drain walks it with
2868            // its own parent index.
2869            let reparsed_ancestry = ParentIndex::new(root);
2870            for constructor in constructors {
2871                let mut stack = Vec::new();
2872                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
2873                while let Some(work) = stack.pop() {
2874                    match work {
2875                        CppWork::Container(container) => {
2876                            push_cpp_container_work(container.node, container.scope, &mut stack);
2877                        }
2878                        CppWork::Siblings(siblings) => {
2879                            advance_cpp_siblings(siblings, self.source, &mut stack);
2880                        }
2881                        CppWork::Node(work) => {
2882                            self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
2883                        }
2884                    }
2885                }
2886            }
2887            return false;
2888        }
2889        // The reparsed region is its own tree, so this walk indexes it itself.
2890        self.run_container_work(root, member_scope, &ParentIndex::new(root));
2891        true
2892    }
2893
2894    fn visit_recovered_fragment_constructor<'tree>(
2895        &mut self,
2896        range: std::ops::Range<usize>,
2897        constructor_body: Node<'tree>,
2898        class_declaration: Node<'tree>,
2899        class_unit: &CodeUnit,
2900        scope: &ScopeInfo,
2901        ancestry: &ParentIndex<'tree>,
2902    ) {
2903        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
2904            return;
2905        };
2906        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
2907            tree.root_node(),
2908            range.start,
2909            class_unit.identifier(),
2910            self.source,
2911        ) else {
2912            return;
2913        };
2914        let member_scope = ScopeInfo {
2915            package_name: class_unit.package_name().to_string(),
2916            module: scope.module.clone(),
2917            class_unit: Some(class_unit.clone()),
2918            template_signature: scope.template_signature.clone(),
2919            template_metadata: None,
2920            declarations_are_fields: true,
2921            recovered_specialization_member_scope: false,
2922            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2923        };
2924        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
2925        else {
2926            return;
2927        };
2928        debug_assert_eq!(function.name, class_unit.identifier());
2929        let code_unit = function.code_unit(self.file.clone());
2930        self.add_declaration_with_range(
2931            code_unit.clone(),
2932            Range {
2933                start_byte: function_declarator.start_byte(),
2934                end_byte: constructor_body.end_byte(),
2935                start_line: function_declarator.start_position().row + 1,
2936                end_line: constructor_body.end_position().row + 1,
2937            },
2938            None,
2939            None,
2940        );
2941        self.parsed.add_signature_with_metadata(
2942            code_unit.clone(),
2943            cpp_signature_metadata(
2944                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
2945                function_declarator,
2946                self.source,
2947                ancestry,
2948            )
2949            .with_declaration_only(false)
2950            .with_callable_linkage(cpp_callable_linkage(
2951                class_declaration,
2952                self.source,
2953                ancestry,
2954            )),
2955        );
2956        self.parsed.add_child(class_unit.clone(), code_unit);
2957    }
2958
2959    fn visit_recovered_fragment_prefix_members<'tree>(
2960        &mut self,
2961        root: Node<'tree>,
2962        constructor_start: usize,
2963        class_unit: &CodeUnit,
2964        scope: &ScopeInfo,
2965        ancestry: &ParentIndex<'tree>,
2966    ) {
2967        let member_scope = ScopeInfo {
2968            package_name: class_unit.package_name().to_string(),
2969            module: scope.module.clone(),
2970            class_unit: Some(class_unit.clone()),
2971            template_signature: scope.template_signature.clone(),
2972            template_metadata: None,
2973            declarations_are_fields: true,
2974            recovered_specialization_member_scope: false,
2975            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2976        };
2977        let mut stack = vec![root];
2978        while let Some(current) = stack.pop() {
2979            if current.kind() == "comment" || current.start_byte() >= constructor_start {
2980                continue;
2981            }
2982            if current.end_byte() <= constructor_start
2983                && current.kind() != "translation_unit"
2984                && current.kind() != "labeled_statement"
2985                && current.kind() != "ERROR"
2986            {
2987                let mut work_stack = Vec::new();
2988                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
2989                while let Some(work) = work_stack.pop() {
2990                    match work {
2991                        CppWork::Container(container) => {
2992                            push_cpp_container_work(
2993                                container.node,
2994                                container.scope,
2995                                &mut work_stack,
2996                            );
2997                        }
2998                        CppWork::Siblings(siblings) => {
2999                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
3000                        }
3001                        CppWork::Node(work) => {
3002                            self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
3003                        }
3004                    }
3005                }
3006                continue;
3007            }
3008            if matches!(
3009                current.kind(),
3010                "translation_unit" | "labeled_statement" | "ERROR"
3011            ) {
3012                let mut cursor = current.walk();
3013                stack.extend(current.named_children(&mut cursor));
3014            }
3015        }
3016    }
3017
3018    fn visit_node<'tree>(
3019        &mut self,
3020        node: Node<'tree>,
3021        scope: &ScopeInfo,
3022        stack: &mut Vec<CppWork<'tree>>,
3023        ancestry: &ParentIndex<'tree>,
3024    ) {
3025        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
3026            self.visit_node(node, &recovered_scope, stack, ancestry);
3027            return;
3028        }
3029        // Fragmented-class recovery below may consume a malformed function
3030        // envelope before the ordinary kind dispatch runs. Recover any
3031        // export-macro class embedded in that envelope first; the strict class
3032        // head/base/body predicate is independent of which later recovery owns
3033        // the surrounding parser fragment.
3034        if node.kind() == "function_definition" && node.has_error() {
3035            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
3036        }
3037        if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
3038        {
3039            let displaced_namespace_items =
3040                displaced_fragment_namespace_geometry(node, self.source)
3041                    .map(|boundary| boundary.namespace_items)
3042                    .unwrap_or_default();
3043            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
3044            let mut class_stack = Vec::new();
3045            // When the full body cannot be safely reparsed, the original class
3046            // node still proves ownership for its parser-visible prefix.
3047            let parser_visible_body =
3048                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
3049                    .then(|| cpp_body_node(class_node))
3050                    .flatten();
3051            let class_unit = self.visit_named_class_like_shape(
3052                class_node,
3053                name,
3054                parser_visible_body,
3055                true,
3056                Some(fragmented.class_range),
3057                Some(extract_cpp_supertypes(class_node, self.source)),
3058                scope,
3059                &mut class_stack,
3060                ancestry,
3061            );
3062            let member_scope = ScopeInfo {
3063                package_name: class_unit.package_name().to_string(),
3064                module: scope.module.clone(),
3065                class_unit: Some(class_unit.clone()),
3066                template_signature: scope.template_signature.clone(),
3067                template_metadata: None,
3068                declarations_are_fields: true,
3069                recovered_specialization_member_scope: false,
3070                visible_using_namespaces: scope.visible_using_namespaces.clone(),
3071            };
3072            let complete = outcome.is_some_and(|outcome| {
3073                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
3074            });
3075            if complete {
3076                self.consumed_fragment_regions
3077                    .push((node.start_byte(), fragmented.class_range.end_byte));
3078            } else {
3079                // A macro-constrained member can make the full body reparse
3080                // unsafe while tree-sitter still exposes later class members
3081                // as bounded siblings up to the displaced `}`/`;`. Keep the
3082                // structurally proven class/base declaration and re-own those
3083                // sibling nodes under it. They retain their original parser
3084                // nodes and exact ranges; the close boundary comes solely from
3085                // `fragmented_plain_class_body`.
3086                // Template wrappers put the escaped members beside the
3087                // template rather than beside its malformed declaration.
3088                for candidate in cpp_following_named_siblings(node, self.source) {
3089                    if candidate.start_byte() >= fragmented.reparse_end {
3090                        break;
3091                    }
3092                    if cpp_fragment_sibling_is_class_member(
3093                        candidate,
3094                        fragmented.reparse_end,
3095                        self.source,
3096                    ) {
3097                        self.recovered_class_sibling_scopes
3098                            .insert(candidate.id(), member_scope.clone());
3099                    }
3100                }
3101            }
3102            for item in displaced_namespace_items {
3103                self.recovered_class_sibling_scopes
3104                    .insert(item.id(), scope.clone());
3105            }
3106            stack.extend(class_stack);
3107            return;
3108        }
3109        match node.kind() {
3110            "template_declaration" => {
3111                if let Some(recovered) =
3112                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
3113                {
3114                    let mut template_scope = scope.clone();
3115                    template_scope.template_signature =
3116                        cpp_template_signature(node, recovered.declaration_node, self.source);
3117                    template_scope.template_metadata =
3118                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
3119                    let raw_supertypes =
3120                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
3121                    let mut class_stack = Vec::new();
3122                    let class_unit = self.visit_named_class_like_shape(
3123                        recovered.class_node,
3124                        recovered.name,
3125                        Some(recovered.body),
3126                        true,
3127                        Some(recovered.range),
3128                        raw_supertypes,
3129                        &template_scope,
3130                        &mut class_stack,
3131                        ancestry,
3132                    );
3133                    self.parsed.record_materialization(
3134                        MaterializationRecord::RecoveredDeclaration {
3135                            recovery: recovered.range,
3136                            unit: class_unit.clone(),
3137                        },
3138                    );
3139                    let member_scope = ScopeInfo {
3140                        package_name: template_scope.package_name.clone(),
3141                        module: template_scope.module.clone(),
3142                        class_unit: Some(class_unit.clone()),
3143                        template_signature: template_scope.template_signature.clone(),
3144                        template_metadata: None,
3145                        declarations_are_fields: true,
3146                        recovered_specialization_member_scope: recovered
3147                            .class_node
3148                            .child_by_field_name("name")
3149                            .is_some_and(|name| name.kind() == "template_type"),
3150                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
3151                    };
3152                    for tail_member in recovered.tail_members.into_iter().rev() {
3153                        stack.push(CppWork::Node(CppNodeWork {
3154                            node: tail_member,
3155                            scope: member_scope.clone(),
3156                        }));
3157                    }
3158                    stack.extend(class_stack);
3159                    for sibling in recovered.member_siblings {
3160                        self.recovered_class_sibling_scopes
3161                            .insert(sibling.id(), member_scope.clone());
3162                    }
3163                    return;
3164                }
3165                for index in (0..node.named_child_count()).rev() {
3166                    let Some(child) = node.named_child(index) else {
3167                        continue;
3168                    };
3169                    if matches!(
3170                        child.kind(),
3171                        "class_specifier"
3172                            | "struct_specifier"
3173                            | "union_specifier"
3174                            | "enum_specifier"
3175                            | "function_definition"
3176                            | "declaration"
3177                            | "field_declaration"
3178                            | "alias_declaration"
3179                            | "namespace_definition"
3180                    ) {
3181                        let mut template_scope = scope.clone();
3182                        template_scope.template_signature =
3183                            cpp_template_signature(node, child, self.source);
3184                        template_scope.template_metadata =
3185                            cpp_template_metadata(node, child, self.source, ancestry);
3186                        if let Some(recovered) = recover_fragmented_partial_specialization(
3187                            node,
3188                            child,
3189                            self.source,
3190                            ancestry,
3191                        ) {
3192                            let code_unit = self.visit_named_class_like_shape(
3193                                recovered.declaration_node,
3194                                recovered.name,
3195                                None,
3196                                true,
3197                                Some(recovered.range),
3198                                None,
3199                                &template_scope,
3200                                stack,
3201                                ancestry,
3202                            );
3203                            self.parsed.record_materialization(
3204                                MaterializationRecord::RecoveredDeclaration {
3205                                    recovery: recovered.range,
3206                                    unit: code_unit.clone(),
3207                                },
3208                            );
3209                            let mut member_scope = template_scope.clone();
3210                            member_scope.class_unit = Some(code_unit);
3211                            member_scope.declarations_are_fields = true;
3212                            member_scope.recovered_specialization_member_scope = true;
3213                            for prefix_member in recovered.prefix_members.into_iter().rev() {
3214                                stack.push(CppWork::Node(CppNodeWork {
3215                                    node: prefix_member,
3216                                    scope: member_scope.clone(),
3217                                }));
3218                            }
3219                            for sibling in recovered.member_siblings {
3220                                self.recovered_class_sibling_scopes
3221                                    .insert(sibling.id(), member_scope.clone());
3222                            }
3223                            for following in recovered.following_declarations.into_iter().rev() {
3224                                stack.push(CppWork::Node(CppNodeWork {
3225                                    node: following,
3226                                    scope: scope.clone(),
3227                                }));
3228                            }
3229                            return;
3230                        }
3231                        stack.push(CppWork::Node(CppNodeWork {
3232                            node: child,
3233                            scope: template_scope,
3234                        }));
3235                    }
3236                }
3237            }
3238            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
3239            "linkage_specification" => {
3240                if let Some(body) = cpp_body_node(node) {
3241                    stack.push(CppWork::Container(CppContainer {
3242                        node: body,
3243                        scope: scope.clone(),
3244                    }));
3245                } else {
3246                    stack.push(CppWork::Container(CppContainer {
3247                        node,
3248                        scope: scope.clone(),
3249                    }));
3250                }
3251            }
3252            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
3253                self.visit_class_like(node, scope, stack, ancestry)
3254            }
3255            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
3256            // A bare namespace-begin sentinel can make tree-sitter promote the
3257            // wrapped declaration to an ERROR node instead of the usual bogus
3258            // function_definition envelope. Keep the recovery entry point on
3259            // the same structured path for both shapes; ordinary ERROR nodes
3260            // retain their declaration-preserving wrapper traversal when the
3261            // sentinel predicate does not match.
3262            "ERROR" => {
3263                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
3264                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
3265                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
3266                        return;
3267                    }
3268                    self.visit_macro_swallowed_function_declarations(node, scope);
3269                    stack.push(CppWork::Container(CppContainer {
3270                        node,
3271                        scope: scope.clone(),
3272                    }));
3273                }
3274            }
3275            "declaration" => {
3276                if scope.class_unit.is_some()
3277                    && scope.declarations_are_fields
3278                    && scope.recovered_specialization_member_scope
3279                    && let Some(alias_name) =
3280                        recovered_using_declaration_alias_name(node, self.source)
3281                {
3282                    self.add_type_aliases(node, scope, vec![alias_name]);
3283                } else {
3284                    self.visit_declaration(
3285                        node,
3286                        scope,
3287                        scope.declarations_are_fields,
3288                        stack,
3289                        ancestry,
3290                    )
3291                }
3292            }
3293            "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
3294            "type_definition" | "alias_declaration" => {
3295                self.visit_type_declaration(node, scope, stack, ancestry)
3296            }
3297            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
3298            // `#include` is collected by `collect_cpp_includes` before the
3299            // walk, so a directive the container walk never reaches -- inside
3300            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
3301            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
3302            // include claim.
3303            "preproc_include" => {}
3304            kind if preserves_declaration_scope_through_wrapper(
3305                kind,
3306                scope.class_unit.is_some(),
3307            ) =>
3308            {
3309                // A preprocessor conditional gates every declaration inside it
3310                // on a configuration this analyzer never evaluates; record the
3311                // interval so declaration state can say so (issue #1476). The
3312                // else/elif branches are children of the `preproc_if` node, so
3313                // recording the openers covers every branch.
3314                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
3315                    let mut range = cpp_declaration_range(node);
3316                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
3317                        range.end_byte = boundary.end_byte;
3318                        range.end_line = boundary.end_line;
3319                    }
3320                    self.parsed.record_materialization(
3321                        MaterializationRecord::ConfigurationConditional { range },
3322                    );
3323                    if node.has_error() {
3324                        // A malformed export-macro class can close the namespace
3325                        // node early while the enclosing include guard still owns
3326                        // the remaining class-head/body pairs. The ordinary walk
3327                        // cannot carry the lost namespace through those promoted
3328                        // siblings. Scan only structured ERROR nodes in this
3329                        // already-malformed conditional; the pair recovery's
3330                        // exact class/macro/body predicate remains the admission
3331                        // gate, and its namespace lifting restores the owner.
3332                        let mut candidates = vec![node];
3333                        while let Some(candidate) = candidates.pop() {
3334                            if candidate.kind() == "ERROR"
3335                                && self.visit_function_like_export_class_pair(
3336                                    candidate, scope, stack, ancestry,
3337                                )
3338                            {
3339                                continue;
3340                            }
3341                            for index in (0..candidate.named_child_count()).rev() {
3342                                candidates.push(
3343                                    candidate
3344                                        .named_child(index)
3345                                        .expect("index below the node's own named child count"),
3346                                );
3347                            }
3348                        }
3349                    }
3350                }
3351                stack.push(CppWork::Container(CppContainer {
3352                    node,
3353                    scope: scope.clone(),
3354                }))
3355            }
3356            _ => {}
3357        }
3358    }
3359
3360    fn visit_macro_swallowed_function_declarations<'tree>(
3361        &mut self,
3362        envelope: Node<'tree>,
3363        scope: &ScopeInfo,
3364    ) {
3365        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
3366            || envelope.kind() == "ERROR"
3367                && envelope
3368                    .parent()
3369                    .is_some_and(|parent| parent.kind() == "ERROR")
3370        {
3371            return;
3372        }
3373        let mut stack = (0..envelope.named_child_count())
3374            .filter_map(|index| envelope.named_child(index))
3375            .collect::<Vec<_>>();
3376        while let Some(node) = stack.pop() {
3377            if node.kind() == "function_declarator" {
3378                self.visit_error_swallowed_function_declaration(node, scope);
3379            }
3380            for index in 0..node.named_child_count() {
3381                if let Some(child) = node.named_child(index) {
3382                    stack.push(child);
3383                }
3384            }
3385        }
3386    }
3387
3388    fn visit_error_swallowed_function_declaration<'tree>(
3389        &mut self,
3390        node: Node<'tree>,
3391        scope: &ScopeInfo,
3392    ) -> bool {
3393        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
3394            return false;
3395        };
3396        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
3397            return false;
3398        };
3399        let root = tree.root_node();
3400        let mut cursor = root.walk();
3401        let declarations = root
3402            .named_children(&mut cursor)
3403            .filter(|child| child.kind() != "comment")
3404            .collect::<Vec<_>>();
3405        let [declaration] = declarations.as_slice() else {
3406            return false;
3407        };
3408        if declaration.kind() != "declaration"
3409            || declaration.has_error()
3410            || declaration.start_byte() != start
3411            || declaration.end_byte() != end
3412        {
3413            return false;
3414        }
3415        let recovery = cpp_recovery_window(self.source, start, end);
3416        // The reparsed region is its own tree, so this walk indexes it itself.
3417        let reparsed_ancestry = ParentIndex::new(root);
3418        self.record_recovered_declarations(recovery, |visitor| {
3419            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
3420        });
3421        true
3422    }
3423
3424    fn visit_namespace<'tree>(
3425        &mut self,
3426        node: Node<'tree>,
3427        scope: &ScopeInfo,
3428        stack: &mut Vec<CppWork<'tree>>,
3429        ancestry: &ParentIndex<'tree>,
3430    ) {
3431        let name_node = node.child_by_field_name("name");
3432        let Some(name_node) = name_node else {
3433            if let Some(body) = cpp_body_node(node) {
3434                stack.push(CppWork::Container(CppContainer {
3435                    node: body,
3436                    scope: scope.clone(),
3437                }));
3438            }
3439            return;
3440        };
3441        // Diagnostic corpora contain deliberately ill-formed global namespace
3442        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
3443        // the leading global `::` as the first anonymous child. Honor that AST
3444        // boundary instead of appending the name to the lexical namespace;
3445        // appending produced legacy names such as `outer::::outer::inner`, which
3446        // could not round-trip through the structured FqName boundary.
3447        let explicitly_global = name_node
3448            .child(0)
3449            .is_some_and(|child| !child.is_named() && child.kind() == "::");
3450        let components = cpp_namespace_name_components(name_node, self.source);
3451        if components.is_empty() {
3452            return;
3453        }
3454        // One Module per namespace level. C++17's `namespace a::b { ... }` is
3455        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
3456        // shorthand must declare `a` as well as `a::b` -- extracting only the
3457        // innermost level left the enclosing namespace undeclared and made the
3458        // two spellings of one construct disagree (issue #1878).
3459        let mut package_name = if explicitly_global {
3460            String::new()
3461        } else {
3462            scope.package_name.clone()
3463        };
3464        let mut module = None;
3465        for component in components {
3466            let full_name = if package_name.is_empty() {
3467                component
3468            } else {
3469                format!("{package_name}::{component}")
3470            };
3471            let level = CodeUnit::new_fq(
3472                self.file.clone(),
3473                CodeUnitType::Module,
3474                "",
3475                full_name.clone(),
3476                cpp_namespace_fq(&full_name),
3477            );
3478            if !self.parsed.contains_declaration(&level) {
3479                self.add_declaration(level.clone(), node, None, None);
3480            }
3481            package_name = full_name;
3482            module = Some(level);
3483        }
3484
3485        let namespace_scope = ScopeInfo {
3486            package_name,
3487            module,
3488            // C++ never nests a namespace inside a class, so a surviving
3489            // class_unit here is always recovery bleed: a malformed-region
3490            // boundary upstream mis-scoped this namespace block. Keeping the
3491            // owner would mint the namespace's declarations as class members
3492            // under a re-appended package, desyncing the fq boundary assert
3493            // (#2306). Dropping it is identity-neutral for valid code, where
3494            // class_unit is always empty at a namespace definition.
3495            class_unit: None,
3496            template_signature: scope.template_signature.clone(),
3497            template_metadata: scope.template_metadata.clone(),
3498            declarations_are_fields: false,
3499            recovered_specialization_member_scope: false,
3500            visible_using_namespaces: scope.visible_using_namespaces.clone(),
3501        };
3502        let container = cpp_body_node(node).unwrap_or(node);
3503        // A malformed export-macro class body may turn the following class
3504        // into a descendant of a bogus function/labeled/error envelope. Those
3505        // descendants are not declaration containers and the ordinary walk
3506        // intentionally does not descend into them. Scan the namespace tree
3507        // once for the strict embedded class geometry before scheduling its
3508        // normal declarations. When one envelope matches, its helper recovers
3509        // every embedded class and the walk need not inspect its descendants.
3510        let mut candidates = vec![container];
3511        while let Some(candidate) = candidates.pop() {
3512            if matches!(
3513                candidate.kind(),
3514                "ERROR" | "function_definition" | "labeled_statement"
3515            ) && self.visit_embedded_function_like_export_classes(
3516                candidate,
3517                &namespace_scope,
3518                stack,
3519                ancestry,
3520            ) {
3521                continue;
3522            }
3523            for index in (0..candidate.named_child_count()).rev() {
3524                candidates.push(
3525                    candidate
3526                        .named_child(index)
3527                        .expect("index below the node's own named child count"),
3528                );
3529            }
3530        }
3531        stack.push(CppWork::Container(CppContainer {
3532            node: container,
3533            scope: namespace_scope,
3534        }));
3535    }
3536
3537    fn visit_class_like<'tree>(
3538        &mut self,
3539        node: Node<'tree>,
3540        scope: &ScopeInfo,
3541        stack: &mut Vec<CppWork<'tree>>,
3542        ancestry: &ParentIndex<'tree>,
3543    ) {
3544        let Some(name) = class_like_name(node, self.source, ancestry) else {
3545            return;
3546        };
3547        let name = qualified_class_name_chain(node, self.source, scope)
3548            .map(|chain| chain.join("$"))
3549            .unwrap_or(name);
3550        self.visit_named_class_like(node, name, scope, stack, ancestry);
3551    }
3552
3553    fn visit_named_class_like<'tree>(
3554        &mut self,
3555        node: Node<'tree>,
3556        name: String,
3557        scope: &ScopeInfo,
3558        stack: &mut Vec<CppWork<'tree>>,
3559        ancestry: &ParentIndex<'tree>,
3560    ) {
3561        let body = cpp_body_node(node);
3562        let definition_body_present = body.is_some();
3563        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
3564            .then(|| extract_cpp_supertypes(node, self.source));
3565        self.visit_named_class_like_shape(
3566            node,
3567            name,
3568            body,
3569            definition_body_present,
3570            None,
3571            raw_supertypes,
3572            scope,
3573            stack,
3574            ancestry,
3575        );
3576    }
3577
3578    /// Whether this class-like declaration is a C tag that belongs to the
3579    /// enclosing non-aggregate scope rather than to the aggregate it is
3580    /// lexically written inside.
3581    ///
3582    /// `class_specifier` is deliberately excluded: `class` is not C, so text
3583    /// that spells one in a `.c` file is not C code and keeps the C++ reading
3584    /// rather than getting a half-C identity.
3585    fn mints_tag_at_enclosing_c_scope(
3586        &self,
3587        declaration_node: Node<'_>,
3588        scope: &ScopeInfo,
3589        ancestry: &ParentIndex<'_>,
3590    ) -> bool {
3591        self.c_tag_semantics
3592            && scope.class_unit.is_some()
3593            && class_like_name(declaration_node, self.source, ancestry).is_some()
3594            && matches!(
3595                declaration_node.kind(),
3596                "struct_specifier" | "union_specifier" | "enum_specifier"
3597            )
3598    }
3599
3600    #[allow(clippy::too_many_arguments)]
3601    fn visit_named_class_like_shape<'tree>(
3602        &mut self,
3603        declaration_node: Node<'tree>,
3604        name: String,
3605        body: Option<Node<'tree>>,
3606        definition_body_present: bool,
3607        explicit_range: Option<Range>,
3608        raw_supertypes: Option<Vec<String>>,
3609        scope: &ScopeInfo,
3610        stack: &mut Vec<CppWork<'tree>>,
3611        ancestry: &ParentIndex<'tree>,
3612    ) -> CodeUnit {
3613        let displaced_macro_tail = if explicit_range.is_none() {
3614            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
3615        } else {
3616            None
3617        };
3618        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
3619        let recovered_scope = self.scope_for_recovered_exported_class(
3620            declaration_node,
3621            &name,
3622            definition_body_present,
3623            scope,
3624            ancestry,
3625        );
3626        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
3627        // aggregate's member list is declared at the enclosing non-aggregate
3628        // scope, not nested inside the aggregate. `scope.class_unit` is the
3629        // only aggregate carrier in this walk, so dropping it puts the tag at
3630        // the nearest enclosing non-aggregate scope -- the module at file or
3631        // namespace scope, and the same block-scope representation a
3632        // function-local aggregate already gets. The tag's own body scope
3633        // below still owns its members, so fields and enumerators are
3634        // unaffected.
3635        let c_tag_scope;
3636        let scope =
3637            if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
3638                c_tag_scope = ScopeInfo {
3639                    class_unit: None,
3640                    ..recovered_scope.clone()
3641                };
3642                &c_tag_scope
3643            } else {
3644                &recovered_scope
3645            };
3646        let short_name = if let Some(parent) = &scope.class_unit {
3647            cpp_join_nested_short(parent.short_name(), &name)
3648        } else {
3649            name.clone()
3650        };
3651        // A top-level out-of-line qualified class definition (`struct
3652        // Outer::Inner { ... }` inside its namespace, #2246) carries its
3653        // nesting chain as the `$`-joined display name; push one Type/Nested
3654        // segment per class so segment-pop owner navigation keeps working.
3655        // Every other leaf name stays opaque so a literal `$` in a source
3656        // identifier never crosses the split/join boundary (#2140).
3657        let qualified_chain = if scope.class_unit.is_none() {
3658            qualified_class_name_chain(declaration_node, self.source, scope)
3659                .filter(|chain| chain.join("$") == name)
3660        } else {
3661            None
3662        };
3663        let fq = if let Some(chain) = qualified_chain {
3664            let mut fq = FqName::new();
3665            cpp_push_package(&mut fq, &scope.package_name);
3666            let mut first = true;
3667            for component in chain {
3668                let kind = if first {
3669                    SegmentKind::Type
3670                } else {
3671                    SegmentKind::Nested
3672                };
3673                fq.push(cpp_segment(&component, kind));
3674                first = false;
3675            }
3676            fq
3677        } else {
3678            cpp_leaf_fq(
3679                &scope.package_name,
3680                scope.class_unit.as_ref(),
3681                &name,
3682                SegmentKind::Nested,
3683                SegmentKind::Type,
3684            )
3685        };
3686        let code_unit = CodeUnit::with_signature_and_fq(
3687            self.file.clone(),
3688            CodeUnitType::Class,
3689            scope.package_name.clone(),
3690            short_name,
3691            scope.template_signature.clone(),
3692            false,
3693            fq,
3694        );
3695        let has_body = definition_body_present;
3696        if !has_body && self.parsed.contains_declaration(&code_unit) {
3697            self.parsed.record_navigation_range(
3698                code_unit.clone(),
3699                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
3700            );
3701            return code_unit;
3702        }
3703        if has_body {
3704            if let Some(range) = explicit_range {
3705                self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
3706            } else {
3707                self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
3708            }
3709        } else {
3710            self.add_declaration(code_unit.clone(), declaration_node, None, None);
3711        }
3712        if let Some(raw_supertypes) = raw_supertypes {
3713            self.parsed
3714                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
3715        }
3716        self.parsed.add_signature(
3717            code_unit.clone(),
3718            render_cpp_type_signature(
3719                declaration_node,
3720                self.source,
3721                scope.template_signature.as_deref(),
3722            ),
3723        );
3724        if let Some(metadata) = &scope.template_metadata {
3725            let primary_short_name = if let Some(parent) = &scope.class_unit {
3726                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
3727            } else {
3728                metadata.primary_name.clone()
3729            };
3730            let primary_fq_name = CodeUnit::new(
3731                self.file.clone(),
3732                CodeUnitType::Class,
3733                scope.package_name.clone(),
3734                primary_short_name,
3735            )
3736            .fq_name();
3737            let mut metadata = metadata.clone();
3738            metadata.primary_fq_name = primary_fq_name;
3739            self.parsed
3740                .set_cpp_template_metadata(code_unit.clone(), metadata);
3741        }
3742        if let Some(parent) = &scope.class_unit {
3743            self.parsed.add_child(parent.clone(), code_unit.clone());
3744        } else if let Some(module) = &scope.module {
3745            self.parsed.add_child(module.clone(), code_unit.clone());
3746        }
3747
3748        if let Some(body) = body {
3749            let mut nested_scope = scope.clone();
3750            nested_scope.class_unit = Some(code_unit.clone());
3751            nested_scope.template_signature = scope.template_signature.clone();
3752            // Template metadata describes the class just created. It must not
3753            // leak into ordinary nested declarations in that class's body.
3754            // Recovered export-macro specializations carry a separate scope bit
3755            // for their declaration-shaped body members.
3756            nested_scope.template_metadata = None;
3757            // Export-macro class bodies recovered from a function_definition use
3758            // compound_statement children, whose direct fields are declarations.
3759            nested_scope.recovered_specialization_member_scope =
3760                scope.template_metadata.as_ref().is_some_and(|metadata| {
3761                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
3762                });
3763            nested_scope.declarations_are_fields =
3764                is_recovered_exported_class_container(declaration_node, self.source)
3765                    || nested_scope.recovered_specialization_member_scope;
3766            if let Some(displaced) = displaced_macro_tail {
3767                // A macro-shaped field without a source semicolon can make
3768                // tree-sitter consume the real class terminator as an ERROR
3769                // inside that field, then retain following namespace items as
3770                // later field-list children. Drain the proven class prefix
3771                // first and re-own only the structured tail with the outer
3772                // scope. The tail is pushed first because the work stack is
3773                // LIFO.
3774                push_cpp_sibling_range(
3775                    body,
3776                    displaced.split_index,
3777                    usize::MAX,
3778                    scope.clone(),
3779                    stack,
3780                );
3781                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
3782            } else {
3783                stack.push(CppWork::Container(CppContainer {
3784                    node: body,
3785                    scope: nested_scope,
3786                }));
3787            }
3788        }
3789        if declaration_node.kind() == "enum_specifier" {
3790            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
3791            if !self.has_enum_enumerator_units(&code_unit) {
3792                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
3793            }
3794        }
3795        code_unit
3796    }
3797
3798    /// Whether the parse product already holds enumerator fields for `parent`,
3799    /// answered from the walk's carried-forward field ownership index.
3800    ///
3801    /// Built on the first enum's question and advanced by every declaration
3802    /// recorded after it, so a file that declares no enum -- most files -- pays
3803    /// nothing, and one that declares thousands pays a single pass instead of
3804    /// one per enum (#2786).
3805    fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
3806        if self.field_owners.is_none() {
3807            self.field_owners = Some(CppFieldOwnerIndex::of(
3808                self.parsed.declarations().iter(),
3809                self.file,
3810            ));
3811        }
3812        debug_assert_eq!(
3813            parent.source(),
3814            self.file,
3815            "the walk's declarations are declarations of the file it is walking"
3816        );
3817        let carried = self
3818            .field_owners
3819            .as_ref()
3820            .expect("the index was just ensured")
3821            .owns_fields(parent.package_name(), parent.short_name());
3822
3823        #[cfg(debug_assertions)]
3824        assert_eq!(
3825            carried,
3826            cpp_declarations_hold_owned_fields(
3827                self.parsed.declarations(),
3828                self.file,
3829                parent.package_name(),
3830                parent.short_name()
3831            ),
3832            "the carried-forward field index must answer what a fresh declaration scan \
3833             answers for {}",
3834            parent.fq_name()
3835        );
3836
3837        carried
3838    }
3839
3840    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
3841        walk_named_tree_preorder(node, false, |child| {
3842            if child.kind() != "enumerator" {
3843                return WalkControl::Continue;
3844            }
3845            let Some(name_node) = child.child_by_field_name("name") else {
3846                return WalkControl::Continue;
3847            };
3848            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
3849            if name.is_empty() {
3850                return WalkControl::Continue;
3851            }
3852            let code_unit = CodeUnit::new_fq(
3853                self.file.clone(),
3854                CodeUnitType::Field,
3855                scope.package_name.clone(),
3856                cpp_join_member_short(parent.short_name(), &name),
3857                parent
3858                    .fq()
3859                    .clone()
3860                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
3861            );
3862            if self.parsed.contains_declaration(&code_unit) {
3863                return WalkControl::Continue;
3864            }
3865            self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
3866            self.parsed.add_signature(
3867                code_unit,
3868                normalize_cpp_whitespace(node_text(child, self.source)),
3869            );
3870            WalkControl::Continue
3871        });
3872    }
3873
3874    fn visit_enum_enumerators_from_text(
3875        &mut self,
3876        node: Node<'_>,
3877        scope: &ScopeInfo,
3878        parent: &CodeUnit,
3879    ) {
3880        let text = node_text(node, self.source);
3881        let Some((_, body)) = text.split_once('{') else {
3882            return;
3883        };
3884        let Some((body, _)) = body.rsplit_once('}') else {
3885            return;
3886        };
3887        for entry in body.split(',') {
3888            let trimmed = entry.trim();
3889            let name = trimmed
3890                .split('=')
3891                .next()
3892                .unwrap_or("")
3893                .split_whitespace()
3894                .next()
3895                .unwrap_or("");
3896            if name.is_empty() {
3897                continue;
3898            }
3899            let code_unit = CodeUnit::new_fq(
3900                self.file.clone(),
3901                CodeUnitType::Field,
3902                scope.package_name.clone(),
3903                cpp_join_member_short(parent.short_name(), name),
3904                parent
3905                    .fq()
3906                    .clone()
3907                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
3908            );
3909            if self.parsed.contains_declaration(&code_unit) {
3910                continue;
3911            }
3912            self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
3913            self.parsed.add_signature(code_unit, trimmed.to_string());
3914        }
3915    }
3916
3917    fn visit_function_definition<'tree>(
3918        &mut self,
3919        node: Node<'tree>,
3920        scope: &ScopeInfo,
3921        stack: &mut Vec<CppWork<'tree>>,
3922        ancestry: &ParentIndex<'tree>,
3923    ) {
3924        // A file-scope object-like macro sentinel the parser cannot see (issue
3925        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
3926        // prefixes as a bogus `function_definition` that swallows real namespaces,
3927        // classes, and members. Reparse the swallowed interior as C++ items so the
3928        // ordinary declaration visitors index it with byte/line-exact ownership.
3929        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
3930            return;
3931        }
3932        if node.has_error() {
3933            self.visit_macro_swallowed_function_declarations(node, scope);
3934        }
3935        if let Some((class_node, name, raw_supertypes)) =
3936            recover_exported_class_function_definition(node, self.source)
3937        {
3938            let body = cpp_body_node(class_node);
3939            let displaced_namespace = cpp_body_node(node)
3940                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
3941            let fragmented = cpp_body_node(node).and_then(|body| {
3942                fragmented_export_function_body_region(
3943                    node,
3944                    body,
3945                    self.source,
3946                    displaced_namespace.as_ref(),
3947                )
3948            });
3949            // The recovery tuple's first node is the class-like type when the
3950            // parser exposes one, but the synthetic wrapper owns the compound
3951            // statement that contains the truncated class body. Use the
3952            // wrapper body for fragmented-member detection; retain the
3953            // class-node body for the ordinary (non-fragmented) path below.
3954            if let Some(fragmented) = fragmented {
3955                // The lifted sibling no longer sits below the parser-visible
3956                // namespace node. Restore the current parent scope when the
3957                // ordinary work walk reaches that class.
3958                if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
3959                    .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
3960                {
3961                    let mut boundary_scope = scope.clone();
3962                    for sibling in cpp_following_named_siblings(node, self.source) {
3963                        if sibling.start_byte() >= boundary.start_byte() {
3964                            break;
3965                        }
3966                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
3967                            boundary_scope.visible_using_namespaces.push(namespace);
3968                        }
3969                    }
3970                    self.recovered_class_sibling_scopes
3971                        .insert(boundary.id(), boundary_scope);
3972                }
3973                let mut recovered_constructor = None;
3974                let mut recovered_prefix_tree = None;
3975                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
3976                {
3977                    Some(FragmentedExportMembers::Complete(tree)) => {
3978                        if let Some(body) = body
3979                            && let Some(range) =
3980                                cpp_reparsed_synthetic_initializer_constructor_range(
3981                                    tree.root_node(),
3982                                    &name,
3983                                    self.source,
3984                                    body.end_byte(),
3985                                )
3986                        {
3987                            recovered_constructor = Some(range);
3988                            recovered_prefix_tree = Some(tree);
3989                            None
3990                        } else {
3991                            Some(FragmentedExportMembers::Complete(tree))
3992                        }
3993                    }
3994                    outcome => outcome,
3995                };
3996                let mut class_stack = Vec::new();
3997                let class_unit = self.visit_named_class_like_shape(
3998                    class_node,
3999                    name,
4000                    None,
4001                    true,
4002                    Some(fragmented.class_range),
4003                    raw_supertypes,
4004                    scope,
4005                    &mut class_stack,
4006                    ancestry,
4007                );
4008                self.parsed
4009                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
4010                        recovery: fragmented.class_range,
4011                        unit: class_unit.clone(),
4012                    });
4013                let complete = outcome.is_some_and(|outcome| {
4014                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
4015                });
4016                if complete {
4017                    self.consumed_fragment_regions
4018                        .push((node.start_byte(), fragmented.class_range.end_byte));
4019                } else {
4020                    // The reparse can fail when the first constructor or a
4021                    // method body is split into statement-shaped siblings.
4022                    // Keep the recovered class envelope, but do not visit the
4023                    // synthetic wrapper body: its initializer expressions can
4024                    // look like same-named member functions (for example
4025                    // `Token.location(loc)`). Re-own only the original sibling
4026                    // nodes that fall inside the proven class range. Their CST
4027                    // shapes retain the real field/function kinds and ranges.
4028                    let member_scope = ScopeInfo {
4029                        package_name: class_unit.package_name().to_string(),
4030                        module: scope.module.clone(),
4031                        class_unit: Some(class_unit.clone()),
4032                        template_signature: scope.template_signature.clone(),
4033                        template_metadata: None,
4034                        declarations_are_fields: true,
4035                        recovered_specialization_member_scope: false,
4036                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
4037                    };
4038                    for candidate in cpp_following_named_siblings(node, self.source) {
4039                        if candidate.start_byte() >= fragmented.reparse_end {
4040                            break;
4041                        }
4042                        if cpp_fragment_sibling_is_class_member(
4043                            candidate,
4044                            fragmented.reparse_end,
4045                            self.source,
4046                        ) {
4047                            self.recovered_class_sibling_scopes
4048                                .insert(candidate.id(), member_scope.clone());
4049                        }
4050                    }
4051                    if let Some(range) = recovered_constructor
4052                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
4053                    {
4054                        self.visit_recovered_fragment_prefix_members(
4055                            prefix_tree.root_node(),
4056                            range.start,
4057                            &class_unit,
4058                            scope,
4059                            ancestry,
4060                        );
4061                        self.visit_recovered_fragment_constructor(
4062                            range,
4063                            body,
4064                            class_node,
4065                            &class_unit,
4066                            scope,
4067                            ancestry,
4068                        );
4069                    }
4070                }
4071                if let Some(boundary) = displaced_namespace {
4072                    for item in boundary.namespace_items {
4073                        self.recovered_class_sibling_scopes
4074                            .insert(item.id(), scope.clone());
4075                    }
4076                }
4077                stack.extend(class_stack);
4078                return;
4079            }
4080            let mut stack = Vec::new();
4081            let class_unit = self.visit_named_class_like_shape(
4082                class_node,
4083                name,
4084                body,
4085                body.is_some(),
4086                None,
4087                raw_supertypes,
4088                scope,
4089                &mut stack,
4090                ancestry,
4091            );
4092            self.parsed
4093                .record_materialization(MaterializationRecord::RecoveredDeclaration {
4094                    recovery: cpp_declaration_range(node),
4095                    unit: class_unit,
4096                });
4097            // Issue #1524: the bogus `function_definition` body can run past
4098            // the class's true closing brace (the parse ends it with a
4099            // zero-width `MISSING "}"`), swallowing following namespace-scope
4100            // siblings -- they would index as members of the recovered class.
4101            // When the body's text-balanced close lands before the body's own
4102            // end, re-own the swallowed tail with the outer scope instead.
4103            if let Some(body) = body
4104                && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
4105                && class_close < body.end_byte()
4106            {
4107                let split = {
4108                    let mut cursor = body.walk();
4109                    body.named_children(&mut cursor)
4110                        .position(|child| child.start_byte() > class_close)
4111                };
4112                if let Some(split) = split {
4113                    // The seeded work is a single Container over the whole
4114                    // body with the class scope; replace it with the bounded
4115                    // head (class scope) plus the swallowed tail (outer
4116                    // scope). Push tail first so the head drains first.
4117                    let seeded = stack.pop();
4118                    match seeded {
4119                        Some(CppWork::Container(container)) => {
4120                            push_cpp_sibling_range(
4121                                body,
4122                                split,
4123                                usize::MAX,
4124                                scope.clone(),
4125                                &mut stack,
4126                            );
4127                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
4128                        }
4129                        // visit_named_class_like_shape always seeds exactly
4130                        // one Container when a body is present.
4131                        _ => unreachable!("exported-class seed is always one Container"),
4132                    }
4133                }
4134            }
4135            while let Some(work) = stack.pop() {
4136                match work {
4137                    CppWork::Container(container) => {
4138                        push_cpp_container_work(container.node, container.scope, &mut stack);
4139                    }
4140                    CppWork::Siblings(siblings) => {
4141                        advance_cpp_siblings(siblings, self.source, &mut stack);
4142                    }
4143                    CppWork::Node(work) => {
4144                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
4145                    }
4146                }
4147            }
4148            return;
4149        }
4150        let recovered_constraint_constructor =
4151            cpp_recovered_template_macro_constructor(node, self.source);
4152        let declarator = recovered_constraint_constructor
4153            .map(|(declarator, _)| declarator)
4154            .or_else(|| node.child_by_field_name("declarator"));
4155        let Some(declarator) = declarator else {
4156            self.visit_malformed_function_definition_container(node, scope, stack);
4157            return;
4158        };
4159        let Some(function_declarator) = extract_function_declarator(declarator) else {
4160            self.visit_malformed_function_definition_container(node, scope, stack);
4161            return;
4162        };
4163        let function = if let Some((_, callable_name)) =
4164            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
4165        {
4166            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
4167        } else {
4168            extract_function_info(function_declarator, self.source, scope)
4169        };
4170        let Some(mut function) = function else {
4171            self.visit_malformed_function_definition_container(node, scope, stack);
4172            return;
4173        };
4174        if let Some((_, template_parameter)) = recovered_constraint_constructor {
4175            function.signature = format!(
4176                "template <{}>{}",
4177                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
4178                function.signature
4179            );
4180        }
4181        let code_unit = function.code_unit(self.file.clone());
4182        // Keep an earlier same-file prototype as another physical occurrence
4183        // of this callable. `CodeUnit` already identifies the role-neutral
4184        // overload, while ranges and signature metadata describe its
4185        // declaration/definition occurrences.
4186        self.add_declaration(code_unit.clone(), node, None, None);
4187        let signature = if recovered_constraint_constructor.is_some() {
4188            normalize_cpp_whitespace(node_text(function_declarator, self.source))
4189        } else {
4190            render_cpp_function_display_signature_from_node(
4191                node,
4192                self.source,
4193                scope.template_signature.as_deref(),
4194                true,
4195                ancestry,
4196            )
4197        };
4198        self.parsed.add_signature_with_metadata(
4199            code_unit.clone(),
4200            cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
4201                .with_declaration_only(false)
4202                .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
4203        );
4204        if let Some(parent) = &scope.class_unit {
4205            self.parsed.add_child(parent.clone(), code_unit);
4206        } else if let Some(module) = &scope.module {
4207            self.parsed.add_child(module.clone(), code_unit);
4208        }
4209    }
4210
4211    /// Recover the namespace lost when tree-sitter promotes an export-macro
4212    /// class definition to a root-level `function_definition`.  Only a
4213    /// body-bearing, top-level recovery may borrow a namespace, and only when
4214    /// one earlier namespace-scope forward declaration proves the identity.
4215    fn scope_for_recovered_exported_class<'tree>(
4216        &mut self,
4217        node: Node<'tree>,
4218        name: &str,
4219        definition_body_present: bool,
4220        scope: &ScopeInfo,
4221        ancestry: &ParentIndex<'tree>,
4222    ) -> ScopeInfo {
4223        if !definition_body_present
4224            || !scope.package_name.is_empty()
4225            || scope.class_unit.is_some()
4226            || !(is_recovered_exported_class_container(node, self.source)
4227                || recover_function_like_export_class_pair(node, self.source).is_some()
4228                || recover_embedded_function_like_export_classes(node, self.source)
4229                    .iter()
4230                    .any(|recovered| recovered.name == name)
4231                || matches!(node.kind(), "declaration" | "field_declaration")
4232                    && recover_exported_class_declaration(node, self.source).is_some()
4233                || matches!(
4234                    node.kind(),
4235                    "class_specifier" | "struct_specifier" | "union_specifier"
4236                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
4237                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
4238                        name_node,
4239                        self.source,
4240                    )))
4241                }) || ancestry.parent(node).is_some_and(|parent| {
4242                    matches!(parent.kind(), "declaration" | "field_declaration")
4243                        && recover_exported_class_declaration(parent, self.source).is_some()
4244                        || is_recovered_exported_class_container(parent, self.source)
4245                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
4246        {
4247            return scope.clone();
4248        }
4249        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
4250        let Some(package_name) = borrowed_namespace
4251            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
4252        else {
4253            return scope.clone();
4254        };
4255
4256        let module = CodeUnit::new_fq(
4257            self.file.clone(),
4258            CodeUnitType::Module,
4259            "",
4260            package_name.clone(),
4261            cpp_namespace_fq(&package_name),
4262        );
4263        let mut recovered = scope.clone();
4264        recovered.package_name = package_name;
4265        recovered.module = Some(module);
4266        recovered
4267    }
4268
4269    /// The unique namespace-scope forward declaration of `name` that precedes
4270    /// `recovered_node`, answered from the walk's carried-forward scan of the
4271    /// tree `recovered_node` belongs to.
4272    ///
4273    /// The scan is built on the first question and advanced by each later one,
4274    /// so a file that never reaches this path -- almost every file -- pays
4275    /// nothing, and one that reaches it thousands of times pays a single pass
4276    /// (#2754).
4277    fn unique_earlier_namespace_forward<'tree>(
4278        &mut self,
4279        recovered_node: Node<'tree>,
4280        name: &str,
4281        ancestry: &ParentIndex<'tree>,
4282    ) -> Option<String> {
4283        let mut root = recovered_node;
4284        while let Some(parent) = ancestry.parent(root) {
4285            root = parent;
4286        }
4287        let source = self.source;
4288        let scan = self
4289            .namespace_forward_scans
4290            .entry(CppTreeIdentity::of(root))
4291            .or_default();
4292        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
4293        let borrowed = scan.unique_earlier_forward(name, recovered_node);
4294
4295        #[cfg(debug_assertions)]
4296        assert_eq!(
4297            borrowed,
4298            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
4299            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
4300             for {name} at byte {}",
4301            recovered_node.start_byte()
4302        );
4303
4304        borrowed
4305    }
4306
4307    fn visit_malformed_function_definition_container<'tree>(
4308        &mut self,
4309        node: Node<'tree>,
4310        scope: &ScopeInfo,
4311        stack: &mut Vec<CppWork<'tree>>,
4312    ) {
4313        let Some(body) = cpp_body_node(node) else {
4314            return;
4315        };
4316        if !cpp_contains_namespace_definition(body) {
4317            return;
4318        }
4319        stack.push(CppWork::Container(CppContainer {
4320            node: body,
4321            scope: scope.clone(),
4322        }));
4323    }
4324
4325    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
4326    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
4327    /// emits for a sentinel-prefixed region, reparse the interior after the
4328    /// sentinel identifier as real C++ items -- confined to the region so
4329    /// every reparsed node keeps its original byte/line position -- and run the
4330    /// ordinary container visitation over the result. Returns `true` when it fired
4331    /// (the caller must then skip normal function processing). Nested sentinel
4332    /// regions recover recursively: the reparsed interior is walked through the
4333    /// same `visit_function_definition` path, so a sentinel inside the region hits
4334    /// this recovery again.
4335    /// Runs `reparse_walk` and records every declaration it mints as a
4336    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
4337    /// `recovery` (issue #1657). A reparsed sentinel region has no single
4338    /// recovered envelope unit: the ordinary visitors mint namespaces,
4339    /// classes, and members directly from the reparsed tree, so the walk's
4340    /// declaration delta is the recovered set. Records are ordered by
4341    /// declaration start byte so the parse product stays deterministic.
4342    fn record_recovered_declarations(
4343        &mut self,
4344        recovery: Range,
4345        reparse_walk: impl FnOnce(&mut Self),
4346    ) {
4347        // The set difference this used to be, kept as the oracle every answer
4348        // is asserted against (#2787).
4349        #[cfg(any(debug_assertions, test))]
4350        let before = self.parsed.declarations().clone();
4351
4352        self.recovery_captures.push(CppRecoveryCapture::default());
4353        reparse_walk(self);
4354        let captured = self
4355            .recovery_captures
4356            .pop()
4357            .expect("the capture this call pushed is the one it pops");
4358
4359        // The capture holds every declaration created while it was open, once
4360        // each and in creation order, so the recovered set costs what the
4361        // recovery made rather than everything the file has declared so far.
4362        // One filter is left to apply: a created declaration that a later
4363        // deferred replacement removed is not in the parse product to report.
4364        let mut minted: Vec<CodeUnit> = captured
4365            .created
4366            .into_iter()
4367            .filter(|unit| self.parsed.contains_declaration(unit))
4368            .collect();
4369        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
4370
4371        #[cfg(any(debug_assertions, test))]
4372        {
4373            let mut rediscovered: Vec<CodeUnit> = self
4374                .parsed
4375                .declarations()
4376                .iter()
4377                .filter(|unit| !before.contains(*unit))
4378                .cloned()
4379                .collect();
4380            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
4381            assert_eq!(
4382                minted, rediscovered,
4383                "the captured recovered set must be the declaration delta of the reparse \
4384                 walk over {recovery:?}"
4385            );
4386        }
4387
4388        for unit in minted {
4389            self.parsed
4390                .record_materialization(MaterializationRecord::RecoveredDeclaration {
4391                    recovery,
4392                    unit,
4393                });
4394        }
4395    }
4396
4397    /// Where one recovered declaration sorts: by start byte, then by name, so
4398    /// the parse product stays deterministic.
4399    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
4400        let start = self
4401            .parsed
4402            .declaration_ranges(unit)
4403            .first()
4404            .map(|range| range.start_byte)
4405            .unwrap_or(usize::MAX);
4406        (start, unit.fq_name().to_string())
4407    }
4408
4409    fn visit_sentinel_macro_region<'tree>(
4410        &mut self,
4411        node: Node<'tree>,
4412        scope: &ScopeInfo,
4413        stack: &mut Vec<CppWork<'tree>>,
4414        ancestry: &ParentIndex<'tree>,
4415    ) -> bool {
4416        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
4417            return true;
4418        }
4419        if let Some((
4420            reparse_start,
4421            class_start,
4422            body_start,
4423            class_close_start,
4424            class_close_end,
4425            class_close_line,
4426        )) = cpp_sentinel_macro_class_region(node, self.source)
4427        {
4428            let Some(class_tree) =
4429                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
4430            else {
4431                return false;
4432            };
4433            let class_root = class_tree.root_node();
4434            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
4435            // A region reparse is its own tree and needs its own parent index.
4436            let class_ancestry = ParentIndex::new(class_root);
4437            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
4438                class_root,
4439                template_node,
4440                self.source,
4441                &class_ancestry,
4442            ) else {
4443                return false;
4444            };
4445            let class_node = reparsed_class.declaration_node;
4446            let name = reparsed_class.name;
4447            let mut class_scope = scope.clone();
4448            if let Some(template_node) = template_node {
4449                class_scope.template_signature =
4450                    cpp_template_signature(template_node, class_node, self.source);
4451                class_scope.template_metadata =
4452                    cpp_template_metadata(template_node, class_node, self.source, ancestry);
4453            }
4454            let Some(body_tree) =
4455                cpp_reparse_region_items(self.source, body_start, class_close_start)
4456            else {
4457                return false;
4458            };
4459            let raw_supertypes = reparsed_class.raw_supertypes;
4460            let class_range = Range {
4461                start_byte: class_start,
4462                end_byte: class_close_end,
4463                start_line: class_node.start_position().row + 1,
4464                end_line: class_close_line,
4465            };
4466            let class_scope = self.scope_for_recovered_exported_class(
4467                class_node,
4468                &name,
4469                true,
4470                &class_scope,
4471                ancestry,
4472            );
4473            let mut class_stack = Vec::new();
4474            let class_unit = self.visit_named_class_like_shape(
4475                class_node,
4476                name,
4477                None,
4478                true,
4479                Some(class_range),
4480                raw_supertypes,
4481                &class_scope,
4482                &mut class_stack,
4483                ancestry,
4484            );
4485            self.parsed
4486                .record_materialization(MaterializationRecord::RecoveredDeclaration {
4487                    recovery: class_range,
4488                    unit: class_unit.clone(),
4489                });
4490            let member_scope = ScopeInfo {
4491                package_name: class_scope.package_name.clone(),
4492                module: class_scope.module.clone(),
4493                class_unit: Some(class_unit),
4494                template_signature: class_scope.template_signature.clone(),
4495                template_metadata: None,
4496                declarations_are_fields: true,
4497                recovered_specialization_member_scope: false,
4498                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
4499            };
4500            // The padded body reparse is its own tree, so it indexes itself.
4501            let body_root = body_tree.root_node();
4502            self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
4503            // Register only after the padded body reparse: its nodes deliberately
4504            // retain offsets inside the consumed region and must be visited first.
4505            self.consumed_fragment_regions
4506                .push((node.start_byte(), class_close_end));
4507            // An ERROR envelope can hold real sibling declarations after the
4508            // recovered class's close (the suffix-reparse boundary in
4509            // `cpp_sentinel_macro_class_region` partitions, it does not
4510            // consume). Walk the envelope's remaining children normally; the
4511            // consumed region above keeps the recovered class from being
4512            // indexed twice.
4513            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
4514                stack.push(CppWork::Container(CppContainer {
4515                    node,
4516                    scope: scope.clone(),
4517                }));
4518            }
4519            return true;
4520        }
4521        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
4522            return false;
4523        };
4524        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
4525            return false;
4526        };
4527        let root = tree.root_node();
4528        if !cpp_reparsed_items_are_indexable(root, self.source) {
4529            return false;
4530        }
4531        let recovery = cpp_recovery_window(self.source, start, end);
4532        // The reparsed region is its own tree, so this walk indexes it itself.
4533        let reparsed_ancestry = ParentIndex::new(root);
4534        self.record_recovered_declarations(recovery, |visitor| {
4535            visitor.visit_container(
4536                root,
4537                &reparsed_ancestry,
4538                &scope.package_name,
4539                scope.module.clone(),
4540                scope.class_unit.clone(),
4541                scope.template_signature.clone(),
4542                scope.visible_using_namespaces.clone(),
4543            );
4544        });
4545        if end > node.end_byte() {
4546            self.consumed_fragment_regions
4547                .push((node.start_byte(), end));
4548        } else if node.kind() == "ERROR" && node.end_byte() > end {
4549            // The sentinel region ended at the first recovered class-like item
4550            // but the ERROR envelope keeps real sibling declarations after it
4551            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
4552            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
4553            // envelope's remaining children normally; the consumed region
4554            // keeps the reparsed prefix from being indexed twice.
4555            self.consumed_fragment_regions
4556                .push((node.start_byte(), end));
4557            stack.push(CppWork::Container(CppContainer {
4558                node,
4559                scope: scope.clone(),
4560            }));
4561        }
4562        true
4563    }
4564
4565    /// Re-own complete class declarations from the structured Abseil
4566    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
4567    /// its direct CST children already prove both namespace components and the
4568    /// class bodies, so the ordinary class/member visitor can retain ownership
4569    /// and exact source ranges without admitting unrelated callable bodies.
4570    fn visit_nested_namespace_sentinel<'tree>(
4571        &mut self,
4572        node: Node<'tree>,
4573        scope: &ScopeInfo,
4574        ancestry: &ParentIndex<'tree>,
4575    ) -> bool {
4576        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
4577            return false;
4578        };
4579
4580        let mut package_name = scope.package_name.clone();
4581        let mut module = scope.module.clone();
4582        for component in recovered.namespace_components {
4583            package_name = if package_name.is_empty() {
4584                component
4585            } else {
4586                format!("{package_name}::{component}")
4587            };
4588            let namespace_module = CodeUnit::new_fq(
4589                self.file.clone(),
4590                CodeUnitType::Module,
4591                "",
4592                package_name.clone(),
4593                cpp_namespace_fq(&package_name),
4594            );
4595            if !self.parsed.contains_declaration(&namespace_module) {
4596                self.add_declaration(namespace_module.clone(), recovered.function, None, None);
4597            }
4598            module = Some(namespace_module);
4599        }
4600
4601        let recovered_scope = ScopeInfo {
4602            package_name,
4603            module,
4604            class_unit: scope.class_unit.clone(),
4605            template_signature: scope.template_signature.clone(),
4606            template_metadata: scope.template_metadata.clone(),
4607            declarations_are_fields: false,
4608            recovered_specialization_member_scope: false,
4609            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4610        };
4611        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
4612            recovered.function,
4613            recovered.body,
4614            self.source,
4615            ancestry,
4616        ) {
4617            let mut class_scope = recovered_scope.clone();
4618            if let Some(template_node) = fragmented.template_node {
4619                class_scope.template_signature =
4620                    cpp_template_signature(template_node, fragmented.class_node, self.source);
4621                class_scope.template_metadata = cpp_template_metadata(
4622                    template_node,
4623                    fragmented.class_node,
4624                    self.source,
4625                    ancestry,
4626                );
4627            }
4628            if let Some(outcome) = self
4629                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
4630            {
4631                let mut class_stack = Vec::new();
4632                let class_unit = self.visit_named_class_like_shape(
4633                    fragmented.class_node,
4634                    fragmented.name.clone(),
4635                    None,
4636                    true,
4637                    Some(fragmented.fragmented.class_range),
4638                    fragmented.raw_supertypes.clone(),
4639                    &class_scope,
4640                    &mut class_stack,
4641                    ancestry,
4642                );
4643                self.parsed
4644                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
4645                        recovery: fragmented.fragmented.class_range,
4646                        unit: class_unit.clone(),
4647                    });
4648                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
4649                    self.consumed_fragment_regions.push((
4650                        fragmented.consumed_start,
4651                        fragmented.fragmented.class_range.end_byte,
4652                    ));
4653                }
4654            }
4655        }
4656        // The class requirement above is the admission gate; once admitted,
4657        // traverse the whole proven inner namespace body so sibling aliases,
4658        // functions, and variables are not silently discarded. The body is a
4659        // node of the tree being walked, so it reuses that tree's index.
4660        self.run_container_work(recovered.body, recovered_scope, ancestry);
4661        true
4662    }
4663
4664    fn visit_declaration<'tree>(
4665        &mut self,
4666        node: Node<'tree>,
4667        scope: &ScopeInfo,
4668        in_class_body: bool,
4669        stack: &mut Vec<CppWork<'tree>>,
4670        ancestry: &ParentIndex<'tree>,
4671    ) {
4672        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4673            return;
4674        }
4675        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
4676            !cpp_active_template_type_parameter(
4677                node,
4678                node_text(declarator, self.source),
4679                self.source,
4680                ancestry,
4681            )
4682        }) {
4683            return;
4684        }
4685        if in_class_body
4686            && let Some(parent) = scope.class_unit.as_ref()
4687            && let Some(call) =
4688                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
4689        {
4690            self.visit_recovered_macro_qualified_constructor_definition(
4691                node, call, scope, ancestry,
4692            );
4693            return;
4694        }
4695        if in_class_body
4696            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
4697        {
4698            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
4699            return;
4700        }
4701        if in_class_body
4702            && let Some(declarators) =
4703                recovered_macro_qualified_field_declarators(node, self.source)
4704        {
4705            for declarator in declarators {
4706                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
4707            }
4708            return;
4709        }
4710        let recovered_alias_names = recovered_type_alias_names(node, self.source);
4711        if !recovered_alias_names.is_empty() {
4712            self.add_type_aliases(node, scope, recovered_alias_names);
4713            return;
4714        }
4715        if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
4716        {
4717            return;
4718        }
4719
4720        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
4721            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
4722                // Issue #938: the members tree-sitter scattered out of the fragmented
4723                // multiple-base export node are reparsed from their true body region
4724                // and re-owned as members of the recovered class, with an explicit
4725                // navigation range spanning to the displaced closing brace.
4726                if let Some(outcome) =
4727                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
4728                {
4729                    let consumed_region = (
4730                        recovered.declaration_node.end_byte(),
4731                        fragmented.class_range.end_byte,
4732                    );
4733                    let code_unit = self.visit_named_class_like_shape(
4734                        recovered.declaration_node,
4735                        recovered.name,
4736                        None,
4737                        true,
4738                        Some(fragmented.class_range),
4739                        recovered.raw_supertypes,
4740                        scope,
4741                        stack,
4742                        ancestry,
4743                    );
4744                    self.parsed.record_materialization(
4745                        MaterializationRecord::RecoveredDeclaration {
4746                            recovery: fragmented.class_range,
4747                            unit: code_unit.clone(),
4748                        },
4749                    );
4750                    let consume_fragment =
4751                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
4752                    // Everything between the fragmented declaration and its displaced
4753                    // closing brace now belongs to the recovered class; keep the
4754                    // ordinary walk from re-indexing those scattered siblings at top
4755                    // level. Register the consumed region only after indexing because
4756                    // the reparsed nodes retain byte offsets inside that same region.
4757                    if consume_fragment {
4758                        self.consumed_fragment_regions.push(consumed_region);
4759                    }
4760                    return;
4761                }
4762            }
4763            let uses_initializer_body = recovered.uses_initializer_body;
4764            let definition_body_present = recovered.body.is_some();
4765            let class_unit = self.visit_named_class_like_shape(
4766                recovered.declaration_node,
4767                recovered.name,
4768                recovered.body,
4769                definition_body_present,
4770                None,
4771                recovered.raw_supertypes,
4772                scope,
4773                stack,
4774                ancestry,
4775            );
4776            self.parsed
4777                .record_materialization(MaterializationRecord::RecoveredDeclaration {
4778                    recovery: cpp_declaration_range(node),
4779                    unit: class_unit,
4780                });
4781            if uses_initializer_body {
4782                return;
4783            }
4784        }
4785
4786        let mut handled_function = false;
4787        let mut handled_declarator = false;
4788        let mut cursor = node.walk();
4789        for child in node.named_children(&mut cursor) {
4790            if matches!(
4791                child.kind(),
4792                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
4793            ) {
4794                // A named class-like definition remains a declaration even when
4795                // the same statement also declares an object, for example
4796                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
4797                // declaration's type and `kind` as its declarator.  Dropping the
4798                // type here loses both its nested owner and every later lexical
4799                // reference to it.  A body is the structured proof that this is
4800                // a definition rather than an elaborated type use such as
4801                // `class Kind value;`.
4802                if cpp_body_node(child).is_some() {
4803                    self.visit_class_like(child, scope, stack, ancestry);
4804                }
4805                continue;
4806            }
4807        }
4808
4809        let mut cursor = node.walk();
4810        for child in node.children_by_field_name("declarator", &mut cursor) {
4811            if crate::structural::is_recovered_designator_init_declarator(child) {
4812                handled_declarator = true;
4813                continue;
4814            }
4815            if let Some(kind) = classify_declarator(child) {
4816                handled_declarator = true;
4817                match kind {
4818                    DeclaratorKind::Function(function_declarator) => {
4819                        handled_function = true;
4820                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
4821                    }
4822                    DeclaratorKind::Variable(variable_declarator) => {
4823                        self.visit_variable_declaration(
4824                            node,
4825                            variable_declarator,
4826                            scope,
4827                            in_class_body,
4828                            ancestry,
4829                        );
4830                    }
4831                }
4832            }
4833        }
4834
4835        if !handled_declarator {
4836            let mut cursor = node.walk();
4837            for child in node.named_children(&mut cursor) {
4838                if crate::structural::is_recovered_designator_init_declarator(child) {
4839                    handled_declarator = true;
4840                    continue;
4841                }
4842                if !is_unfielded_declarator_candidate(child) {
4843                    continue;
4844                }
4845                let Some(kind) = classify_declarator(child) else {
4846                    continue;
4847                };
4848                handled_declarator = true;
4849                match kind {
4850                    DeclaratorKind::Function(function_declarator) => {
4851                        handled_function = true;
4852                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
4853                    }
4854                    DeclaratorKind::Variable(variable_declarator) => {
4855                        self.visit_variable_declaration(
4856                            node,
4857                            variable_declarator,
4858                            scope,
4859                            in_class_body,
4860                            ancestry,
4861                        );
4862                    }
4863                }
4864            }
4865        }
4866
4867        if handled_function {
4868            return;
4869        }
4870
4871        if !handled_declarator {
4872            if in_class_body {
4873                self.visit_class_members_from_declaration(node, scope, ancestry);
4874            } else {
4875                self.visit_global_variables_from_declaration(node, scope, ancestry);
4876            }
4877        }
4878    }
4879
4880    /// Preserve the member structure of an anonymous C aggregate.
4881    ///
4882    /// An anonymous union with no declarator promotes its fields into the
4883    /// containing aggregate. An anonymous struct/union followed by a named
4884    /// declarator, such as `struct { T *ops; } sock`, declares both the field
4885    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
4886    /// the declarator's structured nested identity so a later `value.sock.ops`
4887    /// chain can traverse it without parsing a type spelling (#2407).
4888    fn visit_c_anonymous_aggregate_declaration<'tree>(
4889        &mut self,
4890        node: Node<'tree>,
4891        scope: &ScopeInfo,
4892        in_class_body: bool,
4893        stack: &mut Vec<CppWork<'tree>>,
4894        ancestry: &ParentIndex<'tree>,
4895    ) -> bool {
4896        if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
4897            return false;
4898        }
4899        let Some(aggregate) = node.child_by_field_name("type") else {
4900            return false;
4901        };
4902        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
4903            || aggregate.child_by_field_name("name").is_some()
4904        {
4905            return false;
4906        }
4907        let Some(body) = cpp_body_node(aggregate) else {
4908            return false;
4909        };
4910
4911        let mut cursor = node.walk();
4912        let declarators = node
4913            .children_by_field_name("declarator", &mut cursor)
4914            .filter_map(|declarator| match classify_declarator(declarator) {
4915                Some(DeclaratorKind::Variable(variable)) => Some(variable),
4916                Some(DeclaratorKind::Function(_)) | None => None,
4917            })
4918            .collect::<Vec<_>>();
4919        if declarators.is_empty() {
4920            stack.push(CppWork::Container(CppContainer {
4921                node: body,
4922                scope: scope.clone(),
4923            }));
4924            return true;
4925        }
4926
4927        for declarator in declarators {
4928            let Some(name) = extract_variable_name(declarator, self.source) else {
4929                continue;
4930            };
4931            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
4932            self.visit_named_class_like_shape(
4933                aggregate,
4934                name,
4935                Some(body),
4936                true,
4937                None,
4938                None,
4939                scope,
4940                stack,
4941                ancestry,
4942            );
4943        }
4944        true
4945    }
4946
4947    fn visit_function_declaration<'tree>(
4948        &mut self,
4949        declaration_node: Node<'tree>,
4950        declarator: Node<'tree>,
4951        scope: &ScopeInfo,
4952        ancestry: &ParentIndex<'tree>,
4953    ) {
4954        let Some(function) = extract_function_info(declarator, self.source, scope) else {
4955            return;
4956        };
4957        let code_unit =
4958            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
4959        if self.parsed.contains_declaration(&code_unit) {
4960            self.parsed
4961                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
4962            return;
4963        }
4964        self.add_declaration(code_unit.clone(), declaration_node, None, None);
4965        let signature = render_cpp_function_display_signature_from_node(
4966            declaration_node,
4967            self.source,
4968            scope.template_signature.as_deref(),
4969            false,
4970            ancestry,
4971        );
4972        self.parsed.add_signature_with_metadata(
4973            code_unit.clone(),
4974            cpp_signature_metadata(signature, declarator, self.source, ancestry)
4975                .with_declaration_only(true)
4976                .with_callable_linkage(cpp_callable_linkage(
4977                    declaration_node,
4978                    self.source,
4979                    ancestry,
4980                )),
4981        );
4982        if let Some(parent) = &scope.class_unit {
4983            self.parsed.add_child(parent.clone(), code_unit);
4984        } else if let Some(module) = &scope.module {
4985            self.parsed.add_child(module.clone(), code_unit);
4986        }
4987    }
4988
4989    fn visit_recovered_macro_qualified_function_declaration<'tree>(
4990        &mut self,
4991        declaration_node: Node<'tree>,
4992        call: Node<'tree>,
4993        scope: &ScopeInfo,
4994        ancestry: &ParentIndex<'tree>,
4995    ) {
4996        let Some(parent) = &scope.class_unit else {
4997            return;
4998        };
4999        let Some(name_node) = call.child_by_field_name("function") else {
5000            return;
5001        };
5002        let Some(arguments) = call.child_by_field_name("arguments") else {
5003            return;
5004        };
5005        let Some((signature, parameter_labels)) =
5006            recovered_macro_qualified_function_parameters(arguments, self.source)
5007        else {
5008            return;
5009        };
5010        let arity = parameter_labels.len();
5011        let function = FunctionInfo {
5012            package_name: scope.package_name.clone(),
5013            owner: Some(CppMemberOwner::Unit(parent.clone())),
5014            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
5015            signature,
5016        };
5017        if function.name.is_empty() {
5018            return;
5019        }
5020        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
5021        if self.parsed.contains_declaration(&code_unit) {
5022            self.parsed
5023                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
5024            return;
5025        }
5026        self.add_declaration(code_unit.clone(), declaration_node, None, None);
5027        let signature_label = render_cpp_function_display_signature_from_node(
5028            declaration_node,
5029            self.source,
5030            scope.template_signature.as_deref(),
5031            false,
5032            ancestry,
5033        );
5034        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
5035            .with_declaration_only(true)
5036            .with_callable_arity(CallableArity::exact(arity))
5037            .with_callable_linkage(cpp_callable_linkage(
5038                declaration_node,
5039                self.source,
5040                ancestry,
5041            ));
5042        self.parsed
5043            .add_signature_with_metadata(code_unit.clone(), metadata);
5044        self.parsed.add_child(parent.clone(), code_unit);
5045    }
5046
5047    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
5048        &mut self,
5049        declaration_node: Node<'tree>,
5050        call: Node<'tree>,
5051        scope: &ScopeInfo,
5052        ancestry: &ParentIndex<'tree>,
5053    ) {
5054        let Some(parent) = &scope.class_unit else {
5055            return;
5056        };
5057        let Some(arguments) = call.child_by_field_name("arguments") else {
5058            return;
5059        };
5060        let Some((mut signature, parameter_labels)) =
5061            recovered_macro_qualified_function_parameters(arguments, self.source)
5062        else {
5063            return;
5064        };
5065        if let Some(template_signature) = &scope.template_signature {
5066            signature = format!("{template_signature}{signature}");
5067        }
5068        let arity = parameter_labels.len();
5069        let function = FunctionInfo {
5070            package_name: scope.package_name.clone(),
5071            owner: Some(CppMemberOwner::Unit(parent.clone())),
5072            name: parent.identifier().to_string(),
5073            signature,
5074        };
5075        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
5076        self.add_declaration(code_unit.clone(), declaration_node, None, None);
5077        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
5078        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
5079            .with_declaration_only(false)
5080            .with_callable_arity(CallableArity::exact(arity))
5081            .with_callable_linkage(cpp_callable_linkage(
5082                declaration_node,
5083                self.source,
5084                ancestry,
5085            ));
5086        self.parsed
5087            .add_signature_with_metadata(code_unit.clone(), metadata);
5088        self.parsed.add_child(parent.clone(), code_unit);
5089    }
5090
5091    fn visit_variable_declaration<'tree>(
5092        &mut self,
5093        declaration_node: Node<'tree>,
5094        declarator: Node<'tree>,
5095        scope: &ScopeInfo,
5096        in_class_body: bool,
5097        ancestry: &ParentIndex<'tree>,
5098    ) {
5099        let Some(name) = extract_variable_name(declarator, self.source) else {
5100            return;
5101        };
5102        let parent = if in_class_body {
5103            let Some(parent) = &scope.class_unit else {
5104                return;
5105            };
5106            Some(parent)
5107        } else {
5108            None
5109        };
5110        let short_name = match parent {
5111            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
5112            None => name.clone(),
5113        };
5114        let fq = cpp_leaf_fq(
5115            &scope.package_name,
5116            parent,
5117            &name,
5118            SegmentKind::Member,
5119            SegmentKind::Member,
5120        );
5121        let code_unit = CodeUnit::new_fq(
5122            self.file.clone(),
5123            CodeUnitType::Field,
5124            scope.package_name.clone(),
5125            short_name,
5126            fq,
5127        );
5128        if self.parsed.contains_declaration(&code_unit) {
5129            return;
5130        }
5131        self.add_declaration(code_unit.clone(), declaration_node, None, None);
5132        self.parsed.add_signature_with_metadata(
5133            code_unit.clone(),
5134            SignatureMetadata::new(
5135                render_cpp_field_signature(declaration_node, declarator, self.source),
5136                Vec::new(),
5137            )
5138            .with_cpp_field_linkage(cpp_field_declaration_linkage(
5139                declaration_node,
5140                self.source,
5141                ancestry,
5142            )),
5143        );
5144        if let Some(parent) = &scope.class_unit {
5145            self.parsed.add_child(parent.clone(), code_unit);
5146        } else if let Some(module) = &scope.module {
5147            self.parsed.add_child(module.clone(), code_unit);
5148        }
5149    }
5150
5151    fn visit_class_members_from_declaration<'tree>(
5152        &mut self,
5153        node: Node<'tree>,
5154        scope: &ScopeInfo,
5155        ancestry: &ParentIndex<'tree>,
5156    ) {
5157        let mut cursor = node.walk();
5158        for child in node.named_children(&mut cursor) {
5159            if child.kind() == "init_declarator"
5160                && let Some(inner) = child.child_by_field_name("declarator")
5161            {
5162                self.visit_variable_declaration(node, inner, scope, true, ancestry);
5163            } else if matches!(
5164                child.kind(),
5165                "identifier"
5166                    | "field_identifier"
5167                    | "pointer_declarator"
5168                    | "reference_declarator"
5169                    | "array_declarator"
5170                    | "parenthesized_declarator"
5171            ) {
5172                self.visit_variable_declaration(node, child, scope, true, ancestry);
5173            }
5174        }
5175    }
5176
5177    fn visit_global_variables_from_declaration<'tree>(
5178        &mut self,
5179        node: Node<'tree>,
5180        scope: &ScopeInfo,
5181        ancestry: &ParentIndex<'tree>,
5182    ) {
5183        let mut cursor = node.walk();
5184        for child in node.named_children(&mut cursor) {
5185            if child.kind() == "init_declarator"
5186                && let Some(inner) = child.child_by_field_name("declarator")
5187            {
5188                self.visit_variable_declaration(node, inner, scope, false, ancestry);
5189            } else if matches!(
5190                child.kind(),
5191                "identifier"
5192                    | "field_identifier"
5193                    | "pointer_declarator"
5194                    | "reference_declarator"
5195                    | "array_declarator"
5196                    | "parenthesized_declarator"
5197            ) {
5198                self.visit_variable_declaration(node, child, scope, false, ancestry);
5199            }
5200        }
5201    }
5202
5203    fn visit_type_declaration<'tree>(
5204        &mut self,
5205        node: Node<'tree>,
5206        scope: &ScopeInfo,
5207        stack: &mut Vec<CppWork<'tree>>,
5208        ancestry: &ParentIndex<'tree>,
5209    ) {
5210        let type_node = node.child_by_field_name("type");
5211        if let Some(type_node) = type_node
5212            && matches!(
5213                type_node.kind(),
5214                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
5215            )
5216        {
5217            self.visit_class_like(type_node, scope, stack, ancestry);
5218        }
5219
5220        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
5221            let range = Range {
5222                start_byte: node.start_byte(),
5223                end_byte: recovered.end_node.end_byte(),
5224                start_line: node.start_position().row + 1,
5225                end_line: recovered.end_node.end_position().row + 1,
5226            };
5227            let signature = self
5228                .source
5229                .get(range.start_byte..range.end_byte)
5230                .map(normalize_cpp_whitespace)
5231                .unwrap_or_default();
5232            self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
5233            return;
5234        }
5235
5236        let alias_names = match node.kind() {
5237            "alias_declaration" => extract_alias_declaration_name(node, self.source)
5238                .into_iter()
5239                .collect::<Vec<_>>(),
5240            "type_definition" => extract_typedef_alias_names(node, self.source),
5241            _ => Vec::new(),
5242        };
5243        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
5244            (type_node, alias_names.as_slice())
5245            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
5246            && type_node.child_by_field_name("name").is_none()
5247        {
5248            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
5249        } else {
5250            None
5251        };
5252        self.add_type_aliases(node, scope, alias_names);
5253        if let Some((body, alias_name)) = anonymous_aggregate {
5254            // The typedef alias is also the only user-visible identity of an
5255            // anonymous aggregate. Reuse it as the member owner instead of
5256            // minting a second signatureless class with the same FQN. The
5257            // latter makes forward lookup ambiguous when conditional aliases
5258            // coexist and returns duplicate definitions even without guards.
5259            let signature = normalize_cpp_whitespace(node_text(node, self.source));
5260            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
5261            debug_assert!(self.parsed.contains_declaration(&alias_unit));
5262            let mut nested_scope = scope.clone();
5263            nested_scope.class_unit = Some(alias_unit);
5264            nested_scope.template_signature = scope.template_signature.clone();
5265            nested_scope.template_metadata = None;
5266            nested_scope.declarations_are_fields = false;
5267            nested_scope.recovered_specialization_member_scope = false;
5268            stack.push(CppWork::Container(CppContainer {
5269                node: body,
5270                scope: nested_scope,
5271            }));
5272        }
5273    }
5274
5275    fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
5276        let signature = normalize_cpp_whitespace(node_text(node, self.source));
5277        self.record_type_aliases(
5278            node,
5279            scope,
5280            alias_names,
5281            signature,
5282            cpp_declaration_range(node),
5283        );
5284    }
5285
5286    fn record_type_aliases(
5287        &mut self,
5288        node: Node<'_>,
5289        scope: &ScopeInfo,
5290        alias_names: Vec<String>,
5291        signature: String,
5292        range: Range,
5293    ) {
5294        if signature.is_empty() {
5295            return;
5296        }
5297        let type_name = node
5298            .child_by_field_name("type")
5299            .and_then(|type_node| type_node.child_by_field_name("name"))
5300            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
5301        for alias_name in alias_names {
5302            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
5303                continue;
5304            }
5305            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
5306            // Declaration identity does not include the alias signature. Keep
5307            // each physical range so conditional aliases retain their guards.
5308            self.add_declaration_with_range(code_unit.clone(), range, None, None);
5309            self.parsed
5310                .add_signature(code_unit.clone(), signature.clone());
5311            if let Some(metadata) = &scope.template_metadata {
5312                let mut metadata = metadata.clone();
5313                metadata.primary_fq_name = code_unit.fq_name();
5314                self.parsed
5315                    .set_cpp_template_metadata(code_unit.clone(), metadata);
5316            }
5317            if let Some(parent) = &scope.class_unit {
5318                self.parsed.add_child(parent.clone(), code_unit.clone());
5319            } else if let Some(module) = &scope.module {
5320                self.parsed.add_child(module.clone(), code_unit.clone());
5321            }
5322            self.parsed.mark_type_alias(code_unit);
5323        }
5324    }
5325
5326    fn type_alias_unit(
5327        &self,
5328        scope: &ScopeInfo,
5329        alias_name: String,
5330        signature: String,
5331    ) -> CodeUnit {
5332        let short_name = if let Some(parent) = &scope.class_unit {
5333            cpp_join_nested_short(parent.short_name(), &alias_name)
5334        } else {
5335            alias_name.clone()
5336        };
5337        let fq = cpp_leaf_fq(
5338            &scope.package_name,
5339            scope.class_unit.as_ref(),
5340            &alias_name,
5341            SegmentKind::Nested,
5342            SegmentKind::Type,
5343        );
5344        CodeUnit::with_signature_and_fq(
5345            self.file.clone(),
5346            CodeUnitType::Class,
5347            scope.package_name.clone(),
5348            short_name,
5349            Some(signature),
5350            false,
5351            fq,
5352        )
5353    }
5354
5355    fn visit_macro(&mut self, node: Node<'_>) {
5356        let Some(name) = extract_macro_name(node, self.source) else {
5357            return;
5358        };
5359        let signature = node_text(node, self.source).trim_end().to_string();
5360        if signature.is_empty() {
5361            return;
5362        }
5363        let fq = cpp_member_fq("", &name);
5364        // A macro can be undefined and redefined later in the same file. Its
5365        // structured directive is part of the declaration identity so the
5366        // temporal environment can navigate to the definition active at a
5367        // reference instead of collapsing every spelling to the first range.
5368        // The same physical directive parsed through another C/C++ reading
5369        // still produces the same unit and remains deduplicated.
5370        let code_unit = CodeUnit::with_signature_and_fq(
5371            self.file.clone(),
5372            CodeUnitType::Macro,
5373            "",
5374            name,
5375            Some(signature.clone()),
5376            false,
5377            fq,
5378        );
5379        if self.parsed.contains_declaration(&code_unit) {
5380            return;
5381        }
5382        self.add_declaration(code_unit.clone(), node, None, None);
5383        let name_range = node
5384            .child_by_field_name("name")
5385            .map(cpp_declaration_range)
5386            .unwrap_or_else(|| cpp_declaration_range(node));
5387        self.parsed
5388            .record_materialization(MaterializationRecord::GeneratedDeclaration {
5389                site: cpp_declaration_range(node),
5390                argument: name_range,
5391                kind: GenerationKind::PreprocessorDefinition,
5392                unit: code_unit.clone(),
5393            });
5394        self.parsed.add_signature(code_unit, signature);
5395    }
5396}
5397
5398/// Classify a C++ field while its declaration syntax is already available.
5399///
5400/// The persisted result lets later visibility queries avoid reparsing the
5401/// complete source file only to recover linkage.
5402pub fn cpp_field_declaration_linkage<'tree>(
5403    declaration: Node<'tree>,
5404    source: &str,
5405    ancestry: &ParentIndex<'tree>,
5406) -> CppFieldLinkage {
5407    let mut current = ancestry.parent(declaration);
5408    let mut enclosed_by_class = false;
5409    while let Some(node) = current {
5410        if node.kind() == "namespace_definition"
5411            && node
5412                .child_by_field_name("name")
5413                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
5414        {
5415            return CppFieldLinkage::Internal;
5416        }
5417        if matches!(
5418            node.kind(),
5419            "class_specifier" | "struct_specifier" | "union_specifier"
5420        ) && node
5421            .child_by_field_name("name")
5422            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
5423        {
5424            return CppFieldLinkage::Internal;
5425        }
5426        if matches!(
5427            node.kind(),
5428            "class_specifier" | "struct_specifier" | "union_specifier"
5429        ) {
5430            enclosed_by_class = true;
5431        }
5432        if matches!(node.kind(), "function_definition" | "lambda_expression") {
5433            return CppFieldLinkage::Internal;
5434        }
5435        current = ancestry.parent(node);
5436    }
5437    if enclosed_by_class {
5438        return CppFieldLinkage::External;
5439    }
5440    let mut cursor = declaration.walk();
5441    let mut has_static = false;
5442    let mut has_extern = false;
5443    let mut has_inline = false;
5444    let mut has_const = false;
5445    let mut has_constexpr = false;
5446    for child in declaration.named_children(&mut cursor) {
5447        let text = normalize_cpp_whitespace(node_text(child, source));
5448        match (child.kind(), text.as_str()) {
5449            ("storage_class_specifier", "static") => has_static = true,
5450            ("storage_class_specifier", "extern") => has_extern = true,
5451            ("storage_class_specifier", "inline") => has_inline = true,
5452            ("storage_class_specifier", "constexpr") => has_constexpr = true,
5453            ("type_qualifier", "const") => has_const = true,
5454            ("type_qualifier", "constexpr") => has_constexpr = true,
5455            _ => {}
5456        }
5457    }
5458    if has_static {
5459        CppFieldLinkage::Internal
5460    } else if has_extern || has_inline {
5461        CppFieldLinkage::External
5462    } else if has_const || has_constexpr {
5463        CppFieldLinkage::InternalUnlessExternalPeer
5464    } else {
5465        CppFieldLinkage::External
5466    }
5467}
5468
5469fn cpp_declaration_range(node: Node<'_>) -> Range {
5470    Range {
5471        start_byte: node.start_byte(),
5472        end_byte: node.end_byte(),
5473        start_line: node.start_position().row + 1,
5474        end_line: node.end_position().row + 1,
5475    }
5476}
5477
5478/// A recovery interval as a [`Range`], for materialization records whose
5479/// window is a byte region rather than one parser node (the sentinel-macro
5480/// region reparses, issue #941/#1657).
5481fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
5482    let line_at = |byte: usize| {
5483        source.as_bytes()[..byte]
5484            .iter()
5485            .filter(|&&b| b == b'\n')
5486            .count()
5487            + 1
5488    };
5489    Range {
5490        start_byte,
5491        end_byte,
5492        start_line: line_at(start_byte),
5493        end_line: line_at(end_byte),
5494    }
5495}
5496
5497/// Every `#include` directive the tree holds, in source order.
5498///
5499/// A preorder sweep rather than a step of the declaration walk: the container
5500/// walk descends only through declaration scopes, so an include written inside
5501/// a class body or a function body would otherwise never be seen, and an
5502/// include is an include wherever it is written.
5503pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
5504    walk_named_tree_preorder(root, true, |node| {
5505        if node.kind() == "preproc_include" {
5506            let raw = normalize_cpp_whitespace(node_text(node, source));
5507            if !raw.is_empty() {
5508                parsed.imports.push(ImportInfo {
5509                    raw_snippet: raw,
5510                    is_wildcard: false,
5511                    is_global: false,
5512                    identifier: None,
5513                    alias: None,
5514                    path: None,
5515                    binder_span: None,
5516                });
5517            }
5518            return WalkControl::SkipChildren;
5519        }
5520        WalkControl::Continue
5521    });
5522}
5523
5524pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
5525    let mut in_block_comment = false;
5526    for line in source.lines() {
5527        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
5528        let trimmed = stripped.trim();
5529        if !looks_like_quoted_include_line(trimmed) {
5530            continue;
5531        }
5532
5533        let raw = normalize_cpp_whitespace(trimmed);
5534        // The tree-sitter walk already recorded every `#include` it could see;
5535        // this line scan only recovers the ones a parse error hid, so skip a
5536        // snippet that is already an import binding.
5537        if parsed
5538            .imports
5539            .iter()
5540            .any(|import| import.raw_snippet == raw)
5541        {
5542            continue;
5543        }
5544
5545        parsed.imports.push(ImportInfo {
5546            raw_snippet: raw,
5547            is_wildcard: false,
5548            is_global: false,
5549            identifier: None,
5550            alias: None,
5551            path: None,
5552            binder_span: None,
5553        });
5554    }
5555}
5556
5557fn looks_like_quoted_include_line(line: &str) -> bool {
5558    let Some(rest) = line.trim_start().strip_prefix('#') else {
5559        return false;
5560    };
5561    let Some(rest) = rest.trim_start().strip_prefix("include") else {
5562        return false;
5563    };
5564    rest.trim_start().starts_with('"')
5565}
5566
5567fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
5568    let mut raw = Vec::new();
5569    let mut cursor = node.walk();
5570    for child in node.named_children(&mut cursor) {
5571        if child.kind() == "base_class_clause" {
5572            collect_cpp_base_nodes(child, source, &mut raw);
5573        }
5574    }
5575    raw
5576}
5577
5578fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
5579    walk_named_tree_preorder(node, false, |child| match child.kind() {
5580        "type_identifier" | "qualified_identifier" | "template_type" => {
5581            let text = normalize_cpp_whitespace(node_text(child, source));
5582            if !text.is_empty() {
5583                raw.push(text);
5584            }
5585            WalkControl::SkipChildren
5586        }
5587        _ => WalkControl::Continue,
5588    });
5589}
5590
5591fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
5592    let mut out = String::new();
5593    let chars: Vec<char> = line.chars().collect();
5594    let mut index = 0;
5595    let mut in_string = false;
5596    let mut in_char = false;
5597    let mut escape = false;
5598
5599    while index < chars.len() {
5600        let ch = chars[index];
5601        let next = chars.get(index + 1).copied();
5602
5603        if *in_block_comment {
5604            if ch == '*' && next == Some('/') {
5605                *in_block_comment = false;
5606                index += 2;
5607            } else {
5608                index += 1;
5609            }
5610            continue;
5611        }
5612
5613        if in_string {
5614            out.push(ch);
5615            if escape {
5616                escape = false;
5617            } else if ch == '\\' {
5618                escape = true;
5619            } else if ch == '"' {
5620                in_string = false;
5621            }
5622            index += 1;
5623            continue;
5624        }
5625
5626        if in_char {
5627            out.push(ch);
5628            if escape {
5629                escape = false;
5630            } else if ch == '\\' {
5631                escape = true;
5632            } else if ch == '\'' {
5633                in_char = false;
5634            }
5635            index += 1;
5636            continue;
5637        }
5638
5639        if ch == '/' && next == Some('/') {
5640            break;
5641        }
5642        if ch == '/' && next == Some('*') {
5643            *in_block_comment = true;
5644            index += 2;
5645            continue;
5646        }
5647        if ch == '"' {
5648            in_string = true;
5649            out.push(ch);
5650            index += 1;
5651            continue;
5652        }
5653        if ch == '\'' {
5654            in_char = true;
5655            out.push(ch);
5656            index += 1;
5657            continue;
5658        }
5659
5660        out.push(ch);
5661        index += 1;
5662    }
5663
5664    out
5665}
5666
5667#[derive(Clone)]
5668struct FunctionInfo {
5669    package_name: String,
5670    owner: Option<CppMemberOwner>,
5671    name: String,
5672    signature: String,
5673}
5674
5675/// Owner of a member function, kept structured so a literal `$` inside a
5676/// source-level class name never crosses a join/split boundary: the legacy
5677/// `$`-joined owner string was re-split at fq construction, dropping a leading
5678/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
5679/// and tripping the package/short boundary assert -- the #2140 corruption one
5680/// level up (#2362).
5681#[derive(Clone)]
5682enum CppMemberOwner {
5683    /// Source-level owner class chain from a qualified declarator-id, one
5684    /// class name per component (`Outer::Inner::method` -> `["Outer",
5685    /// "Inner"]`); each component may itself contain a literal `$`.
5686    Chain(Vec<String>),
5687    /// The lexically enclosing or recovered class unit; the member fq extends
5688    /// its fq directly instead of re-splitting its `$`-joined short chain.
5689    Unit(CodeUnit),
5690}
5691
5692impl CppMemberOwner {
5693    /// The legacy `$`-joined owner chain used in the member's short name.
5694    fn short_chain(&self) -> String {
5695        match self {
5696            Self::Chain(chain) => chain.join("$"),
5697            Self::Unit(parent) => parent.short_name().to_string(),
5698        }
5699    }
5700}
5701
5702enum DeclaratorKind<'a> {
5703    Function(Node<'a>),
5704    Variable(Node<'a>),
5705}
5706
5707impl FunctionInfo {
5708    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
5709        self.code_unit_with_synthetic(file, false)
5710    }
5711
5712    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
5713        let short_name = match &self.owner {
5714            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
5715            None => self.name.clone(),
5716        };
5717        let fq = match &self.owner {
5718            Some(CppMemberOwner::Chain(chain)) => {
5719                debug_assert!(
5720                    !chain.is_empty(),
5721                    "an empty owner chain is no owner; producers return None instead"
5722                );
5723                let mut fq = FqName::new();
5724                cpp_push_package(&mut fq, &self.package_name);
5725                let mut first = true;
5726                for component in chain {
5727                    let kind = if first {
5728                        SegmentKind::Type
5729                    } else {
5730                        SegmentKind::Nested
5731                    };
5732                    fq.push(cpp_segment(component, kind));
5733                    first = false;
5734                }
5735                fq.push(cpp_segment(&self.name, SegmentKind::Member));
5736                fq
5737            }
5738            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
5739                .fq()
5740                .clone()
5741                .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
5742            // An anonymous parent (empty short chain) contributes no owner
5743            // segment -- the same guard as cpp_join_member_short above.
5744            Some(CppMemberOwner::Unit(_)) | None => {
5745                let mut fq = FqName::new();
5746                cpp_push_package(&mut fq, &self.package_name);
5747                fq.push(cpp_segment(&self.name, SegmentKind::Member));
5748                fq
5749            }
5750        };
5751        CodeUnit::with_signature_and_fq(
5752            file,
5753            CodeUnitType::Function,
5754            self.package_name.clone(),
5755            short_name,
5756            Some(self.signature.clone()),
5757            synthetic,
5758            fq,
5759        )
5760    }
5761}
5762
5763fn extract_function_info(
5764    declarator: Node<'_>,
5765    source: &str,
5766    scope: &ScopeInfo,
5767) -> Option<FunctionInfo> {
5768    let parameters_node = declarator.child_by_field_name("parameters")?;
5769    let declarator_name_node = declarator
5770        .child_by_field_name("declarator")
5771        .or_else(|| parameters_node.prev_named_sibling())?;
5772    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
5773}
5774
5775fn extract_function_info_from_name(
5776    declarator: Node<'_>,
5777    declarator_name_node: Node<'_>,
5778    source: &str,
5779    scope: &ScopeInfo,
5780) -> Option<FunctionInfo> {
5781    let parameters_node = declarator.child_by_field_name("parameters")?;
5782    let parameters_text = cpp_parameter_signature(parameters_node, source);
5783    let recovered_specialization_member = scope
5784        .recovered_specialization_member_scope
5785        .then(|| {
5786            let terminal = declarator_name_node
5787                .child_by_field_name("name")
5788                .unwrap_or(declarator_name_node);
5789            let name = canonical_cpp_qualified_component(terminal, source)?.name;
5790            let owner = scope.class_unit.as_ref()?;
5791            Some((
5792                Some(CppMemberOwner::Unit(owner.clone())),
5793                name,
5794                scope.package_name.clone(),
5795            ))
5796        })
5797        .flatten();
5798    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
5799        parts
5800    } else if let Some(parts) =
5801        split_structured_templated_cpp_name(declarator_name_node, source, scope)
5802    {
5803        parts
5804    } else {
5805        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
5806            declarator_name_node,
5807            source,
5808        )?);
5809        if raw_name.is_empty() {
5810            return None;
5811        }
5812        split_cpp_name(&raw_name, scope)
5813    };
5814    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
5815    let mut signature = if suffix.is_empty() {
5816        parameters_text
5817    } else {
5818        format!("{parameters_text} {suffix}")
5819    };
5820    if let Some(template_signature) = &scope.template_signature {
5821        signature = format!("{template_signature}{signature}");
5822    }
5823
5824    Some(FunctionInfo {
5825        package_name,
5826        owner,
5827        name,
5828        signature,
5829    })
5830}
5831
5832/// Recover the semantic return type and callable name when a declaration macro
5833/// occupies a function definition's `type` field. Tree-sitter either exposes a
5834/// scalar return as the declarator's apparent name and the callable as the sole
5835/// identifier in an `ERROR`, or joins a template return and callable into a
5836/// qualified identifier with a missing `::`. Both shapes retain the complete
5837/// parameter list and body; a concrete separator remains an out-of-line member.
5838fn cpp_macro_displaced_callable_parts<'tree>(
5839    function_declarator: Node<'tree>,
5840    source: &str,
5841    ancestry: &ParentIndex<'tree>,
5842) -> Option<(Node<'tree>, Node<'tree>)> {
5843    let definition = ancestry.parent(function_declarator)?;
5844    if definition.kind() != "function_definition"
5845        || definition.child_by_field_name("declarator") != Some(function_declarator)
5846        || definition
5847            .child_by_field_name("body")
5848            .is_none_or(|body| body.kind() != "compound_statement")
5849    {
5850        return None;
5851    }
5852    let macro_type = definition.child_by_field_name("type")?;
5853    if macro_type.kind() != "type_identifier"
5854        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
5855    {
5856        return None;
5857    }
5858
5859    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
5860    if apparent_return_type.kind() == "qualified_identifier"
5861        && let (Some(return_type), Some(callable_name)) = (
5862            apparent_return_type.child_by_field_name("scope"),
5863            apparent_return_type.child_by_field_name("name"),
5864        )
5865        && return_type.kind() == "template_type"
5866        && matches!(callable_name.kind(), "identifier" | "field_identifier")
5867        && (0..apparent_return_type.child_count())
5868            .filter_map(|index| apparent_return_type.child(index))
5869            .any(|child| child.kind() == "::" && child.is_missing())
5870        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
5871        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
5872    {
5873        return Some((return_type, callable_name));
5874    }
5875    if !matches!(
5876        apparent_return_type.kind(),
5877        "identifier" | "field_identifier" | "type_identifier"
5878    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
5879    {
5880        return None;
5881    }
5882    let parameters = function_declarator.child_by_field_name("parameters")?;
5883    let mut cursor = function_declarator.walk();
5884    let between = function_declarator
5885        .named_children(&mut cursor)
5886        .filter(|child| child.kind() != "comment")
5887        .filter(|child| {
5888            child.start_byte() >= apparent_return_type.end_byte()
5889                && child.end_byte() <= parameters.start_byte()
5890                && !same_node(*child, apparent_return_type)
5891                && !same_node(*child, parameters)
5892        })
5893        .collect::<Vec<_>>();
5894    let [name_error] = between.as_slice() else {
5895        return None;
5896    };
5897    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
5898        return None;
5899    }
5900    let callable_name = name_error.named_child(0)?;
5901    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
5902        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
5903    {
5904        return None;
5905    }
5906    Some((apparent_return_type, callable_name))
5907}
5908
5909/// The part of a `function_declarator` after its parameter list that belongs to
5910/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
5911/// specification, a trailing return type and a trailing requires-clause.
5912///
5913/// The grammar makes each of these a distinct sibling of the `parameters`
5914/// field, so they are read from the tree. Splitting the declarator's text on
5915/// the parameter list instead silently dropped every qualifier whenever the
5916/// parameter list was spelled with whitespace that normalization rewrote - a
5917/// line break or a double space was enough to make a `const` member definition
5918/// a different logical symbol from its declaration (#1827).
5919///
5920/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
5921/// are deliberately excluded. C++ does not make them part of the signature and
5922/// an out-of-line definition never repeats them, so including them would split
5923/// a declaration from its own definition.
5924fn cpp_declarator_identity_suffix(
5925    declarator: Node<'_>,
5926    parameters_node: Node<'_>,
5927    source: &str,
5928) -> String {
5929    let mut cursor = declarator.walk();
5930    let parts = declarator
5931        .named_children(&mut cursor)
5932        .filter(|child| child.start_byte() >= parameters_node.end_byte())
5933        .filter(|child| {
5934            matches!(
5935                child.kind(),
5936                "type_qualifier"
5937                    | "ref_qualifier"
5938                    | "noexcept"
5939                    | "throw_specifier"
5940                    | "trailing_return_type"
5941                    | "requires_clause"
5942            )
5943        })
5944        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
5945        .filter(|text| !text.is_empty())
5946        .collect::<Vec<_>>();
5947    normalize_cpp_qualifier_suffix(&parts.join(" "))
5948}
5949
5950/// The identity suffix of one callable declarator, for a consumer that holds
5951/// the declarator rather than the declaration walk's parts.
5952///
5953/// The persisted signature concatenates the parameter spelling and this suffix,
5954/// so a comparison that must agree on the suffix alone recomputes it here
5955/// instead of splitting the stored string.
5956pub(crate) fn cpp_callable_identity_suffix(
5957    function_declarator: Node<'_>,
5958    source: &str,
5959) -> Option<String> {
5960    let parameters_node = function_declarator.child_by_field_name("parameters")?;
5961    Some(cpp_declarator_identity_suffix(
5962        function_declarator,
5963        parameters_node,
5964        source,
5965    ))
5966}
5967
5968fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
5969    match classify_declarator(node)? {
5970        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
5971        DeclaratorKind::Variable(_) => None,
5972    }
5973}
5974
5975fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
5976    match node.kind() {
5977        "function_declarator" => {
5978            let inner = node
5979                .child_by_field_name("declarator")
5980                .or_else(|| node.child_by_field_name("name"))
5981                .or_else(|| last_named_child(node));
5982            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
5983                Some(DeclaratorKind::Variable(node))
5984            } else {
5985                Some(DeclaratorKind::Function(node))
5986            }
5987        }
5988        "init_declarator"
5989        | "pointer_declarator"
5990        | "reference_declarator"
5991        | "parenthesized_declarator"
5992        | "array_declarator"
5993        | "attributed_declarator"
5994        | "template_function" => node
5995            .child_by_field_name("declarator")
5996            .or_else(|| node.child_by_field_name("name"))
5997            .or_else(|| last_named_child(node))
5998            .and_then(classify_declarator),
5999        "identifier" | "field_identifier" | "qualified_identifier" => {
6000            Some(DeclaratorKind::Variable(node))
6001        }
6002        _ => node
6003            .child_by_field_name("declarator")
6004            .or_else(|| node.child_by_field_name("name"))
6005            .or_else(|| last_named_child(node))
6006            .and_then(classify_declarator),
6007    }
6008}
6009
6010fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
6011    matches!(
6012        node.kind(),
6013        "function_declarator"
6014            | "init_declarator"
6015            | "pointer_declarator"
6016            | "reference_declarator"
6017            | "parenthesized_declarator"
6018            | "array_declarator"
6019            | "attributed_declarator"
6020            | "template_function"
6021            | "identifier"
6022            | "field_identifier"
6023            | "qualified_identifier"
6024    )
6025}
6026
6027fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
6028    let class_like = first_class_like_child(node);
6029    let mut cursor = node.walk();
6030    node.named_children(&mut cursor).any(|child| {
6031        matches!(
6032            child.kind(),
6033            "init_declarator"
6034                | "pointer_declarator"
6035                | "reference_declarator"
6036                | "array_declarator"
6037                | "function_declarator"
6038                | "parenthesized_declarator"
6039                | "attributed_declarator"
6040        ) || matches!(
6041            child.kind(),
6042            "identifier" | "field_identifier" | "qualified_identifier"
6043        ) && class_like.is_none_or(|class_node| {
6044            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
6045        })
6046    })
6047}
6048
6049/// One namespace-scope forward class declaration that a recovered export-macro
6050/// class definition may borrow its identity from.  Tree-sitter can close a
6051/// malformed class at the enclosing namespace's closing brace, leaving the later
6052/// class definitions as root-level recovered `function_definition` nodes.  A
6053/// preceding `class Name;` in the same namespace is the only structured identity
6054/// signal available in that shape.
6055///
6056/// Everything recorded here is a property of the forward declaration alone.
6057/// What depends on the node doing the asking -- that the forward and its
6058/// namespace both close before it, with nothing but recovery trivia between --
6059/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
6060/// tree answers every later question about it.
6061struct CppNamespaceForward {
6062    name: String,
6063    start_byte: usize,
6064    /// Where the malformed namespace that held the forward ended.  No query
6065    /// asks anything else about that node.
6066    namespace_end_byte: usize,
6067    package_name: String,
6068}
6069
6070/// Read `node` as a borrowable namespace forward declaration.
6071///
6072/// The admission is deliberately conservative: only a body-less class specifier
6073/// whose declaration has no declarator, at namespace scope rather than inside a
6074/// function or class body, in a namespace that itself failed to parse.
6075fn cpp_namespace_forward_entry<'tree>(
6076    node: Node<'tree>,
6077    source: &str,
6078    ancestry: &ParentIndex<'tree>,
6079) -> Option<CppNamespaceForward> {
6080    if !matches!(
6081        node.kind(),
6082        "class_specifier" | "struct_specifier" | "union_specifier"
6083    ) || cpp_body_node(node).is_some()
6084    {
6085        return None;
6086    }
6087    let parent = node.parent()?;
6088    if !(parent.kind() == "declaration_list"
6089        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
6090    {
6091        return None;
6092    }
6093    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
6094    // Borrowing is only justified by the parser-recovery shape we are
6095    // repairing: the namespace that held the forward must itself contain a
6096    // syntax error. A clean, unrelated namespace forward is not an identity
6097    // proof.
6098    if !namespace.has_error() {
6099        return None;
6100    }
6101    Some(CppNamespaceForward {
6102        name: class_like_name(node, source, ancestry)?,
6103        start_byte: node.start_byte(),
6104        namespace_end_byte: namespace.end_byte(),
6105        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
6106    })
6107}
6108
6109/// Whether `forward` stands in the recovery relation to the node asking about
6110/// it: it and its malformed namespace both closed before the recovered class,
6111/// and nothing but recovery trivia separates the two.
6112fn cpp_namespace_forward_matches_recovery(
6113    forward: &CppNamespaceForward,
6114    recovered_node: Node<'_>,
6115) -> bool {
6116    forward.start_byte < recovered_node.start_byte()
6117        && forward.namespace_end_byte < recovered_node.start_byte()
6118        && malformed_namespace_is_nearest_recovery_region(
6119            forward.namespace_end_byte,
6120            recovered_node,
6121        )
6122}
6123
6124/// What one open [`CppVisitor::record_recovered_declarations`] has watched
6125/// happen to the declaration set.
6126///
6127/// The recovered set used to be a difference against a clone of the whole
6128/// declaration set, taken once per recovery: O(recoveries x declarations) on
6129/// exactly the error-recovered files that already walk slowest (#2787). The
6130/// walk knows which declarations it creates, so the capture collects them as
6131/// they are made and the difference is never needed.
6132///
6133/// `removed_pre_existing` is what makes that equal to the difference. A
6134/// deferred replacement removes the replaced declaration's children
6135/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
6136/// re-creates them. Creation alone cannot tell that apart from a first mint, so
6137/// a removal of a declaration this capture did not create records that it was
6138/// already there when the capture opened.
6139#[derive(Debug, Default)]
6140pub struct CppRecoveryCapture {
6141    /// Declarations created while this capture was open, in creation order.
6142    created: Vec<CodeUnit>,
6143    /// Membership for `created`.
6144    created_units: HashSet<CodeUnit>,
6145    /// Declarations that predate this capture and have been removed during it.
6146    removed_pre_existing: HashSet<CodeUnit>,
6147}
6148
6149/// Which owners the parse product already holds field declarations for, folded
6150/// in as the walk records them.
6151///
6152/// [`CppVisitor::has_enum_enumerator_units`] asks that question once per enum
6153/// and used to answer it by scanning every declaration accumulated so far:
6154/// O(enums x declarations) on exactly the generated headers that declare many
6155/// of both (#2786). The answer only grows by declaration, so the walk carries
6156/// it. A field's short name names its owner chain, `Owner.member`, so one field
6157/// answers for every dotted prefix of its own short name; an anonymous enum's
6158/// or union's enumerators carry bare short names instead (#2140), which is what
6159/// an empty owner short name asks about.
6160#[derive(Debug, Default)]
6161pub struct CppFieldOwnerIndex {
6162    /// Package name -> the owner short names its fields name.
6163    owners: HashMap<String, HashSet<String>>,
6164    /// Packages holding at least one field that names no owner.
6165    ownerless_packages: HashSet<String>,
6166}
6167
6168impl CppFieldOwnerIndex {
6169    /// The index of the declarations recorded so far, built when the first
6170    /// question arrives.
6171    fn of<'unit>(
6172        declarations: impl IntoIterator<Item = &'unit CodeUnit>,
6173        file: &ProjectFile,
6174    ) -> Self {
6175        let mut index = Self::default();
6176        for declaration in declarations {
6177            index.record(declaration, file);
6178        }
6179        index
6180    }
6181
6182    fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
6183        if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
6184            return;
6185        }
6186        let short_name = code_unit.short_name();
6187        let package_name = code_unit.package_name();
6188        if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
6189            self.ownerless_packages.insert(package_name.to_string());
6190        }
6191        if !short_name.contains('.') {
6192            return;
6193        }
6194        if !self.owners.contains_key(package_name) {
6195            self.owners
6196                .insert(package_name.to_string(), HashSet::default());
6197        }
6198        let owners = self
6199            .owners
6200            .get_mut(package_name)
6201            .expect("the package entry was just ensured");
6202        for (offset, _) in short_name.match_indices('.') {
6203            let owner = &short_name[..offset];
6204            if !owners.contains(owner) {
6205                owners.insert(owner.to_string());
6206            }
6207        }
6208    }
6209
6210    /// Whether a field declaration in `package_name` names `owner_short_name`
6211    /// as its owner. An empty owner asks about ownerless fields instead.
6212    fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
6213        if owner_short_name.is_empty() {
6214            self.ownerless_packages.contains(package_name)
6215        } else {
6216            self.owners
6217                .get(package_name)
6218                .is_some_and(|owners| owners.contains(owner_short_name))
6219        }
6220    }
6221}
6222
6223/// The declaration scan [`CppFieldOwnerIndex`] replaces, kept as the oracle a
6224/// debug build asserts every carried answer against and as the release-mode
6225/// parity tests' reference (#2786).
6226#[cfg(any(debug_assertions, test))]
6227fn cpp_declarations_hold_owned_fields<'unit>(
6228    declarations: impl IntoIterator<Item = &'unit CodeUnit>,
6229    file: &ProjectFile,
6230    package_name: &str,
6231    owner_short_name: &str,
6232) -> bool {
6233    let prefix = format!("{owner_short_name}.");
6234    declarations.into_iter().any(|unit| {
6235        unit.kind() == CodeUnitType::Field
6236            && unit.source() == file
6237            && unit.package_name() == package_name
6238            && if owner_short_name.is_empty() {
6239                // Anonymous enum/union parent: its enumerators carry bare
6240                // short names (#2140), so presence means any ownerless
6241                // field in this file.
6242                !unit.short_name().contains(['.', '$'])
6243            } else {
6244                unit.short_name().starts_with(&prefix)
6245            }
6246    })
6247}
6248
6249/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
6250///
6251/// A region reparse is its own tree and is dropped while the walk that made it
6252/// continues, so a later parse can be allocated at the same address and hand out
6253/// the same node ids.  The root's span and shape pin the identity its address
6254/// alone does not: two roots agreeing on all of this are the same parse of the
6255/// same bytes, and a scan of one is a scan of the other.
6256#[derive(PartialEq, Eq, Hash)]
6257pub struct CppTreeIdentity {
6258    root_id: usize,
6259    start_byte: usize,
6260    end_byte: usize,
6261    kind_id: u16,
6262    child_count: usize,
6263}
6264
6265impl CppTreeIdentity {
6266    fn of(root: Node<'_>) -> Self {
6267        Self {
6268            root_id: root.id(),
6269            start_byte: root.start_byte(),
6270            end_byte: root.end_byte(),
6271            kind_id: root.kind_id(),
6272            child_count: root.child_count(),
6273        }
6274    }
6275}
6276
6277/// The namespace forward declarations one tree's prefix holds, folded in as the
6278/// walk asks about them.
6279///
6280/// `scope_for_recovered_exported_class` asks the same question once per
6281/// recovered class, and the answer depends only on the part of the tree that
6282/// starts before the asking node.  Rescanning that prefix per question is
6283/// quadratic in the file, and a generated header whose parse recovery leaves
6284/// thousands of class-like declarations at file scope pays all of it: 1,904
6285/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
6286/// (#2754).  This carries the scan forward instead.  Each question advances the
6287/// traversal from wherever the last one stopped to the asking node's start byte,
6288/// so a whole walk pays at most one pass over the prefix its furthest question
6289/// reaches, and each question then costs a name lookup.
6290#[derive(Default)]
6291pub struct CppNamespaceForwardScan {
6292    /// Every named node starting before this byte has been folded in.
6293    scanned_through: usize,
6294    forwards: HashMap<String, Vec<CppNamespaceForward>>,
6295}
6296
6297impl CppNamespaceForwardScan {
6298    /// Fold in every named node of `root` that starts at or after the fold
6299    /// watermark and before `cutoff`.
6300    ///
6301    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
6302    /// pass owes are exactly the ones no earlier pass reached, and a question
6303    /// about an earlier byte than one already answered costs nothing.
6304    fn advance_to<'tree>(
6305        &mut self,
6306        root: Node<'tree>,
6307        cutoff: usize,
6308        source: &str,
6309        ancestry: &ParentIndex<'tree>,
6310    ) {
6311        if cutoff <= self.scanned_through {
6312            return;
6313        }
6314        let folded_through = self.scanned_through;
6315        let mut cursor = root.walk();
6316        let mut stack = vec![root];
6317        while let Some(current) = stack.pop() {
6318            if (folded_through..cutoff).contains(&current.start_byte())
6319                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
6320            {
6321                self.forwards
6322                    .entry(forward.name.clone())
6323                    .or_default()
6324                    .push(forward);
6325            }
6326            // A subtree ending before the watermark holds only nodes an earlier
6327            // pass already folded, and one starting at or after the cutoff is
6328            // outside the prefix being asked about. Skipping both is what keeps
6329            // the total traversal to one pass.
6330            for child in current.named_children(&mut cursor) {
6331                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
6332                    stack.push(child);
6333                }
6334            }
6335        }
6336        self.scanned_through = cutoff;
6337    }
6338
6339    /// The one namespace `name` was forward declared in before `recovered_node`.
6340    /// More than one matching forward declaration is ambiguous and answers
6341    /// nothing rather than guessing.
6342    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
6343        let mut matching = self
6344            .forwards
6345            .get(name)
6346            .into_iter()
6347            .flatten()
6348            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
6349        let first = matching.next()?;
6350        matching
6351            .next()
6352            .is_none()
6353            .then(|| first.package_name.clone())
6354    }
6355}
6356
6357/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
6358/// debug build checks every answer against (and the one the parity tests drive
6359/// directly).  It walks the whole prefix per question, which is exactly the cost
6360/// #2754 removed from the release path.
6361#[cfg(any(debug_assertions, test))]
6362fn unique_earlier_cpp_namespace_forward<'tree>(
6363    recovered_node: Node<'tree>,
6364    name: &str,
6365    source: &str,
6366    ancestry: &ParentIndex<'tree>,
6367) -> Option<String> {
6368    let mut root = recovered_node;
6369    while let Some(parent) = ancestry.parent(root) {
6370        root = parent;
6371    }
6372
6373    let mut candidates = Vec::new();
6374    let mut stack = vec![root];
6375    while let Some(current) = stack.pop() {
6376        if current.start_byte() < recovered_node.start_byte()
6377            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
6378            && forward.name == name
6379            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
6380        {
6381            candidates.push(forward.package_name);
6382        }
6383
6384        let mut cursor = current.walk();
6385        for child in current.named_children(&mut cursor) {
6386            if child.start_byte() < recovered_node.start_byte() {
6387                stack.push(child);
6388            }
6389        }
6390    }
6391
6392    if candidates.len() == 1 {
6393        candidates.pop()
6394    } else {
6395        None
6396    }
6397}
6398
6399fn malformed_namespace_is_nearest_recovery_region(
6400    namespace_end_byte: usize,
6401    recovered_node: Node<'_>,
6402) -> bool {
6403    let mut root = recovered_node;
6404    while let Some(parent) = root.parent() {
6405        root = parent;
6406    }
6407    let mut cursor = root.walk();
6408    root.named_children(&mut cursor)
6409        .filter(|sibling| {
6410            namespace_end_byte <= sibling.start_byte()
6411                && sibling.end_byte() <= recovered_node.start_byte()
6412        })
6413        .all(is_malformed_namespace_recovery_trivia)
6414}
6415
6416fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
6417    matches!(node.kind(), "ERROR" | "comment")
6418        || node.kind().starts_with("preproc_")
6419        || node.kind() == "expression_statement" && node.named_child_count() == 0
6420}
6421
6422/// Return the namespace path for a forward class only when the declaration is
6423/// at namespace scope.  A declaration nested in a function/class body may share
6424/// the same namespace ancestor but cannot identify a top-level class definition.
6425fn cpp_namespace_name_for_forward<'tree>(
6426    node: Node<'tree>,
6427    source: &str,
6428    ancestry: &ParentIndex<'tree>,
6429) -> Option<String> {
6430    cpp_namespace_definition_for_forward(node, ancestry)?;
6431    cpp_lexical_namespace_name(node, source, ancestry)
6432}
6433
6434fn cpp_namespace_definition_for_forward<'tree>(
6435    node: Node<'tree>,
6436    ancestry: &ParentIndex<'tree>,
6437) -> Option<Node<'tree>> {
6438    let declaration = ancestry.parent(node)?;
6439    let mut ancestor = ancestry.parent(declaration);
6440    while let Some(current) = ancestor {
6441        if matches!(
6442            current.kind(),
6443            "compound_statement"
6444                | "field_declaration_list"
6445                | "class_specifier"
6446                | "struct_specifier"
6447                | "union_specifier"
6448                | "function_definition"
6449                | "lambda_expression"
6450        ) {
6451            return None;
6452        }
6453        if current.kind() == "namespace_definition" {
6454            return Some(current);
6455        }
6456        ancestor = ancestry.parent(current);
6457    }
6458    None
6459}
6460
6461fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
6462    match node.kind() {
6463        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
6464        "parenthesized_declarator" => node
6465            .child_by_field_name("declarator")
6466            .or_else(|| last_named_child(node))
6467            .is_some_and(is_pointer_wrapper_declarator),
6468        "template_function" => node
6469            .child_by_field_name("name")
6470            .is_some_and(is_function_pointer_like_inner_declarator),
6471        _ => false,
6472    }
6473}
6474
6475fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
6476    match node.kind() {
6477        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
6478        "parenthesized_declarator" => node
6479            .child_by_field_name("declarator")
6480            .or_else(|| last_named_child(node))
6481            .is_some_and(is_pointer_wrapper_declarator),
6482        _ => false,
6483    }
6484}
6485
6486fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
6487    let cleaned = raw_name.trim_start_matches("template ").trim();
6488    // A leading `::` is the explicit-global marker, not an empty owner segment.
6489    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
6490    // erroneous macro envelope swallowing the first identifier of an
6491    // out-of-line `X::X` constructor, chromium #1573); without this strip the
6492    // split below yields owner_parts `[""]`, constructing a unit with an empty
6493    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
6494    let cleaned = cleaned.trim_start_matches("::");
6495    // Parser recovery can preserve two adjacent scope operators around a
6496    // missing component (for example `X::/**/::method` in compiler diagnostic
6497    // fixtures). Empty components are syntax-recovery artifacts, never C++
6498    // owners. Keeping one as the final owner constructed `short_name=".method"`
6499    // and violated the structured package/short boundary during a large LLVM
6500    // workspace build. This is the same legacy-string-to-FqName bridge as the
6501    // ordinary split above; discard only components that the delimiter itself
6502    // proves empty.
6503    let parts: Vec<_> = cleaned
6504        .split("::")
6505        .filter(|component| !component.is_empty())
6506        .collect();
6507    if parts.is_empty() {
6508        return (None, cleaned.to_string(), scope.package_name.clone());
6509    }
6510    if parts.len() > 1 {
6511        let name = parts.last().unwrap_or(&cleaned).to_string();
6512        let owner_parts = &parts[..parts.len() - 1];
6513        if let Some(class_unit) = &scope.class_unit {
6514            // Lexically inside a class body: the owner is that class, whatever
6515            // the declarator re-qualifies it as.
6516            return (
6517                Some(CppMemberOwner::Unit(class_unit.clone())),
6518                name,
6519                scope.package_name.clone(),
6520            );
6521        }
6522        if !scope.package_name.is_empty() {
6523            // Out-of-line member definition written *inside* an enclosing
6524            // `namespace {}` block (scope package is that namespace). Every
6525            // owner segment before the terminal member is a class-nesting step
6526            // -- an out-of-line nested-class member `Outer::Inner::method` in
6527            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
6528            // namespace path: `using namespace` never brings nested-class
6529            // access into unqualified scope, so C++ always writes the full
6530            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
6531            // that redundantly re-states the enclosing namespace it already
6532            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
6533            // strip that re-qualifying prefix (which duplicates a suffix of the
6534            // enclosing package path) before treating what remains as the
6535            // nested-class chain, so the redundant spelling still lands on the
6536            // same `log4cxx.Foo.method` identity as its header declaration.
6537            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
6538            let owner = (!nested.is_empty()).then(|| {
6539                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
6540            });
6541            return (owner, name, scope.package_name.clone());
6542        }
6543        // File scope (no enclosing `namespace {}` block, scope package empty).
6544        let (owner, package_name) = if owner_parts.len() > 1 {
6545            // A multi-segment qualifier at file scope with no enclosing
6546            // namespace: treat all but the last owner segment as the namespace
6547            // path and the last as the owning class (`ns1::ns2::Class::method`
6548            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
6549            // is really a namespace or an outer class cannot be told from the
6550            // declarator text alone here, and no enclosing namespace or
6551            // in-index owner is available at per-file extraction to confirm the
6552            // class reading, so the far-more-common namespace interpretation is
6553            // kept rather than guessed away (the nested-class-at-file-scope and
6554            // using-directive-qualified nested-class shapes remain on this
6555            // behavior; see #1121).
6556            (
6557                Some(CppMemberOwner::Chain(vec![
6558                    owner_parts.last().unwrap_or(&"").to_string(),
6559                ])),
6560                owner_parts[..owner_parts.len() - 1].join("::"),
6561            )
6562        } else {
6563            // A bare `Class::member` qualifier at file scope carries no
6564            // namespace segment of its own. The declarator alone cannot say
6565            // which namespace owns `Class` -- but a `using namespace X;`
6566            // directive already in effect at this point in the file (#1093,
6567            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
6568            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
6569            // is the remaining structural signal for it, so fall back to it
6570            // rather than leaving the definition's package empty while its
6571            // header declaration (parsed inside the `namespace {}` block) keeps
6572            // the real one -- an identity split that made the same member
6573            // unresolvable under its own displayed spelling.
6574            (
6575                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
6576                cpp_using_directive_namespace_for_bare_owner(scope),
6577            )
6578        };
6579        return (owner, name, package_name);
6580    }
6581
6582    let package_name = scope.package_name.clone();
6583    let owner = scope
6584        .class_unit
6585        .as_ref()
6586        .map(|parent| CppMemberOwner::Unit(parent.clone()));
6587    (owner, cleaned.to_string(), package_name)
6588}
6589
6590/// Drop the leading owner segments of an out-of-line member qualifier that
6591/// merely re-state the enclosing namespace the definition already sits in, so
6592/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
6593/// definition may redundantly write `a::b::Outer::Inner::method` (or the
6594/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
6595/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
6596/// noise, not class-nesting steps. Returns the owner segments with the longest
6597/// such re-qualifying prefix removed (possibly all of them, when the qualifier
6598/// names only the enclosing namespace before the terminal member -- a
6599/// re-qualified free function). `package_name` is the enclosing namespace path
6600/// in its stored `::`-joined form; both sides are split on the same delimiter
6601/// the namespace walker joined them with, so this compares namespace *segments*
6602/// rather than scanning text.
6603fn strip_redundant_namespace_prefix<'a>(
6604    owner_parts: &'a [&'a str],
6605    package_name: &str,
6606) -> &'a [&'a str] {
6607    if package_name.is_empty() {
6608        return owner_parts;
6609    }
6610    let package_segments: Vec<&str> = package_name.split("::").collect();
6611    let max_prefix = owner_parts.len().min(package_segments.len());
6612    for prefix_len in (1..=max_prefix).rev() {
6613        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
6614        if &owner_parts[..prefix_len] == package_suffix {
6615            return &owner_parts[prefix_len..];
6616        }
6617    }
6618    owner_parts
6619}
6620
6621/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
6622/// class name at file/namespace scope, from the `using namespace` directives
6623/// visible at this point in the file. Several may be in scope at once (a
6624/// primary `using namespace NS;` alongside deeper conveniences like `using
6625/// namespace NS::helpers;`); since the declarator gives no way to tell which
6626/// one actually declares the owner class, prefer the shallowest (fewest
6627/// `::`-separated segments) as the file's most likely "home" namespace,
6628/// breaking ties by declaration order. Returns an empty string (leaving the
6629/// caller's package unqualified, as before) when no using-namespace directive
6630/// is in scope.
6631fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
6632    scope
6633        .visible_using_namespaces
6634        .iter()
6635        .min_by_key(|namespace| namespace.split("::").count())
6636        .cloned()
6637        .unwrap_or_default()
6638}
6639
6640struct CppQualifiedNameComponent {
6641    name: String,
6642    is_template_id: bool,
6643}
6644
6645/// Canonical nested-class chain for an out-of-line class definition written
6646/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
6647/// component per class (`["Outer", "Inner"]`).
6648///
6649/// The enclosing namespace fixes the namespace/class boundary: after an
6650/// optional redundant spelling of that namespace, every component belongs to
6651/// the class chain. File-scope qualified class names remain untouched because
6652/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
6653///
6654/// The components stay structured (rather than being `$`-joined here) so the
6655/// fq construction can push one Type/Nested segment per class; the `$`-joined
6656/// short-name display form is derived at the call sites that need it.
6657fn qualified_class_name_chain(
6658    class_node: Node<'_>,
6659    source: &str,
6660    scope: &ScopeInfo,
6661) -> Option<Vec<String>> {
6662    if scope.package_name.is_empty() || scope.class_unit.is_some() {
6663        return None;
6664    }
6665    let name = class_node.child_by_field_name("name")?;
6666    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
6667    if explicitly_global
6668        || components.len() < 2
6669        || components.iter().any(|component| component.is_template_id)
6670    {
6671        return None;
6672    }
6673    let names = components
6674        .iter()
6675        .map(|component| component.name.as_str())
6676        .collect::<Vec<_>>();
6677    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
6678    if class_chain.is_empty() {
6679        return None;
6680    }
6681    Some(class_chain.iter().map(|name| name.to_string()).collect())
6682}
6683
6684fn structured_cpp_qualified_components(
6685    qualified_name: Node<'_>,
6686    source: &str,
6687) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
6688    if qualified_name.kind() != "qualified_identifier" {
6689        return None;
6690    }
6691
6692    let mut components = Vec::new();
6693    let mut current = qualified_name;
6694    let mut explicitly_global = false;
6695    loop {
6696        if current.kind() == "qualified_identifier" {
6697            if let Some(component) = current.child_by_field_name("scope") {
6698                components.push(canonical_cpp_qualified_component(component, source)?);
6699            } else if components.is_empty() {
6700                explicitly_global = true;
6701            } else {
6702                return None;
6703            }
6704            current = current.child_by_field_name("name")?;
6705        } else {
6706            components.push(canonical_cpp_qualified_component(current, source)?);
6707            break;
6708        }
6709    }
6710    Some((components, explicitly_global))
6711}
6712
6713fn split_structured_templated_cpp_name(
6714    declarator_name: Node<'_>,
6715    source: &str,
6716    scope: &ScopeInfo,
6717) -> Option<(Option<CppMemberOwner>, String, String)> {
6718    let (mut components, explicitly_global) =
6719        structured_cpp_qualified_components(declarator_name, source)?;
6720
6721    let terminal = components.pop()?;
6722    let owner_start = components
6723        .iter()
6724        .position(|component| component.is_template_id)?;
6725    let explicit_package = components[..owner_start]
6726        .iter()
6727        .map(|component| component.name.as_str())
6728        .collect::<Vec<_>>()
6729        .join("::");
6730    let explicit_package_is_empty = explicit_package.is_empty();
6731    let package_name = match (
6732        explicitly_global,
6733        scope.package_name.is_empty(),
6734        explicit_package_is_empty,
6735    ) {
6736        (true, _, _) => explicit_package,
6737        (false, _, true) => scope.package_name.clone(),
6738        (false, true, false) => explicit_package,
6739        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
6740    };
6741    // Same identity-split fallback as `split_cpp_name` (#1093): a template
6742    // specialization's owner class named with no namespace segment of its own
6743    // (`explicit_package` empty) at file scope (`explicitly_global` false)
6744    // with nothing enclosing (`package_name` still empty) has no structural
6745    // signal for its namespace besides an in-scope `using namespace X;`.
6746    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
6747    {
6748        cpp_using_directive_namespace_for_bare_owner(scope)
6749    } else {
6750        package_name
6751    };
6752    let owner_chain = components[owner_start..]
6753        .iter()
6754        .map(|component| component.name.clone())
6755        .collect::<Vec<_>>();
6756    if owner_chain.is_empty() || terminal.name.is_empty() {
6757        return None;
6758    }
6759
6760    Some((
6761        Some(CppMemberOwner::Chain(owner_chain)),
6762        terminal.name,
6763        package_name,
6764    ))
6765}
6766
6767fn canonical_cpp_qualified_component(
6768    mut component: Node<'_>,
6769    source: &str,
6770) -> Option<CppQualifiedNameComponent> {
6771    let mut is_template_id = false;
6772    loop {
6773        match component.kind() {
6774            "template_type" => {
6775                is_template_id = true;
6776                component = component.child_by_field_name("name")?;
6777            }
6778            "dependent_name" => component = component.named_child(0)?,
6779            "identifier"
6780            | "field_identifier"
6781            | "namespace_identifier"
6782            | "type_identifier"
6783            | "operator_name"
6784            | "destructor_name" => {
6785                let name = normalize_cpp_whitespace(node_text(component, source));
6786                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
6787                    name,
6788                    is_template_id,
6789                });
6790            }
6791            _ => component = component.child_by_field_name("name")?,
6792        }
6793    }
6794}
6795
6796fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
6797    match node.kind() {
6798        "identifier"
6799        | "field_identifier"
6800        | "type_identifier"
6801        | "operator_name"
6802        | "destructor_name"
6803        | "qualified_identifier" => node_text(node, source).to_string(),
6804        "function_declarator"
6805        | "pointer_declarator"
6806        | "reference_declarator"
6807        | "parenthesized_declarator"
6808        | "array_declarator"
6809        | "template_function" => node
6810            .child_by_field_name("declarator")
6811            .or_else(|| node.child_by_field_name("name"))
6812            .or_else(|| last_named_child(node))
6813            .map(|child| extract_declarator_name(child, source))
6814            .unwrap_or_else(|| node_text(node, source).to_string()),
6815        _ => node
6816            .child_by_field_name("name")
6817            .map(|child| extract_declarator_name(child, source))
6818            .unwrap_or_else(|| node_text(node, source).to_string()),
6819    }
6820}
6821
6822/// Extract a callable identity only through declaration-shaped AST nodes.
6823/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
6824/// expose the call's parameter list as a false function declarator; accepting
6825/// arbitrary node text there emitted bogus names such as `.*f`.
6826fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
6827    match node.kind() {
6828        "identifier"
6829        | "field_identifier"
6830        | "type_identifier"
6831        | "operator_name"
6832        | "destructor_name"
6833        | "qualified_identifier" => Some(node_text(node, source).to_string()),
6834        "function_declarator"
6835        | "pointer_declarator"
6836        | "reference_declarator"
6837        | "parenthesized_declarator"
6838        | "array_declarator"
6839        | "template_function" => node
6840            .child_by_field_name("declarator")
6841            .or_else(|| node.child_by_field_name("name"))
6842            .and_then(|child| extract_callable_declarator_name(child, source)),
6843        _ => None,
6844    }
6845}
6846
6847fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
6848    match node.kind() {
6849        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
6850            let name = node_text(node, source).trim().to_string();
6851            (!name.is_empty()).then_some(name)
6852        }
6853        _ => node
6854            .child_by_field_name("declarator")
6855            .or_else(|| node.child_by_field_name("name"))
6856            .or_else(|| last_named_child(node))
6857            .and_then(|child| extract_variable_name(child, source)),
6858    }
6859}
6860
6861fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
6862    let count = node.named_child_count();
6863    if count == 0 {
6864        None
6865    } else {
6866        node.named_child(count - 1)
6867    }
6868}
6869
6870fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
6871    let name_node = node.child_by_field_name("name")?;
6872    let name = normalize_cpp_whitespace(node_text(name_node, source));
6873    (!name.is_empty()).then_some(name)
6874}
6875
6876fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
6877    if node.kind() != "declaration" {
6878        return Vec::new();
6879    }
6880    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
6881        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
6882    }) else {
6883        return Vec::new();
6884    };
6885    let Some(declarator) = node.child_by_field_name("declarator") else {
6886        return Vec::new();
6887    };
6888    if node_text(keyword, source) == "using"
6889        && (declarator.kind() != "init_declarator"
6890            || declarator.child_by_field_name("value").is_none())
6891    {
6892        return Vec::new();
6893    }
6894    if node_text(keyword, source) == "typedef"
6895        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
6896    {
6897        return vec![alias_name];
6898    }
6899    extract_typedef_declarator_name(declarator, source)
6900        .into_iter()
6901        .collect()
6902}
6903
6904fn recovered_typedef_error_alias_name(
6905    declaration: Node<'_>,
6906    declarator: Node<'_>,
6907    source: &str,
6908) -> Option<String> {
6909    // An export macro between `class` and its name can make tree-sitter parse
6910    // the recovered class body as a function body. In that shape,
6911    //
6912    //     typedef spi::Filter BASE_CLASS;
6913    //
6914    // becomes a declaration whose `declarator` is the underlying qualified
6915    // type (`spi::Filter`) and whose actual alias name is displaced into the
6916    // following ERROR node. Do not publish the terminal underlying type
6917    // (`Filter`) as a false class-owned alias.
6918    if declarator.kind() != "qualified_identifier" {
6919        return None;
6920    }
6921    let mut cursor = declaration.walk();
6922    let mut errors = declaration
6923        .named_children(&mut cursor)
6924        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
6925    let error = errors.next()?;
6926    if errors.next().is_some() || error.named_child_count() != 1 {
6927        return None;
6928    }
6929    let name = error.named_child(0)?;
6930    if !matches!(
6931        name.kind(),
6932        "identifier" | "field_identifier" | "type_identifier"
6933    ) {
6934        return None;
6935    }
6936    let name = normalize_cpp_whitespace(node_text(name, source));
6937    (!name.is_empty()).then_some(name)
6938}
6939
6940fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
6941    // A function-like token in the type position can make tree-sitter expose
6942    // its argument as a parenthesized declarator. Do not publish that argument
6943    // as an alias. The macro-specific recovery below handles the proven shape.
6944    if fragmented_parenthesized_typedef_type(node).is_some() {
6945        return Vec::new();
6946    }
6947    let has_function_like_macro_type = node
6948        .child_by_field_name("type")
6949        .filter(|type_node| type_node.kind() == "type_identifier")
6950        .is_some_and(|type_node| {
6951            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
6952        });
6953    let mut names = Vec::new();
6954    let mut cursor = node.walk();
6955    for declarator in node.children_by_field_name("declarator", &mut cursor) {
6956        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
6957            continue;
6958        }
6959        if let Some(name) = extract_typedef_declarator_name(declarator, source)
6960            && !names.contains(&name)
6961        {
6962            names.push(name);
6963        }
6964    }
6965    names
6966}
6967
6968struct RecoveredMacroTypedefAlias<'tree> {
6969    name: String,
6970    end_node: Node<'tree>,
6971}
6972
6973/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
6974/// into an identifier expression statement. The uppercase macro token, missing
6975/// typedef terminator, and complete sibling terminator prove this exact shape.
6976fn recovered_macro_typedef_alias<'tree>(
6977    node: Node<'tree>,
6978    source: &str,
6979) -> Option<RecoveredMacroTypedefAlias<'tree>> {
6980    let type_node = fragmented_parenthesized_typedef_type(node)?;
6981    if type_node.kind() != "type_identifier"
6982        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
6983    {
6984        return None;
6985    }
6986
6987    let end_node = node.next_named_sibling()?;
6988    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
6989        return None;
6990    }
6991    let name_node = end_node.named_child(0)?;
6992    if name_node.kind() != "identifier" {
6993        return None;
6994    }
6995    let has_terminator = (0..end_node.child_count()).any(|index| {
6996        end_node
6997            .child(index)
6998            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
6999    });
7000    if !has_terminator {
7001        return None;
7002    }
7003    let name = normalize_cpp_whitespace(node_text(name_node, source));
7004    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
7005}
7006
7007fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
7008    if node.kind() != "type_definition" {
7009        return None;
7010    }
7011    let mut declarator_cursor = node.walk();
7012    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
7013    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
7014        return None;
7015    }
7016    let has_missing_terminator = (0..node.child_count()).any(|index| {
7017        node.child(index)
7018            .is_some_and(|child| child.kind() == ";" && child.is_missing())
7019    });
7020    if !has_missing_terminator {
7021        return None;
7022    }
7023    node.child_by_field_name("type")
7024}
7025
7026fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
7027    match node.kind() {
7028        "identifier" | "field_identifier" | "type_identifier" => {
7029            let name = normalize_cpp_whitespace(node_text(node, source));
7030            (!name.is_empty()).then_some(name)
7031        }
7032        "qualified_identifier" => node
7033            .child_by_field_name("name")
7034            .and_then(|name| extract_typedef_declarator_name(name, source)),
7035        _ => node
7036            .child_by_field_name("declarator")
7037            .or_else(|| node.child_by_field_name("name"))
7038            .or_else(|| last_named_child(node))
7039            .and_then(|child| extract_typedef_declarator_name(child, source)),
7040    }
7041}
7042
7043fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
7044    let name = node
7045        .child_by_field_name("name")
7046        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
7047        .or_else(|| {
7048            let mut cursor = node.walk();
7049            node.named_children(&mut cursor)
7050                .find(|child| {
7051                    matches!(
7052                        child.kind(),
7053                        "identifier" | "field_identifier" | "type_identifier"
7054                    )
7055                })
7056                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
7057        })?;
7058    (!name.is_empty()).then_some(name)
7059}
7060
7061fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
7062    left.id() == right.id()
7063}
7064
7065fn render_cpp_type_signature(
7066    node: Node<'_>,
7067    source: &str,
7068    template_signature: Option<&str>,
7069) -> String {
7070    let text = normalize_cpp_whitespace(node_text(node, source));
7071    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
7072    let rendered = if head.ends_with(';') {
7073        head.to_string()
7074    } else {
7075        format!("{head} {{")
7076    };
7077    if let Some(template_signature) = template_signature {
7078        format!("template {template_signature} {rendered}")
7079    } else {
7080        rendered
7081    }
7082}
7083
7084fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
7085    if let Some(signature) =
7086        render_recovered_macro_qualified_field_signature(node, declarator, source)
7087    {
7088        return signature;
7089    }
7090    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
7091    let prefix = cpp_declaration_prefix(node, source);
7092    let name = extract_variable_name(declarator, source).unwrap_or_default();
7093    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
7094    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
7095        || (prefix.ends_with('&') && raw_suffix == "&")
7096    {
7097        String::new()
7098    } else {
7099        raw_suffix
7100    };
7101
7102    let mut rendered = if suffix.is_empty() {
7103        format!("{prefix} {name}")
7104    } else if suffix.starts_with('*') || suffix.starts_with('&') {
7105        format!("{prefix}{suffix} {name}")
7106    } else if suffix.starts_with('[') || suffix.starts_with('(') {
7107        format!("{prefix} {name}{suffix}")
7108    } else {
7109        format!("{prefix} {suffix}{name}")
7110    };
7111    rendered = collapse_cpp_whitespace(&rendered);
7112
7113    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
7114        format!("{rendered} = {initializer};")
7115    } else if declaration_text.ends_with(';') {
7116        format!("{rendered};")
7117    } else {
7118        rendered
7119    }
7120}
7121
7122fn render_recovered_macro_qualified_field_signature(
7123    node: Node<'_>,
7124    declarator: Node<'_>,
7125    source: &str,
7126) -> Option<String> {
7127    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
7128    if !recovered
7129        .iter()
7130        .any(|candidate| same_node(*candidate, declarator))
7131    {
7132        return None;
7133    }
7134    let pseudo_declarator = node.child_by_field_name("declarator")?;
7135    let mut cursor = node.walk();
7136    let clause = node
7137        .named_children(&mut cursor)
7138        .find(|child| child.kind() == "bitfield_clause")?;
7139    let mut cursor = clause.walk();
7140    let error = clause
7141        .named_children(&mut cursor)
7142        .find(|child| child.kind() == "ERROR")?;
7143    let qualified_type =
7144        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
7145    let prefix = cpp_declaration_prefix(node, source);
7146    let name = extract_variable_name(declarator, source)?;
7147    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
7148    let mut rendered = if suffix.is_empty() {
7149        format!("{prefix} {qualified_type} {name}")
7150    } else {
7151        format!("{prefix} {qualified_type} {suffix} {name}")
7152    };
7153    rendered = collapse_cpp_whitespace(&rendered);
7154
7155    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
7156        Some(format!(
7157            "{rendered} = {};",
7158            normalize_cpp_whitespace(node_text(initializer, source))
7159        ))
7160    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
7161        Some(format!("{rendered} = {initializer};"))
7162    } else {
7163        Some(format!("{rendered};"))
7164    }
7165}
7166
7167fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
7168    match node.kind() {
7169        "pointer_expression" => {
7170            let operator = node
7171                .child_by_field_name("operator")
7172                .or_else(|| node.child(0))
7173                .map(|operator| node_text(operator, source))
7174                .unwrap_or("*");
7175            let argument = node
7176                .child_by_field_name("argument")
7177                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
7178                .unwrap_or_default();
7179            format!("{operator}{argument}")
7180        }
7181        "unary_expression" => {
7182            let operator = node
7183                .child_by_field_name("operator")
7184                .or_else(|| node.child(0))
7185                .map(|operator| node_text(operator, source))
7186                .unwrap_or_default();
7187            let argument = node
7188                .child_by_field_name("argument")
7189                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
7190                .unwrap_or_default();
7191            format!("{operator}{argument}")
7192        }
7193        "identifier" | "field_identifier" => String::new(),
7194        _ => cpp_declarator_suffix_without_name(node, source),
7195    }
7196}
7197
7198fn recovered_macro_qualified_field_initializer<'tree>(
7199    clause: Node<'tree>,
7200    declarator: Node<'tree>,
7201) -> Option<Node<'tree>> {
7202    let mut stack = vec![clause];
7203    while let Some(current) = stack.pop() {
7204        if current.kind() == "assignment_expression"
7205            && current
7206                .child_by_field_name("left")
7207                .is_some_and(|left| same_node(left, declarator))
7208        {
7209            return current.child_by_field_name("right");
7210        }
7211        let mut cursor = current.walk();
7212        stack.extend(current.named_children(&mut cursor));
7213    }
7214    None
7215}
7216
7217fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
7218    let text = node_text(node, source);
7219    let mut cursor = node.walk();
7220    let first_declarator = node.named_children(&mut cursor).find(|child| {
7221        matches!(
7222            child.kind(),
7223            "init_declarator"
7224                | "identifier"
7225                | "field_identifier"
7226                | "pointer_declarator"
7227                | "reference_declarator"
7228                | "array_declarator"
7229                | "function_declarator"
7230        )
7231    });
7232    let prefix = if let Some(first_declarator) = first_declarator {
7233        let end = first_declarator
7234            .start_byte()
7235            .saturating_sub(node.start_byte());
7236        let mut prefix = text.get(..end).unwrap_or(text).to_string();
7237        let declarator_suffix = match first_declarator.kind() {
7238            "init_declarator" => first_declarator
7239                .child_by_field_name("declarator")
7240                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
7241                .unwrap_or_default(),
7242            _ => cpp_declarator_suffix_without_name(first_declarator, source),
7243        };
7244        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
7245            prefix.push_str(&declarator_suffix);
7246        }
7247        return collapse_cpp_whitespace(&prefix)
7248            .trim_end_matches(',')
7249            .trim_end_matches(';')
7250            .trim()
7251            .to_string();
7252    } else {
7253        text
7254    };
7255    collapse_cpp_whitespace(prefix)
7256        .trim_end_matches(',')
7257        .trim_end_matches(';')
7258        .trim()
7259        .to_string()
7260}
7261
7262fn cpp_preserved_initializer(
7263    declaration_node: Node<'_>,
7264    declarator: Node<'_>,
7265    source: &str,
7266) -> Option<String> {
7267    let name = extract_variable_name(declarator, source)?;
7268    let mut cursor = declaration_node.walk();
7269    for child in declaration_node.named_children(&mut cursor) {
7270        if child.kind() != "init_declarator" {
7271            continue;
7272        }
7273        let Some(inner) = child.child_by_field_name("declarator") else {
7274            continue;
7275        };
7276        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
7277            continue;
7278        }
7279        let value = child.child_by_field_name("value")?;
7280        let kind = value.kind();
7281        if matches!(
7282            kind,
7283            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
7284        ) {
7285            return Some(normalize_cpp_whitespace(node_text(value, source)));
7286        }
7287        break;
7288    }
7289    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
7290    let pattern = format!(
7291        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
7292        regex::escape(&name)
7293    );
7294    Regex::new(&pattern)
7295        .ok()
7296        .and_then(|regex| regex.captures(&declaration_text))
7297        .and_then(|captures| captures.get(1))
7298        .map(|value| value.as_str().to_string())
7299}
7300
7301fn render_cpp_function_display_signature_from_node<'tree>(
7302    node: Node<'tree>,
7303    source: &str,
7304    template_signature: Option<&str>,
7305    has_body: bool,
7306    ancestry: &ParentIndex<'tree>,
7307) -> String {
7308    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
7309    let parent_text = node_text(root, source);
7310    let body_local_start = root
7311        .child_by_field_name("body")
7312        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
7313        .unwrap_or(parent_text.len());
7314    let display = parent_text
7315        .get(..body_local_start)
7316        .unwrap_or(parent_text)
7317        .trim()
7318        .trim();
7319    let display = if let Some(template_signature) = template_signature {
7320        if display.starts_with("template ") {
7321            display.to_string()
7322        } else {
7323            format!("template {template_signature} {display}")
7324        }
7325    } else {
7326        display.to_string()
7327    };
7328    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
7329    if has_body {
7330        format!("{display} {{...}}")
7331    } else {
7332        format!("{display};")
7333    }
7334}
7335
7336fn cpp_template_signature(
7337    template_node: Node<'_>,
7338    declaration_child: Node<'_>,
7339    source: &str,
7340) -> Option<String> {
7341    let text = source
7342        .get(template_node.start_byte()..declaration_child.start_byte())
7343        .unwrap_or("");
7344    let text = normalize_cpp_whitespace(text);
7345    let start = text.find('<')?;
7346    let end = text.rfind('>')?;
7347    if end < start {
7348        return None;
7349    }
7350    Some(text[start..=end].to_string())
7351}
7352
7353struct RecoveredFragmentedPartialSpecialization<'tree> {
7354    declaration_node: Node<'tree>,
7355    name: String,
7356    range: Range,
7357    prefix_members: Vec<Node<'tree>>,
7358    member_siblings: Vec<Node<'tree>>,
7359    following_declarations: Vec<Node<'tree>>,
7360}
7361
7362struct RecoveredFragmentedPreprocessorClass<'tree> {
7363    declaration_node: Node<'tree>,
7364    class_node: Node<'tree>,
7365    body: Node<'tree>,
7366    name: String,
7367    range: Range,
7368    tail_members: Vec<Node<'tree>>,
7369    member_siblings: Vec<Node<'tree>>,
7370}
7371
7372/// Recover a class whose preprocessor-fragmented parse closes at an early
7373/// member body and publishes the remaining in-class declarations as siblings
7374/// of the surrounding alternative. Primary classes are admitted only when an
7375/// earlier branch contains the matching bodyless declaration and the class
7376/// node retains the displaced `#endif`. Partial specializations instead carry
7377/// their identity structurally in the `template_type` name and template
7378/// metadata. Retain the original AST nodes and re-own only the siblings through
7379/// the displaced structural `};` terminator.
7380fn recover_fragmented_preprocessor_class<'tree>(
7381    template_node: Node<'tree>,
7382    source: &str,
7383    ancestry: &ParentIndex<'tree>,
7384) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
7385    let alternative = ancestry.parent(template_node)?;
7386    if alternative.kind() != "preproc_else" {
7387        return None;
7388    }
7389    let conditional = alternative.parent()?;
7390    if conditional.kind() != "preproc_if" {
7391        return None;
7392    }
7393    let declaration_node = template_node
7394        .named_children(&mut template_node.walk())
7395        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
7396    let class_node = declaration_node
7397        .named_children(&mut declaration_node.walk())
7398        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
7399    let body = cpp_body_node(class_node)?;
7400    if class_node.end_byte() >= declaration_node.end_byte() {
7401        return None;
7402    }
7403    let name = class_like_name(class_node, source, ancestry)?;
7404    let is_partial_specialization = class_node
7405        .child_by_field_name("name")
7406        .is_some_and(|class_name| class_name.kind() == "template_type");
7407    if is_partial_specialization {
7408        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
7409        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
7410            return None;
7411        }
7412    } else {
7413        if !class_has_displaced_preprocessor_terminator(class_node) {
7414            return None;
7415        }
7416        let matching_other_branch = conditional
7417            .named_children(&mut conditional.walk())
7418            .take_while(|child| !same_node(*child, alternative))
7419            .filter(|child| child.kind() == "template_declaration")
7420            .filter_map(first_class_like_child)
7421            .any(|candidate| {
7422                cpp_body_node(candidate).is_none()
7423                    && class_like_name(candidate, source, ancestry).as_deref()
7424                        == Some(name.as_str())
7425            });
7426        if !matching_other_branch {
7427            return None;
7428        }
7429    }
7430
7431    let mut tail_members = Vec::new();
7432    let mut saw_class = false;
7433    let mut declaration_cursor = declaration_node.walk();
7434    for child in declaration_node.named_children(&mut declaration_cursor) {
7435        if same_node(child, class_node) {
7436            saw_class = true;
7437        } else if saw_class {
7438            tail_members.push(child);
7439        }
7440    }
7441
7442    let mut member_siblings = Vec::new();
7443    let mut saw_template = false;
7444    let mut terminator = None;
7445    for index in 0..alternative.child_count() {
7446        let Some(child) = alternative.child(index) else {
7447            continue;
7448        };
7449        if same_node(child, template_node) {
7450            saw_template = true;
7451            continue;
7452        }
7453        if !saw_template {
7454            continue;
7455        }
7456        if displaced_fragmented_class_terminator(alternative, index) {
7457            terminator = alternative.child(index + 1);
7458            break;
7459        }
7460        if child.is_named() {
7461            member_siblings.push(child);
7462        }
7463    }
7464    let terminator = terminator?;
7465    Some(RecoveredFragmentedPreprocessorClass {
7466        declaration_node,
7467        class_node,
7468        body,
7469        name,
7470        range: Range {
7471            start_byte: class_node.start_byte(),
7472            end_byte: terminator.end_byte(),
7473            start_line: class_node.start_position().row + 1,
7474            end_line: terminator.end_position().row + 1,
7475        },
7476        tail_members,
7477        member_siblings,
7478    })
7479}
7480
7481fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
7482    (0..class_node.child_count()).any(|index| {
7483        class_node.child(index).is_some_and(|child| {
7484            child.kind() == "ERROR"
7485                && (0..child.child_count()).any(|error_index| {
7486                    child
7487                        .child(error_index)
7488                        .is_some_and(|token| token.kind() == "#endif")
7489                })
7490        })
7491    })
7492}
7493
7494/// The real `#endif` that tree-sitter consumed inside an error subtree.
7495///
7496/// A preprocessor directive inside a malformed array bound can cause later
7497/// declarations to remain children of the conditional. The non-missing token
7498/// still gives the exact structured boundary. Ignore nested conditionals and
7499/// select the last error-owned token. Tree-sitter can pair a later outer
7500/// `#endif` with this conditional, so the direct terminator is not necessarily
7501/// missing.
7502pub fn cpp_displaced_preprocessor_terminator<'tree>(
7503    conditional: Node<'tree>,
7504) -> Option<Node<'tree>> {
7505    if !conditional.has_error() {
7506        return None;
7507    }
7508    let has_concrete_direct_terminator = conditional
7509        .child_count()
7510        .checked_sub(1)
7511        .and_then(|index| conditional.child(index))
7512        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
7513    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
7514        // A structured alternative proves that the direct `#endif` closes
7515        // this family. An error-owned terminator inside either branch belongs
7516        // to a damaged nested conditional, not to this one.
7517        return None;
7518    }
7519    let mut displaced = None;
7520    let mut stack = (0..conditional.child_count())
7521        .filter_map(|index| conditional.child(index))
7522        .map(|child| (child, false))
7523        .collect::<Vec<_>>();
7524    while let Some((node, inside_error)) = stack.pop() {
7525        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
7526            continue;
7527        }
7528        if node.kind() == "#endif" && !node.is_missing() && inside_error {
7529            if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
7530                displaced = Some(node);
7531            }
7532            continue;
7533        }
7534        if node != conditional
7535            && matches!(
7536                node.kind(),
7537                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
7538            )
7539        {
7540            continue;
7541        }
7542        let inside_error = inside_error || node.kind() == "ERROR";
7543        for index in 0..node.child_count() {
7544            if let Some(child) = node.child(index) {
7545                stack.push((child, inside_error));
7546            }
7547        }
7548    }
7549    displaced
7550}
7551
7552/// The effective end of a conditional whose real terminator tree-sitter
7553/// displaced into declaration recovery.
7554///
7555/// Most damaged conditionals retain a concrete `#endif` token below an
7556/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
7557/// boundary. A preprocessor family that selects the middle of a declaration
7558/// can lose the directive tokens entirely. In that shape tree-sitter leaves
7559/// the declaration's `typedef` token as the sole child of the immediately
7560/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
7561/// declarator name inside the conditional's first declaration. The declaration
7562/// end is then the smallest structured boundary that contains the whole split
7563/// declaration.
7564#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7565pub struct CppDisplacedPreprocessorBoundary {
7566    pub end_byte: usize,
7567    pub end_line: usize,
7568}
7569
7570pub fn cpp_displaced_preprocessor_boundary(
7571    conditional: Node<'_>,
7572) -> Option<CppDisplacedPreprocessorBoundary> {
7573    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
7574        return Some(CppDisplacedPreprocessorBoundary {
7575            end_byte: terminator.end_byte(),
7576            end_line: terminator.end_position().row + 1,
7577        });
7578    }
7579    if let Some(declaration) = displaced_split_declaration(conditional) {
7580        return Some(CppDisplacedPreprocessorBoundary {
7581            end_byte: declaration.end_byte(),
7582            end_line: declaration.end_position().row + 1,
7583        });
7584    }
7585    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
7586        return Some(CppDisplacedPreprocessorBoundary {
7587            end_byte: terminator.end_byte(),
7588            end_line: terminator.end_position().row + 1,
7589        });
7590    }
7591    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
7592        return Some(CppDisplacedPreprocessorBoundary {
7593            end_byte: terminator.end_byte(),
7594            end_line: terminator.end_position().row + 1,
7595        });
7596    }
7597    None
7598}
7599
7600/// Recover an outer terminator that tree-sitter assigned to a damaged nested
7601/// conditional. This occurs when a split construct such as `extern "C"`
7602/// consumes the nested `#endif` inside an error node: the nested conditional's
7603/// direct terminator is then the outer conditional's real terminator, while
7604/// the outer node ends with a missing token and absorbs later declarations.
7605fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
7606    if !conditional.has_error()
7607        || conditional.child_by_field_name("alternative").is_some()
7608        || conditional
7609            .child(conditional.child_count().saturating_sub(1))
7610            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
7611    {
7612        return None;
7613    }
7614    let mut recovered = None;
7615    for index in 0..conditional.named_child_count() {
7616        let Some(nested) = conditional.named_child(index) else {
7617            continue;
7618        };
7619        if !matches!(
7620            nested.kind(),
7621            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
7622        ) || nested.child_by_field_name("alternative").is_some()
7623        {
7624            continue;
7625        }
7626        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
7627            continue;
7628        };
7629        if direct.kind() != "#endif" || direct.is_missing() {
7630            continue;
7631        }
7632        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
7633            continue;
7634        };
7635        if displaced.end_byte() >= direct.start_byte() {
7636            continue;
7637        }
7638        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
7639            recovered = Some(direct);
7640        }
7641    }
7642    recovered
7643}
7644
7645fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
7646    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
7647        return None;
7648    }
7649    let mut cursor = conditional.walk();
7650    let declarations = conditional
7651        .named_children(&mut cursor)
7652        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
7653        .collect::<Vec<_>>();
7654    let declaration = *declarations.first()?;
7655    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
7656        return None;
7657    }
7658    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
7659    let mut terminator = None;
7660    let mut stack = (0..declaration.child_count())
7661        .filter_map(|index| declaration.child(index))
7662        .filter(|child| child.start_byte() < declarator_start)
7663        .map(|child| (child, false))
7664        .collect::<Vec<_>>();
7665    while let Some((node, inside_error)) = stack.pop() {
7666        let inside_error = inside_error || node.kind() == "ERROR";
7667        if inside_error && node.kind() == "#endif" && !node.is_missing() {
7668            terminator = Some(node);
7669            continue;
7670        }
7671        for index in 0..node.child_count() {
7672            if let Some(child) = node.child(index)
7673                && child.start_byte() < declarator_start
7674            {
7675                stack.push((child, inside_error));
7676            }
7677        }
7678    }
7679    terminator
7680}
7681
7682fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
7683    if !conditional.has_error()
7684        || conditional.child_by_field_name("alternative").is_some()
7685        || conditional
7686            .prev_named_sibling()
7687            .filter(|sibling| {
7688                sibling.kind() == "ERROR"
7689                    && sibling.child_count() == 1
7690                    && sibling
7691                        .child(0)
7692                        .is_some_and(|child| child.kind() == "typedef")
7693            })
7694            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
7695            .is_none()
7696    {
7697        return None;
7698    }
7699    let mut cursor = conditional.walk();
7700    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
7701    let declaration_index = children
7702        .iter()
7703        .position(|child| child.kind() == "declaration" && child.has_error())?;
7704    let declaration = children[declaration_index];
7705    if !children
7706        .iter()
7707        .skip(declaration_index + 1)
7708        .any(|child| child.end_byte() > declaration.end_byte())
7709    {
7710        return None;
7711    }
7712    let declarator = declaration.child_by_field_name("declarator")?;
7713    let mut error_end = None;
7714    let mut names = Vec::new();
7715    let mut stack = vec![declarator];
7716    while let Some(node) = stack.pop() {
7717        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
7718            error_end =
7719                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
7720            continue;
7721        }
7722        if matches!(node.kind(), "identifier" | "type_identifier") {
7723            names.push(node.start_byte());
7724        }
7725        for index in (0..node.named_child_count()).rev() {
7726            if let Some(child) = node.named_child(index) {
7727                stack.push(child);
7728            }
7729        }
7730    }
7731    let error_end = error_end?;
7732    names
7733        .into_iter()
7734        .any(|start| start >= error_end)
7735        .then_some(declaration)
7736}
7737
7738fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
7739    let Some(error) = parent.child(error_index) else {
7740        return false;
7741    };
7742    if error.kind() != "ERROR"
7743        || error.child_count() != 1
7744        || error.child(0).is_none_or(|child| child.kind() != "}")
7745    {
7746        return false;
7747    }
7748    let Some(semicolon) = parent.child(error_index + 1) else {
7749        return false;
7750    };
7751    semicolon.kind() == "expression_statement"
7752        && semicolon.child_count() == 1
7753        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
7754}
7755
7756/// Locate the real end of a class-like declaration when a macro invocation
7757/// without a source semicolon absorbs the class's `};` into its parsed field.
7758/// The grammar then keeps following namespace declarations as later children
7759/// of the same field list. The direct ERROR-plus-semicolon pair proves the
7760/// boundary structurally; no source-text delimiter scan is needed.
7761fn displaced_macro_class_tail(
7762    declaration_node: Node<'_>,
7763    body: Node<'_>,
7764    source: &str,
7765) -> Option<DisplacedMacroClassTail> {
7766    if !matches!(
7767        declaration_node.kind(),
7768        "class_specifier" | "struct_specifier" | "union_specifier"
7769    ) || body.kind() != "field_declaration_list"
7770    {
7771        return None;
7772    }
7773
7774    let child_count = body.named_child_count();
7775    for index in 0..child_count {
7776        let child = body.named_child(index)?;
7777        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
7778            continue;
7779        };
7780        let split_index = index + 1;
7781        if split_index >= child_count {
7782            return None;
7783        }
7784        let mut cursor = body.walk();
7785        if !body
7786            .named_children(&mut cursor)
7787            .skip(split_index)
7788            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
7789        {
7790            return None;
7791        }
7792        return Some(DisplacedMacroClassTail {
7793            split_index,
7794            class_range: Range {
7795                start_byte: declaration_node.start_byte(),
7796                end_byte: terminator.end_byte(),
7797                start_line: declaration_node.start_position().row + 1,
7798                end_line: terminator.end_position().row + 1,
7799            },
7800        });
7801    }
7802    None
7803}
7804
7805fn displaced_macro_field_terminator<'tree>(
7806    field: Node<'tree>,
7807    source: &str,
7808) -> Option<Node<'tree>> {
7809    if field.kind() != "field_declaration" {
7810        return None;
7811    }
7812    let macro_type = field.child_by_field_name("type")?;
7813    if macro_type.kind() != "type_identifier"
7814        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
7815        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
7816    {
7817        return None;
7818    }
7819    for index in 0..field.child_count() {
7820        let error = field.child(index)?;
7821        if error.kind() != "ERROR"
7822            || error.child_count() != 1
7823            || error.child(0).is_none_or(|child| child.kind() != "}")
7824        {
7825            continue;
7826        }
7827        let semicolon = field.child(index + 1)?;
7828        if semicolon.kind() == ";" {
7829            return Some(semicolon);
7830        }
7831    }
7832    None
7833}
7834
7835fn recover_fragmented_partial_specialization<'tree>(
7836    template_node: Node<'tree>,
7837    declaration_child: Node<'tree>,
7838    source: &str,
7839    ancestry: &ParentIndex<'tree>,
7840) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
7841    if declaration_child.kind() != "function_definition" {
7842        return None;
7843    }
7844    let class_node = declaration_child.child_by_field_name("type")?;
7845    if !matches!(
7846        class_node.kind(),
7847        "class_specifier" | "struct_specifier" | "union_specifier"
7848    ) || !class_node
7849        .child_by_field_name("name")
7850        .and_then(|name| direct_identifier_name(name, source))
7851        .is_some_and(|name| cpp_export_macro_token(&name))
7852    {
7853        return None;
7854    }
7855    let declarator = declaration_child.child_by_field_name("declarator")?;
7856    if declarator.kind() != "template_function" {
7857        return None;
7858    }
7859    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
7860    if metadata.specialization_arguments.is_empty() {
7861        return None;
7862    }
7863    let body = declaration_child.child_by_field_name("body")?;
7864    if body.kind() != "compound_statement" {
7865        return None;
7866    }
7867    let complete_prefix = body.named_child(0).filter(|first| {
7868        first.kind() == "labeled_statement"
7869            && first.has_error()
7870            && first
7871                .named_child(first.named_child_count().saturating_sub(1))
7872                .is_some_and(recovered_declaration_has_class_terminator)
7873    });
7874    let complete_body = complete_prefix.is_some();
7875    let mut prefix_members = Vec::new();
7876    if let Some(prefix) = complete_prefix {
7877        prefix_members.push(prefix);
7878    } else {
7879        let mut body_cursor = body.walk();
7880        for child in body.named_children(&mut body_cursor) {
7881            if !is_structurally_valid_fragmented_class_prefix_member(child) {
7882                break;
7883            }
7884            prefix_members.push(child);
7885        }
7886    }
7887    let containing_declarations = template_node.parent()?;
7888    if !matches!(
7889        containing_declarations.kind(),
7890        "declaration_list" | "compound_statement"
7891    ) {
7892        return None;
7893    }
7894    let mut member_siblings = Vec::new();
7895    let mut following_declarations = Vec::new();
7896    let terminator;
7897    if complete_body {
7898        terminator = complete_prefix?;
7899        let mut cursor = body.walk();
7900        let mut after_prefix = false;
7901        for child in body.named_children(&mut cursor) {
7902            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
7903                after_prefix = true;
7904            } else if after_prefix {
7905                following_declarations.push(child);
7906            }
7907        }
7908    } else {
7909        let mut found_template = false;
7910        let mut cursor = containing_declarations.walk();
7911        let mut class_terminator = None;
7912        for child in containing_declarations.children(&mut cursor) {
7913            if same_node(child, template_node) {
7914                found_template = true;
7915                continue;
7916            }
7917            if found_template && child.kind() == "}" {
7918                class_terminator = Some(child);
7919                break;
7920            }
7921            // A namespace can never be a class member: reaching one before the
7922            // terminator proves the class's true close was swallowed upstream
7923            // and this scan has crossed into the enclosing scope, so the
7924            // recovery cannot be bounded -- continuing re-owns the namespace
7925            // block (and its template specializations) as class members under
7926            // a re-appended package, desyncing the fq boundary (#2306).
7927            if found_template && child.kind() == "namespace_definition" {
7928                return None;
7929            }
7930            if found_template && child.is_named() {
7931                member_siblings.push(child);
7932            }
7933        }
7934        terminator = class_terminator?;
7935    }
7936    let name = format!(
7937        "{}<{}>",
7938        metadata.primary_name,
7939        metadata
7940            .specialization_arguments
7941            .iter()
7942            .map(|argument| argument.text.as_str())
7943            .collect::<Vec<_>>()
7944            .join(", ")
7945    );
7946    Some(RecoveredFragmentedPartialSpecialization {
7947        declaration_node: declaration_child,
7948        name,
7949        range: Range {
7950            start_byte: declaration_child.start_byte(),
7951            end_byte: terminator.end_byte(),
7952            start_line: declaration_child.start_position().row + 1,
7953            end_line: terminator.end_position().row + 1,
7954        },
7955        prefix_members,
7956        member_siblings,
7957        following_declarations,
7958    })
7959}
7960
7961fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
7962    if declaration.kind() != "declaration" {
7963        return false;
7964    }
7965    // With an export macro between `class` and its name, tree-sitter folds a
7966    // complete class body into a function-shaped declaration. The class's own
7967    // `};` remains structurally identifiable as a direct ERROR child holding
7968    // `}`, immediately followed by the declaration's direct `;` child.
7969    (0..declaration.child_count().saturating_sub(1)).any(|index| {
7970        let Some(error) = declaration.child(index) else {
7971            return false;
7972        };
7973        error.kind() == "ERROR"
7974            && error.child_count() == 1
7975            && error.child(0).is_some_and(|child| child.kind() == "}")
7976            && declaration
7977                .child(index + 1)
7978                .is_some_and(|child| child.kind() == ";")
7979    })
7980}
7981
7982fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
7983    if node.has_error() {
7984        return false;
7985    }
7986    match node.kind() {
7987        "declaration"
7988        | "field_declaration"
7989        | "alias_declaration"
7990        | "type_definition"
7991        | "static_assert_declaration" => true,
7992        "labeled_statement" => node
7993            .named_child(node.named_child_count().saturating_sub(1))
7994            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
7995        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
7996            matches!(
7997                child.kind(),
7998                "declaration"
7999                    | "field_declaration"
8000                    | "alias_declaration"
8001                    | "type_definition"
8002                    | "function_definition"
8003            )
8004        }),
8005        _ => false,
8006    }
8007}
8008
8009fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
8010    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
8011        .then(|| node.child_by_field_name("declarator"))
8012        .flatten()
8013        .and_then(|declarator| extract_variable_name(declarator, source))
8014}
8015
8016fn cpp_template_metadata<'tree>(
8017    template_node: Node<'tree>,
8018    declaration_child: Node<'tree>,
8019    source: &str,
8020    ancestry: &ParentIndex<'tree>,
8021) -> Option<CppTemplateMetadata> {
8022    let parameters_node = template_node.child_by_field_name("parameters")?;
8023    let name_node = cpp_templated_class_name_node(declaration_child)?;
8024    let primary_node = match name_node.kind() {
8025        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
8026        _ => name_node,
8027    };
8028    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
8029    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
8030        return None;
8031    }
8032
8033    let mut parameter_nodes = Vec::new();
8034    let mut parameter_names = Vec::new();
8035    let mut cursor = parameters_node.walk();
8036    for parameter in parameters_node.named_children(&mut cursor) {
8037        if !matches!(
8038            parameter.kind(),
8039            "type_parameter_declaration"
8040                | "optional_type_parameter_declaration"
8041                | "variadic_type_parameter_declaration"
8042                | "template_template_parameter_declaration"
8043                | "parameter_declaration"
8044                | "optional_parameter_declaration"
8045                | "variadic_parameter_declaration"
8046        ) {
8047            continue;
8048        }
8049        let index = parameter_nodes.len();
8050        // An unnamed parameter still contributes template arity and kind. Use
8051        // an impossible C++ identifier so positional reconciliation can bind
8052        // it without making source expressions refer to a name that was not
8053        // written.
8054        let name = cpp_template_parameter_name(parameter, source)
8055            .unwrap_or_else(|| format!("<anonymous:{index}>"));
8056        parameter_names.push(name);
8057        parameter_nodes.push(parameter);
8058    }
8059    let parameters = parameter_nodes
8060        .into_iter()
8061        .zip(parameter_names.iter().cloned())
8062        .map(|(parameter, name)| CppTemplateParameterMetadata {
8063            name,
8064            kind: cpp_template_parameter_kind(parameter),
8065            variadic: matches!(
8066                parameter.kind(),
8067                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
8068            ),
8069            default: cpp_template_parameter_default_expression(
8070                parameter,
8071                source,
8072                &parameter_names,
8073                ancestry,
8074            ),
8075        })
8076        .collect();
8077    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
8078        Vec::new()
8079    } else {
8080        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
8081            .unwrap_or_default()
8082    };
8083    let alias_target = (declaration_child.kind() == "alias_declaration")
8084        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
8085        .flatten();
8086    Some(CppTemplateMetadata {
8087        primary_name,
8088        primary_fq_name: String::new(),
8089        parameters,
8090        specialization_arguments,
8091        alias_target,
8092    })
8093}
8094
8095fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
8096    match node.kind() {
8097        "class_specifier" | "struct_specifier" | "union_specifier" => {
8098            node.child_by_field_name("name")
8099        }
8100        "function_definition" => {
8101            let declarator = node.child_by_field_name("declarator")?;
8102            if matches!(declarator.kind(), "identifier" | "template_function") {
8103                Some(declarator)
8104            } else {
8105                None
8106            }
8107        }
8108        "alias_declaration" => node.child_by_field_name("name"),
8109        _ => None,
8110    }
8111}
8112
8113fn cpp_template_alias_target<'tree>(
8114    alias: Node<'tree>,
8115    source: &str,
8116    parameter_names: &[String],
8117    ancestry: &ParentIndex<'tree>,
8118) -> Option<CppTemplateAliasTargetMetadata> {
8119    let mut type_node = alias.child_by_field_name("type")?;
8120    while type_node.kind() == "type_descriptor" {
8121        type_node = type_node.child_by_field_name("type")?;
8122    }
8123    let global = type_node.child_by_field_name("scope").is_none()
8124        && type_node.child(0).is_some_and(|child| child.kind() == "::");
8125    let mut components = Vec::new();
8126    cpp_template_target_components(type_node, source, &mut components)?;
8127    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
8128    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
8129        components,
8130        global,
8131        arguments,
8132    })
8133}
8134
8135fn cpp_template_target_components(
8136    node: Node<'_>,
8137    source: &str,
8138    out: &mut Vec<String>,
8139) -> Option<()> {
8140    match node.kind() {
8141        "identifier" | "namespace_identifier" | "type_identifier" => {
8142            out.push(node_text(node, source).to_string());
8143            Some(())
8144        }
8145        "template_type" => {
8146            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
8147        }
8148        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
8149            if let Some(scope) = node.child_by_field_name("scope") {
8150                cpp_template_target_components(scope, source, out)?;
8151            }
8152            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
8153        }
8154        _ => None,
8155    }
8156}
8157
8158fn cpp_template_argument_expressions<'tree>(
8159    mut node: Node<'tree>,
8160    source: &str,
8161    parameter_names: &[String],
8162    ancestry: &ParentIndex<'tree>,
8163) -> Option<Vec<CppTemplateExpression>> {
8164    loop {
8165        match node.kind() {
8166            "template_type" | "template_function" => {
8167                let arguments = node.child_by_field_name("arguments")?;
8168                let mut cursor = arguments.walk();
8169                return Some(
8170                    arguments
8171                        .named_children(&mut cursor)
8172                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
8173                        .map(|argument| {
8174                            cpp_template_expression(argument, source, parameter_names, ancestry)
8175                        })
8176                        .collect(),
8177                );
8178            }
8179            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
8180                node = node
8181                    .child_by_field_name("name")
8182                    .or_else(|| node.child_by_field_name("type"))?;
8183            }
8184            _ => return None,
8185        }
8186    }
8187}
8188
8189fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
8190    let candidate = node
8191        .child_by_field_name("name")
8192        .or_else(|| node.child_by_field_name("declarator"))
8193        .or_else(|| {
8194            let mut cursor = node.walk();
8195            node.named_children(&mut cursor).find(|child| {
8196                matches!(
8197                    child.kind(),
8198                    "identifier" | "type_identifier" | "field_identifier"
8199                )
8200            })
8201        })?;
8202    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
8203    (!name.is_empty()).then_some(name)
8204}
8205
8206fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
8207    match node.kind() {
8208        "type_parameter_declaration"
8209        | "optional_type_parameter_declaration"
8210        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
8211        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
8212        _ => CppTemplateParameterKind::Value,
8213    }
8214}
8215
8216fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
8217    node.child_by_field_name("default_type")
8218        .or_else(|| node.child_by_field_name("default_value"))
8219}
8220
8221fn cpp_template_parameter_default_expression<'tree>(
8222    parameter: Node<'tree>,
8223    source: &str,
8224    parameter_names: &[String],
8225    ancestry: &ParentIndex<'tree>,
8226) -> Option<CppTemplateExpression> {
8227    let default = cpp_template_parameter_default(parameter)?;
8228    let base = cpp_template_expression(default, source, parameter_names, ancestry);
8229    let Some(pointer_error) = parameter.next_named_sibling() else {
8230        return Some(base);
8231    };
8232    let Some(pointer_declarator) =
8233        recovered_abstract_pointer_declarator_term(pointer_error, source)
8234    else {
8235        return Some(base);
8236    };
8237    Some(CppTemplateExpression {
8238        text: format!(
8239            "{}{}",
8240            base.text,
8241            normalize_cpp_whitespace(node_text(pointer_error, source))
8242        ),
8243        term: CppTemplateTerm::Node {
8244            kind: "type_descriptor".to_string(),
8245            children: vec![base.term, pointer_declarator],
8246        },
8247    })
8248}
8249
8250fn recovered_abstract_pointer_declarator_term(
8251    node: Node<'_>,
8252    source: &str,
8253) -> Option<CppTemplateTerm> {
8254    if node.kind() != "ERROR" || node.child_count() == 0 {
8255        return None;
8256    }
8257    let mut children = Vec::new();
8258    for index in 0..node.child_count() {
8259        let child = node.child(index)?;
8260        if child.kind() != "*" {
8261            return None;
8262        }
8263        children.push(CppTemplateTerm::Atom {
8264            kind: "*".to_string(),
8265            text: normalize_cpp_whitespace(node_text(child, source)),
8266        });
8267    }
8268    Some(CppTemplateTerm::Node {
8269        kind: "abstract_pointer_declarator".to_string(),
8270        children,
8271    })
8272}
8273
8274fn cpp_template_expression<'tree>(
8275    node: Node<'tree>,
8276    source: &str,
8277    parameter_names: &[String],
8278    ancestry: &ParentIndex<'tree>,
8279) -> CppTemplateExpression {
8280    let text = normalize_cpp_whitespace(node_text(node, source));
8281    CppTemplateExpression {
8282        text,
8283        term: cpp_template_term(node, source, parameter_names, ancestry),
8284    }
8285}
8286
8287pub fn cpp_template_term<'tree>(
8288    node: Node<'tree>,
8289    source: &str,
8290    parameter_names: &[String],
8291    ancestry: &ParentIndex<'tree>,
8292) -> CppTemplateTerm {
8293    enum Work<'tree> {
8294        Visit(Node<'tree>),
8295        Build { kind: String, child_count: usize },
8296    }
8297
8298    let mut work = vec![Work::Visit(node)];
8299    let mut terms = Vec::new();
8300    while let Some(next) = work.pop() {
8301        match next {
8302            Work::Visit(current) => {
8303                let text = normalize_cpp_whitespace(node_text(current, source));
8304                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
8305                    terms.push(CppTemplateTerm::Parameter(text));
8306                    continue;
8307                }
8308                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
8309                    let mut cursor = current.walk();
8310                    let named = current
8311                        .named_children(&mut cursor)
8312                        .filter(|child| !child.is_extra() && child.kind() != "comment")
8313                        .collect::<Vec<_>>();
8314                    if let [child] = named.as_slice() {
8315                        work.push(Work::Visit(*child));
8316                        continue;
8317                    }
8318                }
8319                if current.child_count() == 0 {
8320                    terms.push(CppTemplateTerm::Atom {
8321                        kind: if matches!(
8322                            current.kind(),
8323                            "identifier"
8324                                | "type_identifier"
8325                                | "field_identifier"
8326                                | "namespace_identifier"
8327                        ) {
8328                            "identifier".to_string()
8329                        } else {
8330                            current.kind().to_string()
8331                        },
8332                        text,
8333                    });
8334                    continue;
8335                }
8336                let children = (0..current.child_count())
8337                    .filter_map(|index| current.child(index))
8338                    .filter(|child| !child.is_extra() && child.kind() != "comment")
8339                    .collect::<Vec<_>>();
8340                work.push(Work::Build {
8341                    kind: current.kind().to_string(),
8342                    child_count: children.len(),
8343                });
8344                work.extend(children.into_iter().rev().map(Work::Visit));
8345            }
8346            Work::Build { kind, child_count } => {
8347                let children = terms.split_off(terms.len() - child_count);
8348                terms.push(CppTemplateTerm::Node { kind, children });
8349            }
8350        }
8351    }
8352    terms.pop().expect("template term traversal emits one root")
8353}
8354
8355fn cpp_template_term_leaf_is_parameter<'tree>(
8356    node: Node<'tree>,
8357    text: &str,
8358    parameter_names: &[String],
8359    ancestry: &ParentIndex<'tree>,
8360) -> bool {
8361    if !parameter_names.iter().any(|parameter| parameter == text) {
8362        return false;
8363    }
8364    !ancestry.parent(node).is_some_and(|parent| {
8365        matches!(
8366            parent.kind(),
8367            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
8368        ) && parent.child_by_field_name("scope").is_some()
8369            && parent.child_by_field_name("name") == Some(node)
8370    })
8371}
8372
8373fn enclosing_cpp_declaration_node<'tree>(
8374    mut node: Node<'tree>,
8375    ancestry: &ParentIndex<'tree>,
8376) -> Option<Node<'tree>> {
8377    loop {
8378        match node.kind() {
8379            "declaration"
8380            | "function_declaration"
8381            | "field_declaration"
8382            | "function_definition" => return Some(node),
8383            _ => node = ancestry.parent(node)?,
8384        }
8385    }
8386}
8387
8388fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
8389    let mut params = Vec::new();
8390    let mut cursor = parameters_node.walk();
8391    for child in parameters_node.children(&mut cursor) {
8392        match child.kind() {
8393            "parameter_declaration" | "optional_parameter_declaration" => {
8394                params.push(cpp_parameter_type(child, source));
8395            }
8396            "variadic_parameter_declaration" => {
8397                params.push(cpp_parameter_type(child, source));
8398            }
8399            "variadic_parameter" | "..." => params.push("...".to_string()),
8400            _ => {}
8401        }
8402    }
8403
8404    if params.is_empty() {
8405        "()".to_string()
8406    } else {
8407        format!("({})", params.join(", "))
8408    }
8409}
8410
8411fn cpp_signature_metadata<'tree>(
8412    signature: String,
8413    function_declarator: Node<'tree>,
8414    source: &str,
8415    ancestry: &ParentIndex<'tree>,
8416) -> SignatureMetadata {
8417    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
8418    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
8419    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
8420    let return_type_identity =
8421        cpp_callable_return_type_identity(function_declarator, source, ancestry);
8422    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
8423        return enrich(
8424            SignatureMetadata::new(signature, Vec::new())
8425                .with_return_type_text(return_type_text)
8426                .with_return_type_identity(return_type_identity),
8427        );
8428    };
8429    let callable_arity = cpp_callable_arity(parameters_node, source);
8430    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
8431    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
8432    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
8433    let Some(relative_start) = signature
8434        .get(search_from..)
8435        .and_then(|suffix| suffix.find(&parameter_text))
8436    else {
8437        return enrich(
8438            SignatureMetadata::new(signature, Vec::new())
8439                .with_callable_arity(callable_arity)
8440                .with_callable_parameter_types(callable_parameter_types)
8441                .with_return_type_text(return_type_text)
8442                .with_return_type_identity(return_type_identity),
8443        );
8444    };
8445    let parameters_start = search_from + relative_start;
8446    let parameters_end = parameters_start + parameter_text.len();
8447    let mut search_start = parameters_start;
8448    let parameters = cpp_parameter_label_nodes(parameters_node)
8449        .into_iter()
8450        .filter_map(|label_node| {
8451            let label = normalize_cpp_whitespace(node_text(label_node, source));
8452            if label.is_empty() || search_start > parameters_end {
8453                return None;
8454            }
8455            let haystack = signature.get(search_start..parameters_end)?;
8456            let relative_start = haystack.find(&label)?;
8457            let start_byte = search_start + relative_start;
8458            let end_byte = start_byte + label.len();
8459            search_start = end_byte;
8460            Some(ParameterMetadata::new(label, start_byte, end_byte))
8461        })
8462        .collect();
8463    enrich(
8464        SignatureMetadata::new(signature, parameters)
8465            .with_callable_arity(callable_arity)
8466            .with_callable_parameter_types(callable_parameter_types)
8467            .with_return_type_text(return_type_text)
8468            .with_return_type_identity(return_type_identity),
8469    )
8470}
8471
8472fn cpp_callable_is_structural_constructor<'tree>(
8473    function_declarator: Node<'tree>,
8474    source: &str,
8475    ancestry: &ParentIndex<'tree>,
8476) -> bool {
8477    let Some(name_node) = function_declarator
8478        .child_by_field_name("declarator")
8479        .or_else(|| function_declarator.child_by_field_name("name"))
8480        .or_else(|| last_named_child(function_declarator))
8481    else {
8482        return false;
8483    };
8484    let Some(callable_name) = direct_identifier_name(name_node, source) else {
8485        return false;
8486    };
8487
8488    let mut current = ancestry.parent(function_declarator);
8489    while let Some(ancestor) = current {
8490        let owner_name = match ancestor.kind() {
8491            "class_specifier" | "struct_specifier" | "union_specifier" => {
8492                class_like_name(ancestor, source, ancestry)
8493            }
8494            "ERROR" => malformed_class_error_owner_name(ancestor, source),
8495            _ => None,
8496        };
8497        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
8498            return true;
8499        }
8500        current = ancestry.parent(ancestor);
8501    }
8502    false
8503}
8504
8505/// Recover the owner name from the direct grammar shape retained when a later
8506/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
8507/// `ERROR` node:
8508///
8509/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
8510///
8511/// Direct-child checks keep this distinct from an unrelated nested class inside
8512/// a broader error region. The closing brace may be displaced past the error
8513/// node, so the opening body token is the available structural boundary.
8514fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
8515    if node.kind() != "ERROR" {
8516        return None;
8517    }
8518    let keyword = node.child(0)?;
8519    if !matches!(keyword.kind(), "class" | "struct" | "union") {
8520        return None;
8521    }
8522    let name_node = node.child(1)?;
8523    let name = direct_identifier_name(name_node, source)?;
8524    let has_body = (2..node.child_count())
8525        .filter_map(|index| node.child(index))
8526        .any(|child| child.kind() == "{");
8527    has_body.then_some(name)
8528}
8529
8530fn cpp_callable_return_type_identity<'tree>(
8531    function_declarator: Node<'tree>,
8532    source: &str,
8533    ancestry: &ParentIndex<'tree>,
8534) -> Option<StructuredTypeIdentity> {
8535    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
8536        return None;
8537    }
8538    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
8539    if let Some((return_type, _)) =
8540        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
8541    {
8542        return cpp_structured_type_identity(return_type, source, &lexical_scope);
8543    }
8544    let mut cursor = function_declarator.walk();
8545    if let Some(trailing) = function_declarator
8546        .named_children(&mut cursor)
8547        .find(|child| child.kind() == "trailing_return_type")
8548        && let Some(type_descriptor) = trailing.named_child(0)
8549    {
8550        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
8551    }
8552
8553    let mut current = function_declarator;
8554    let mut wrappers = Vec::new();
8555    while let Some(parent) = ancestry.parent(current) {
8556        if matches!(
8557            parent.kind(),
8558            "function_definition" | "declaration" | "field_declaration"
8559        ) {
8560            let type_node = parent.child_by_field_name("type")?;
8561            if cpp_export_macro_token(node_text(type_node, source))
8562                && (0..parent.named_child_count()).any(|index| {
8563                    parent
8564                        .named_child(index)
8565                        .is_some_and(|child| child.kind() == "ERROR")
8566                })
8567            {
8568                return None;
8569            }
8570            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
8571            for wrapper in wrappers.into_iter().rev() {
8572                identity = cpp_wrap_structured_type(identity, wrapper)?;
8573            }
8574            return Some(identity);
8575        }
8576        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
8577            || (matches!(
8578                parent.kind(),
8579                "pointer_declarator"
8580                    | "reference_declarator"
8581                    | "array_declarator"
8582                    | "parenthesized_declarator"
8583            ) && parent.named_child_count() == 1
8584                && parent.named_child(0) == Some(current));
8585        if !wraps_current_declarator {
8586            return None;
8587        }
8588        match parent.kind() {
8589            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
8590            "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
8591            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
8592            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
8593            _ => return None,
8594        }
8595        current = parent;
8596    }
8597    None
8598}
8599
8600fn cpp_structured_type_identity(
8601    node: Node<'_>,
8602    source: &str,
8603    lexical_scope: &[String],
8604) -> Option<StructuredTypeIdentity> {
8605    enum Work<'tree> {
8606        Visit(Node<'tree>),
8607        Wrap(CppStructuredTypeWrapper),
8608        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
8609        BuildGeneric { argument_count: usize },
8610    }
8611
8612    let mut work = vec![Work::Visit(node)];
8613    let mut values = Vec::new();
8614    let mut builder = StructuredTypeIdentityBuilder::default();
8615    while let Some(next) = work.pop() {
8616        match next {
8617            Work::Visit(current) => match current.kind() {
8618                "type_descriptor" => {
8619                    let type_node = current
8620                        .child_by_field_name("type")
8621                        .or_else(|| current.named_child(0))?;
8622                    let mut wrappers = Vec::new();
8623                    let mut cursor = current.walk();
8624                    for child in current.named_children(&mut cursor) {
8625                        if child.id() != type_node.id() {
8626                            wrappers.extend(cpp_structured_declarator_wrappers(child));
8627                        }
8628                    }
8629                    work.push(Work::ApplyWrappers(wrappers));
8630                    work.push(Work::Visit(type_node));
8631                }
8632                "pointer_declarator" | "abstract_pointer_declarator" => {
8633                    let child = current
8634                        .child_by_field_name("declarator")
8635                        .or_else(|| current.named_child(0))?;
8636                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
8637                    work.push(Work::Visit(child));
8638                }
8639                "reference_declarator" => {
8640                    let child = current
8641                        .child_by_field_name("declarator")
8642                        .or_else(|| current.named_child(0))?;
8643                    work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
8644                    work.push(Work::Visit(child));
8645                }
8646                "array_declarator" | "abstract_array_declarator" => {
8647                    let child = current
8648                        .child_by_field_name("declarator")
8649                        .or_else(|| current.named_child(0))?;
8650                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
8651                    work.push(Work::Visit(child));
8652                }
8653                "template_type" => {
8654                    let name_node = current.child_by_field_name("name")?;
8655                    let arguments = current
8656                        .child_by_field_name("arguments")
8657                        .map(|arguments_node| {
8658                            let mut cursor = arguments_node.walk();
8659                            arguments_node
8660                                .named_children(&mut cursor)
8661                                .filter(|child| !child.is_extra() && child.kind() != "comment")
8662                                .collect::<Vec<_>>()
8663                        })
8664                        .unwrap_or_default();
8665                    work.push(Work::BuildGeneric {
8666                        argument_count: arguments.len(),
8667                    });
8668                    work.extend(arguments.into_iter().rev().map(Work::Visit));
8669                    work.push(Work::Visit(name_node));
8670                }
8671                "qualified_identifier"
8672                | "scoped_identifier"
8673                | "scoped_type_identifier"
8674                | "type_identifier"
8675                | "field_identifier"
8676                | "identifier"
8677                | "namespace_identifier"
8678                | "primitive_type" => {
8679                    values.push(builder.named(cpp_structured_named_type(
8680                        current,
8681                        source,
8682                        lexical_scope,
8683                    )?)?);
8684                }
8685                _ => {
8686                    let child = current.child_by_field_name("type").or_else(|| {
8687                        (current.named_child_count() == 1)
8688                            .then(|| current.named_child(0))
8689                            .flatten()
8690                    })?;
8691                    work.push(Work::Visit(child));
8692                }
8693            },
8694            Work::Wrap(wrapper) => {
8695                let root = values.pop()?;
8696                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
8697            }
8698            Work::ApplyWrappers(wrappers) => {
8699                let mut root = values.pop()?;
8700                for wrapper in wrappers.into_iter().rev() {
8701                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
8702                }
8703                values.push(root);
8704            }
8705            Work::BuildGeneric { argument_count } => {
8706                let value_count = argument_count.checked_add(1)?;
8707                let start = values.len().checked_sub(value_count)?;
8708                let mut built = values.split_off(start);
8709                let base = built.remove(0);
8710                values.push(builder.generic(base, built)?);
8711            }
8712        }
8713    }
8714    (values.len() == 1)
8715        .then(|| values.pop())
8716        .flatten()
8717        .and_then(|root| builder.finish(root))
8718}
8719
8720fn cpp_structured_named_type(
8721    node: Node<'_>,
8722    source: &str,
8723    lexical_scope: &[String],
8724) -> Option<StructuredTypeName> {
8725    let path = cpp_structured_type_path(node, source)?;
8726    let absolute = node.child_by_field_name("scope").is_none()
8727        && node.child(0).is_some_and(|child| child.kind() == "::");
8728    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
8729}
8730
8731#[derive(Clone, Copy)]
8732enum CppStructuredTypeWrapper {
8733    Pointer,
8734    Reference,
8735    Array,
8736}
8737
8738fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
8739    let mut wrappers = Vec::new();
8740    let mut current = node;
8741    loop {
8742        match current.kind() {
8743            "pointer_declarator" | "abstract_pointer_declarator" => {
8744                wrappers.push(CppStructuredTypeWrapper::Pointer)
8745            }
8746            "reference_declarator" | "abstract_reference_declarator" => {
8747                wrappers.push(CppStructuredTypeWrapper::Reference)
8748            }
8749            "array_declarator" | "abstract_array_declarator" => {
8750                wrappers.push(CppStructuredTypeWrapper::Array)
8751            }
8752            _ => break,
8753        }
8754        let Some(child) = current
8755            .child_by_field_name("declarator")
8756            .or_else(|| current.named_child(0))
8757        else {
8758            break;
8759        };
8760        current = child;
8761    }
8762    wrappers
8763}
8764
8765fn cpp_wrap_structured_type(
8766    identity: StructuredTypeIdentity,
8767    wrapper: CppStructuredTypeWrapper,
8768) -> Option<StructuredTypeIdentity> {
8769    match wrapper {
8770        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
8771        CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
8772        CppStructuredTypeWrapper::Array => identity.wrap_array(),
8773    }
8774}
8775
8776fn cpp_wrap_structured_type_node(
8777    builder: &mut StructuredTypeIdentityBuilder,
8778    inner: StructuredTypeNodeId,
8779    wrapper: CppStructuredTypeWrapper,
8780) -> Option<StructuredTypeNodeId> {
8781    match wrapper {
8782        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
8783        CppStructuredTypeWrapper::Reference => builder.reference(inner),
8784        CppStructuredTypeWrapper::Array => builder.array(inner),
8785    }
8786}
8787
8788fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
8789    let mut path = Vec::new();
8790    let mut stack = vec![node];
8791    while let Some(current) = stack.pop() {
8792        match current.kind() {
8793            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
8794                let component = node_text(current, source).to_string();
8795                if component.is_empty() {
8796                    return None;
8797                }
8798                path.push(component);
8799            }
8800            "template_type" | "dependent_type" => {
8801                stack.push(current.child_by_field_name("name")?);
8802            }
8803            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
8804                stack.push(current.child_by_field_name("name")?);
8805                if let Some(scope) = current.child_by_field_name("scope") {
8806                    stack.push(scope);
8807                }
8808            }
8809            _ => return None,
8810        }
8811    }
8812    (!path.is_empty()).then_some(path)
8813}
8814
8815fn cpp_callable_lexical_scope<'tree>(
8816    node: Node<'tree>,
8817    source: &str,
8818    ancestry: &ParentIndex<'tree>,
8819) -> Vec<String> {
8820    let mut groups = Vec::new();
8821    let mut current = ancestry.parent(node);
8822    while let Some(parent) = current {
8823        if matches!(
8824            parent.kind(),
8825            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
8826        ) && let Some(name_node) = parent.child_by_field_name("name")
8827            && let Some(components) = cpp_structured_type_path(name_node, source)
8828            && !components.is_empty()
8829        {
8830            groups.push(components);
8831        }
8832        current = ancestry.parent(parent);
8833    }
8834    groups.reverse();
8835    groups.into_iter().flatten().collect()
8836}
8837
8838fn cpp_callable_dispatch_extensibility<'tree>(
8839    function_declarator: Node<'tree>,
8840    ancestry: &ParentIndex<'tree>,
8841) -> DispatchExtensibility {
8842    let mut declaration = None;
8843    let mut current = Some(function_declarator);
8844    while let Some(node) = current {
8845        match node.kind() {
8846            "template_declaration"
8847            | "preproc_if"
8848            | "preproc_ifdef"
8849            | "preproc_else"
8850            | "preproc_elif"
8851            | "preproc_call"
8852            | "ERROR" => return DispatchExtensibility::Open,
8853            "declaration" | "field_declaration" | "function_definition" => {
8854                declaration.get_or_insert(node);
8855            }
8856            "translation_unit" => break,
8857            _ => {}
8858        }
8859        current = ancestry.parent(node);
8860    }
8861    let Some(declaration) = declaration else {
8862        return DispatchExtensibility::Open;
8863    };
8864
8865    let mut saw_virtual_boundary = false;
8866    let mut stack = vec![declaration];
8867    while let Some(node) = stack.pop() {
8868        match node.kind() {
8869            "compound_statement" | "field_declaration_list" => continue,
8870            "final" | "final_specifier" => return DispatchExtensibility::Closed,
8871            "virtual"
8872            | "override"
8873            | "virtual_specifier"
8874            | "pure_virtual_clause"
8875            | "template_parameter_list"
8876            | "template_method"
8877            | "template_function"
8878            | "ERROR" => saw_virtual_boundary = true,
8879            _ => {}
8880        }
8881        let mut cursor = node.walk();
8882        stack.extend(node.children(&mut cursor));
8883    }
8884
8885    if saw_virtual_boundary {
8886        DispatchExtensibility::Open
8887    } else {
8888        DispatchExtensibility::Closed
8889    }
8890}
8891
8892fn cpp_callable_linkage<'tree>(
8893    declaration: Node<'tree>,
8894    source: &str,
8895    ancestry: &ParentIndex<'tree>,
8896) -> CallableLinkage {
8897    let mut enclosed_by_class = false;
8898    let mut current = ancestry.parent(declaration);
8899    while let Some(node) = current {
8900        if node.kind() == "namespace_definition"
8901            && node
8902                .child_by_field_name("name")
8903                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8904        {
8905            return CallableLinkage::Internal;
8906        }
8907        if matches!(
8908            node.kind(),
8909            "class_specifier" | "struct_specifier" | "union_specifier"
8910        ) {
8911            if node
8912                .child_by_field_name("name")
8913                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8914            {
8915                return CallableLinkage::Internal;
8916            }
8917            enclosed_by_class = true;
8918        }
8919        if matches!(node.kind(), "function_definition" | "lambda_expression") {
8920            return CallableLinkage::Internal;
8921        }
8922        current = ancestry.parent(node);
8923    }
8924
8925    if enclosed_by_class {
8926        return CallableLinkage::External;
8927    }
8928
8929    let mut cursor = declaration.walk();
8930    if declaration.named_children(&mut cursor).any(|child| {
8931        child.kind() == "storage_class_specifier"
8932            && normalize_cpp_whitespace(node_text(child, source)) == "static"
8933    }) {
8934        CallableLinkage::Internal
8935    } else {
8936        CallableLinkage::External
8937    }
8938}
8939
8940fn cpp_callable_return_type_text<'tree>(
8941    function_declarator: Node<'tree>,
8942    source: &str,
8943    ancestry: &ParentIndex<'tree>,
8944) -> Option<String> {
8945    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
8946        return None;
8947    }
8948    if let Some((return_type, _)) =
8949        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
8950    {
8951        let text = normalize_cpp_whitespace(node_text(return_type, source));
8952        return (!text.is_empty()).then_some(text);
8953    }
8954    let mut cursor = function_declarator.walk();
8955    if let Some(trailing) = function_declarator
8956        .named_children(&mut cursor)
8957        .find(|child| child.kind() == "trailing_return_type")
8958        && let Some(type_descriptor) = trailing.named_child(0)
8959    {
8960        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
8961        if !text.is_empty() {
8962            return Some(text);
8963        }
8964    }
8965
8966    let mut current = function_declarator;
8967    let mut indirection = String::new();
8968    while let Some(parent) = ancestry.parent(current) {
8969        if matches!(
8970            parent.kind(),
8971            "function_definition" | "declaration" | "field_declaration"
8972        ) {
8973            let type_node = parent.child_by_field_name("type")?;
8974            if cpp_export_macro_token(node_text(type_node, source))
8975                && (0..parent.named_child_count()).any(|index| {
8976                    parent
8977                        .named_child(index)
8978                        .is_some_and(|child| child.kind() == "ERROR")
8979                })
8980            {
8981                // Export/decorator macros commonly occupy the grammar's `type`
8982                // field and leave the semantic return type in an ERROR sibling.
8983                // Do not persist the macro token as a return type. The malformed
8984                // declaration does not carry enough structured evidence here.
8985                return None;
8986            }
8987            let base = normalize_cpp_whitespace(node_text(type_node, source));
8988            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
8989        }
8990        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
8991            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
8992                && parent.named_child_count() == 1
8993                && parent.named_child(0) == Some(current));
8994        if wraps_current_declarator {
8995            match parent.kind() {
8996                "pointer_declarator" => indirection.push('*'),
8997                "reference_declarator" => {
8998                    let reference = parent
8999                        .children(&mut parent.walk())
9000                        .find(|child| !child.is_named())
9001                        .map(|child| node_text(child, source))
9002                        .unwrap_or("&");
9003                    indirection.push_str(reference);
9004                }
9005                "init_declarator" | "parenthesized_declarator" => {}
9006                _ => return None,
9007            }
9008            current = parent;
9009            continue;
9010        }
9011        return None;
9012    }
9013    None
9014}
9015
9016fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
9017    let mut required = 0;
9018    let mut total = 0;
9019    let mut repeated = false;
9020    let mut cursor = parameters_node.walk();
9021    for child in parameters_node.children(&mut cursor) {
9022        match child.kind() {
9023            "parameter_declaration" => {
9024                if cpp_parameter_is_explicit_object(child, source) {
9025                    continue;
9026                }
9027                if child.child_by_field_name("declarator").is_none()
9028                    && child
9029                        .child_by_field_name("type")
9030                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
9031                {
9032                    continue;
9033                }
9034                required += 1;
9035                total += 1;
9036            }
9037            "optional_parameter_declaration" => total += 1,
9038            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
9039                repeated = true;
9040            }
9041            _ => {}
9042        }
9043    }
9044    CallableArity::new(required, total, repeated)
9045}
9046
9047fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
9048    parameter
9049        .child_by_field_name("type")
9050        .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
9051        .and_then(|type_node| type_node.child_by_field_name("constraint"))
9052        .is_some_and(|constraint| {
9053            constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
9054        })
9055}
9056
9057/// One entry of a callable's invocation parameter list.
9058///
9059/// The list excludes an explicit object parameter and a lone `void`, so its
9060/// length is the callable's invocation arity. Every derivation of a parameter
9061/// type - the rendered spelling used for overload discrimination and the
9062/// structured identity used by dependency-pack production - starts from this
9063/// same sequence, so the two can never disagree about which parameters exist.
9064#[derive(Clone, Copy)]
9065enum CppParameterSlot<'tree> {
9066    Declared(Node<'tree>),
9067    Ellipsis,
9068}
9069
9070fn cpp_callable_parameter_slots<'tree>(
9071    parameters_node: Node<'tree>,
9072    source: &str,
9073) -> Vec<CppParameterSlot<'tree>> {
9074    let mut slots = Vec::new();
9075    let mut cursor = parameters_node.walk();
9076    for parameter in parameters_node.children(&mut cursor) {
9077        match parameter.kind() {
9078            "parameter_declaration" | "optional_parameter_declaration" => {
9079                if cpp_parameter_is_explicit_object(parameter, source)
9080                    || (parameter.child_by_field_name("declarator").is_none()
9081                        && parameter
9082                            .child_by_field_name("type")
9083                            .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
9084                {
9085                    continue;
9086                }
9087                slots.push(CppParameterSlot::Declared(parameter));
9088            }
9089            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
9090                slots.push(CppParameterSlot::Ellipsis);
9091            }
9092            _ => {}
9093        }
9094    }
9095    slots
9096}
9097
9098fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
9099    cpp_callable_parameter_slots(parameters_node, source)
9100        .into_iter()
9101        .map(|slot| match slot {
9102            CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
9103            CppParameterSlot::Ellipsis => "...".to_string(),
9104        })
9105        .collect()
9106}
9107
9108/// One callable parameter's parser-derived type.
9109///
9110/// A rendered spelling such as `const T&` is a source text, not a type name. A
9111/// consumer that must publish a type into a structured model - a semantic-pack
9112/// type reference, for example - reads this instead.
9113#[derive(Debug, Clone, PartialEq, Eq)]
9114pub enum CppParameterType {
9115    /// The written type reduced to a structured identity. C++ cv-qualifiers
9116    /// have no place in that model and are not represented.
9117    Structured(StructuredTypeIdentity),
9118    /// A `...` pack, which declares no parameter type at all.
9119    Ellipsis,
9120    /// A written type with no structured reduction, such as a macro-obscured,
9121    /// `decltype`-computed, or function-pointer parameter.
9122    Unstructured,
9123}
9124
9125/// The structured type of each invocation parameter, in declaration order.
9126///
9127/// The result is index-parallel with the rendered
9128/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
9129/// callable.
9130pub fn cpp_callable_parameter_type_identities<'tree>(
9131    function_declarator: Node<'tree>,
9132    source: &str,
9133    ancestry: &ParentIndex<'tree>,
9134) -> Vec<CppParameterType> {
9135    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
9136        return Vec::new();
9137    };
9138    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
9139    cpp_callable_parameter_slots(parameters_node, source)
9140        .into_iter()
9141        .map(|slot| match slot {
9142            CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
9143            CppParameterSlot::Declared(parameter) => {
9144                cpp_parameter_type_identity(parameter, source, &lexical_scope)
9145                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
9146            }
9147        })
9148        .collect()
9149}
9150
9151fn cpp_parameter_type_identity(
9152    parameter: Node<'_>,
9153    source: &str,
9154    lexical_scope: &[String],
9155) -> Option<StructuredTypeIdentity> {
9156    let type_node = parameter.child_by_field_name("type")?;
9157    let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
9158    if let Some(declarator) = cpp_parameter_declarator(parameter) {
9159        for wrapper in cpp_structured_declarator_wrappers(declarator)
9160            .into_iter()
9161            .rev()
9162        {
9163            identity = cpp_wrap_structured_type(identity, wrapper)?;
9164        }
9165    }
9166    Some(identity)
9167}
9168
9169/// One callable parameter's comparable shape.
9170///
9171/// [`CppParameterType`] above answers "which type is written here" for a
9172/// structured model and deliberately records no cv-qualifiers, so it reports
9173/// the same value for `f(char *)` and `f(const char *)`. Deciding whether two
9174/// callable declarations declare one function needs the opposite trade: every
9175/// cv-qualifier that C++ counts as part of the parameter type must survive,
9176/// while the two declarations may spell the same type through different
9177/// qualifications. This slot carries that comparand.
9178///
9179/// The result is index-parallel with [`cpp_callable_parameter_type_identities`]
9180/// and with the rendered parameter spellings of the same callable.
9181#[derive(Debug, Clone, PartialEq, Eq)]
9182pub enum CppComparableSlot {
9183    /// A declared parameter reduced to its comparable shape.
9184    Shape(CppComparableParameter),
9185    /// A `...` pack, which declares no parameter type at all.
9186    Ellipsis,
9187    /// A parameter with no comparable reduction, such as a macro-obscured,
9188    /// `decltype`-computed, or function-pointer parameter.
9189    Unstructured,
9190}
9191
9192/// A parameter type as a flat arena of nodes plus a root index.
9193///
9194/// The arena carries the same rationale as [`StructuredTypeIdentity`]: source
9195/// can nest types very deeply, and cloning, comparing or dropping the value
9196/// must not consume the Rust call stack. Nodes are appended in post-order, so
9197/// every child index is smaller than its parent's and the last appended node is
9198/// the root.
9199///
9200/// That post-order append is also what makes the derived `PartialEq` a correct
9201/// structural equality: the builder below is deterministic, so one type shape
9202/// has exactly one arena layout no matter which spelling produced it. Two
9203/// shapes are equal as values iff they are equal as type trees.
9204#[derive(Debug, Clone, PartialEq, Eq)]
9205pub struct CppComparableParameter {
9206    nodes: Vec<CppComparableNode>,
9207    root: usize,
9208}
9209
9210/// One node of a [`CppComparableParameter`] arena.
9211///
9212/// `Reference` and `Array` carry no qualifiers because the grammar writes none
9213/// on them: a reference cannot be cv-qualified in C++, and an array's
9214/// qualifiers belong to its element type. A cv-qualifier written on a generic
9215/// type (`const std::vector<int>`) is recorded on the generic's base leaf,
9216/// which is the only Named node the whole spelling produces.
9217#[derive(Debug, Clone, PartialEq, Eq)]
9218pub enum CppComparableNode {
9219    Named {
9220        name: StructuredTypeName,
9221        primitive: bool,
9222        konst: bool,
9223        volatil: bool,
9224    },
9225    Pointer {
9226        inner: usize,
9227        konst: bool,
9228        volatil: bool,
9229    },
9230    Reference {
9231        inner: usize,
9232    },
9233    Array {
9234        inner: usize,
9235    },
9236    Generic {
9237        base: usize,
9238        arguments: Vec<usize>,
9239    },
9240}
9241
9242impl CppComparableParameter {
9243    pub fn root(&self) -> usize {
9244        self.root
9245    }
9246
9247    pub fn node(&self, index: usize) -> &CppComparableNode {
9248        &self.nodes[index]
9249    }
9250
9251    /// Apply the [dcl.fct]/5 parameter-type adjustments, which hold at the
9252    /// parameter's top level only.
9253    ///
9254    /// A top-level cv-qualifier is discarded, so `f(const int)` and `f(int)`
9255    /// declare one function, and a top-level array type becomes a pointer to
9256    /// its element type, so `f(int[3])` and `f(int *)` do too. The outermost
9257    /// type constructor is this arena's root, which is why both adjustments
9258    /// are one match on it: cv on an inner pointer level, on a pointee, or on
9259    /// an array element keeps distinguishing the type, and an array behind a
9260    /// pointer or reference is not a top-level array.
9261    fn adjust_parameter_top_level(&mut self) {
9262        let root = self.root;
9263        match &mut self.nodes[root] {
9264            CppComparableNode::Named { konst, volatil, .. }
9265            | CppComparableNode::Pointer { konst, volatil, .. } => {
9266                *konst = false;
9267                *volatil = false;
9268            }
9269            CppComparableNode::Array { inner } => {
9270                let inner = *inner;
9271                self.nodes[root] = CppComparableNode::Pointer {
9272                    inner,
9273                    konst: false,
9274                    volatil: false,
9275                };
9276            }
9277            CppComparableNode::Generic { base, .. } => {
9278                let base = *base;
9279                let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
9280                    unreachable!("a comparable generic's base is always a named leaf");
9281                };
9282                *konst = false;
9283                *volatil = false;
9284            }
9285            CppComparableNode::Reference { .. } => {}
9286        }
9287    }
9288}
9289
9290/// The comparable shape of each invocation parameter, in declaration order.
9291///
9292/// The result is index-parallel with
9293/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
9294/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
9295/// must treat as evidence of nothing rather than as agreement.
9296pub fn cpp_comparable_parameter_shapes<'tree>(
9297    function_declarator: Node<'tree>,
9298    source: &str,
9299    ancestry: &ParentIndex<'tree>,
9300) -> Vec<CppComparableSlot> {
9301    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
9302        return Vec::new();
9303    };
9304    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
9305    cpp_callable_parameter_slots(parameters_node, source)
9306        .into_iter()
9307        .map(|slot| match slot {
9308            CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
9309            CppParameterSlot::Declared(parameter) => {
9310                cpp_comparable_parameter(parameter, source, &lexical_scope)
9311                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
9312            }
9313        })
9314        .collect()
9315}
9316
9317fn cpp_comparable_parameter(
9318    parameter: Node<'_>,
9319    source: &str,
9320    lexical_scope: &[String],
9321) -> Option<CppComparableParameter> {
9322    let type_node = parameter.child_by_field_name("type")?;
9323    let levels = match cpp_parameter_declarator(parameter) {
9324        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
9325        None => Vec::new(),
9326    };
9327    let mut shape = cpp_comparable_type_shape(
9328        type_node,
9329        cpp_cv_qualifiers(parameter, source),
9330        levels,
9331        source,
9332        lexical_scope,
9333    )?;
9334    shape.adjust_parameter_top_level();
9335    Some(shape)
9336}
9337
9338/// The `const` and `volatile` qualifiers written as direct named children of
9339/// `node`.
9340///
9341/// The grammar exposes `type_qualifier` as a non-field named child in exactly
9342/// the three places a parameter's qualifiers can be written: on the
9343/// `parameter_declaration` itself (the base type), on a `type_descriptor`
9344/// (inside a template argument list), and on each `pointer_declarator` level
9345/// (the pointer object). Every other qualifier the grammar admits - `restrict`
9346/// and friends - takes no part in C++ type identity, the same filter
9347/// `cpp_parameter_type` applies to the rendered spelling (#1827).
9348fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
9349    let mut qualifiers = CppCvQualifiers::default();
9350    let mut cursor = node.walk();
9351    for child in node.named_children(&mut cursor) {
9352        if child.kind() != "type_qualifier" {
9353            continue;
9354        }
9355        match node_text(child, source) {
9356            "const" => qualifiers.konst = true,
9357            "volatile" => qualifiers.volatil = true,
9358            _ => {}
9359        }
9360    }
9361    qualifiers
9362}
9363
9364#[derive(Clone, Copy, Default)]
9365struct CppCvQualifiers {
9366    konst: bool,
9367    volatil: bool,
9368}
9369
9370impl CppCvQualifiers {
9371    fn union(self, other: Self) -> Self {
9372        Self {
9373            konst: self.konst || other.konst,
9374            volatil: self.volatil || other.volatil,
9375        }
9376    }
9377}
9378
9379/// One pointer, reference or array level a declarator chain adds.
9380#[derive(Clone, Copy)]
9381enum CppComparableLevel {
9382    Pointer { konst: bool, volatil: bool },
9383    Reference,
9384    Array,
9385}
9386
9387/// The levels `declarator` adds, outermost written level first.
9388///
9389/// C++ declarator syntax binds inside out: the level written closest to the
9390/// declared name is the outermost type constructor, and tree-sitter nests it
9391/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
9392/// applies in order to reach "array of pointer to int", and the qualifier of
9393/// `int * const *p` is read on the level it was written next to, the inner
9394/// pointer of the resulting type.
9395///
9396/// A declarator chain that names a function type - a function-pointer
9397/// parameter - has no comparable shape and reports `None`, matching the
9398/// structured identity channel.
9399fn cpp_comparable_declarator_levels(
9400    declarator: Node<'_>,
9401    source: &str,
9402) -> Option<Vec<CppComparableLevel>> {
9403    let mut levels = Vec::new();
9404    let mut current = declarator;
9405    loop {
9406        match current.kind() {
9407            "pointer_declarator" | "abstract_pointer_declarator" => {
9408                let qualifiers = cpp_cv_qualifiers(current, source);
9409                levels.push(CppComparableLevel::Pointer {
9410                    konst: qualifiers.konst,
9411                    volatil: qualifiers.volatil,
9412                });
9413            }
9414            "reference_declarator" | "abstract_reference_declarator" => {
9415                levels.push(CppComparableLevel::Reference);
9416            }
9417            "array_declarator" | "abstract_array_declarator" => {
9418                levels.push(CppComparableLevel::Array);
9419            }
9420            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
9421            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
9422            _ => return None,
9423        }
9424        let Some(next) = cpp_nested_declarator(current) else {
9425            return Some(levels);
9426        };
9427        current = next;
9428    }
9429}
9430
9431/// Reduce one written type to a comparable arena.
9432///
9433/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
9434/// two additions: each visited type node carries the cv-qualifiers written on
9435/// it, and declarator levels arrive as a prepared list rather than being
9436/// rediscovered inside the walk.
9437fn cpp_comparable_type_shape(
9438    type_node: Node<'_>,
9439    qualifiers: CppCvQualifiers,
9440    levels: Vec<CppComparableLevel>,
9441    source: &str,
9442    lexical_scope: &[String],
9443) -> Option<CppComparableParameter> {
9444    enum Work<'tree> {
9445        Visit {
9446            node: Node<'tree>,
9447            qualifiers: CppCvQualifiers,
9448        },
9449        ApplyLevels(Vec<CppComparableLevel>),
9450        BuildGeneric {
9451            argument_count: usize,
9452        },
9453    }
9454
9455    let mut nodes: Vec<CppComparableNode> = Vec::new();
9456    let mut values: Vec<usize> = Vec::new();
9457    let mut work = vec![
9458        Work::ApplyLevels(levels),
9459        Work::Visit {
9460            node: type_node,
9461            qualifiers,
9462        },
9463    ];
9464    while let Some(next) = work.pop() {
9465        match next {
9466            Work::Visit { node, qualifiers } => match node.kind() {
9467                "type_descriptor" => {
9468                    let inner_type = node
9469                        .child_by_field_name("type")
9470                        .or_else(|| node.named_child(0))?;
9471                    let mut cursor = node.walk();
9472                    let declarator = node.child_by_field_name("declarator").or_else(|| {
9473                        node.named_children(&mut cursor).find(|child| {
9474                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
9475                        })
9476                    });
9477                    let levels = match declarator {
9478                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
9479                        None => Vec::new(),
9480                    };
9481                    work.push(Work::ApplyLevels(levels));
9482                    work.push(Work::Visit {
9483                        node: inner_type,
9484                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
9485                    });
9486                }
9487                "sized_type_specifier" => {
9488                    // `unsigned char` is one primitive type whose components are
9489                    // partly unnamed tokens, so the whole specifier is its own
9490                    // name component. Reducing it to the `type` child would make
9491                    // `f(unsigned char)` and `f(char)` compare equal.
9492                    let name = StructuredTypeName::new(
9493                        vec![normalize_cpp_whitespace(node_text(node, source))],
9494                        lexical_scope.to_vec(),
9495                        false,
9496                    )?;
9497                    values.push(cpp_push_comparable_node(
9498                        &mut nodes,
9499                        CppComparableNode::Named {
9500                            name,
9501                            primitive: true,
9502                            konst: qualifiers.konst,
9503                            volatil: qualifiers.volatil,
9504                        },
9505                    ));
9506                }
9507                "qualified_identifier"
9508                | "scoped_identifier"
9509                | "scoped_type_identifier"
9510                | "type_identifier"
9511                | "field_identifier"
9512                | "identifier"
9513                | "namespace_identifier"
9514                | "primitive_type"
9515                | "template_type" => {
9516                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
9517                    values.push(cpp_push_comparable_node(
9518                        &mut nodes,
9519                        CppComparableNode::Named {
9520                            name,
9521                            primitive: node.kind() == "primitive_type",
9522                            konst: qualifiers.konst,
9523                            volatil: qualifiers.volatil,
9524                        },
9525                    ));
9526                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
9527                        let mut cursor = arguments_node.walk();
9528                        let arguments = arguments_node
9529                            .named_children(&mut cursor)
9530                            .filter(|child| !child.is_extra() && child.kind() != "comment")
9531                            .collect::<Vec<_>>();
9532                        work.push(Work::BuildGeneric {
9533                            argument_count: arguments.len(),
9534                        });
9535                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
9536                            node: argument,
9537                            qualifiers: CppCvQualifiers::default(),
9538                        }));
9539                    }
9540                }
9541                _ => {
9542                    let inner = node.child_by_field_name("type").or_else(|| {
9543                        (node.named_child_count() == 1)
9544                            .then(|| node.named_child(0))
9545                            .flatten()
9546                    })?;
9547                    work.push(Work::Visit {
9548                        node: inner,
9549                        qualifiers,
9550                    });
9551                }
9552            },
9553            Work::ApplyLevels(levels) => {
9554                let mut root = values.pop()?;
9555                for level in levels {
9556                    let node = match level {
9557                        CppComparableLevel::Pointer { konst, volatil } => {
9558                            CppComparableNode::Pointer {
9559                                inner: root,
9560                                konst,
9561                                volatil,
9562                            }
9563                        }
9564                        CppComparableLevel::Reference => {
9565                            CppComparableNode::Reference { inner: root }
9566                        }
9567                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
9568                    };
9569                    root = cpp_push_comparable_node(&mut nodes, node);
9570                }
9571                values.push(root);
9572            }
9573            Work::BuildGeneric { argument_count } => {
9574                let value_count = argument_count.checked_add(1)?;
9575                let start = values.len().checked_sub(value_count)?;
9576                let mut built = values.split_off(start);
9577                let base = built.remove(0);
9578                values.push(cpp_push_comparable_node(
9579                    &mut nodes,
9580                    CppComparableNode::Generic {
9581                        base,
9582                        arguments: built,
9583                    },
9584                ));
9585            }
9586        }
9587    }
9588    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
9589    debug_assert_eq!(
9590        root,
9591        nodes.len().saturating_sub(1),
9592        "comparable nodes are appended in post-order, so the root is the last one"
9593    );
9594    Some(CppComparableParameter { nodes, root })
9595}
9596
9597fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
9598    nodes.push(node);
9599    nodes.len() - 1
9600}
9601
9602/// The template argument list of the name `node` terminates in, if any.
9603///
9604/// `std::vector<int>` writes its arguments on the `name` of a qualified
9605/// identifier, so a walk that stopped at the qualified node would reduce
9606/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
9607fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
9608    let mut current = node;
9609    loop {
9610        match current.kind() {
9611            "template_type" => return current.child_by_field_name("arguments"),
9612            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
9613                current = current.child_by_field_name("name")?;
9614            }
9615            _ => return None,
9616        }
9617    }
9618}
9619
9620/// The callable declarator of the declaration that covers `start_byte`.
9621///
9622/// A consumer that holds a declaration's recorded byte position rather than its
9623/// syntax node - external header extraction, for instance - uses this to reach
9624/// the same `function_declarator` the declaration walk read.
9625pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
9626    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
9627    loop {
9628        if matches!(
9629            current.kind(),
9630            "declaration" | "field_declaration" | "function_definition"
9631        ) && let Some(declarator) = current
9632            .child_by_field_name("declarator")
9633            .and_then(extract_function_declarator)
9634        {
9635            return Some(declarator);
9636        }
9637        current = current.parent()?;
9638    }
9639}
9640
9641fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
9642    let mut labels = Vec::new();
9643    let mut cursor = parameters_node.walk();
9644    for child in parameters_node.children(&mut cursor) {
9645        match child.kind() {
9646            "parameter_declaration" | "optional_parameter_declaration" => {
9647                if let Some(name_node) = child
9648                    .child_by_field_name("declarator")
9649                    .and_then(cpp_declarator_label_node)
9650                {
9651                    labels.push(name_node);
9652                } else {
9653                    labels.push(child);
9654                }
9655            }
9656            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
9657                labels.push(child);
9658            }
9659            _ => {}
9660        }
9661    }
9662    labels
9663}
9664
9665fn cpp_signature_search_start<'tree>(
9666    signature: &str,
9667    function_declarator: Node<'tree>,
9668    source: &str,
9669    ancestry: &ParentIndex<'tree>,
9670) -> usize {
9671    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
9672        return 0;
9673    };
9674    let raw = node_text(enclosing, source);
9675    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
9676    let offset = function_declarator
9677        .start_byte()
9678        .saturating_sub(enclosing.start_byte())
9679        .saturating_sub(leading_trim_bytes);
9680    offset.min(signature.len())
9681}
9682
9683fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
9684    match node.kind() {
9685        "identifier" | "field_identifier" => Some(node),
9686        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
9687            .child_by_field_name("declarator")
9688            .or_else(|| last_named_child(node))
9689            .and_then(cpp_declarator_label_node),
9690        "array_declarator" => node
9691            .child_by_field_name("declarator")
9692            .and_then(cpp_declarator_label_node),
9693        "function_declarator" => node
9694            .child_by_field_name("declarator")
9695            .or_else(|| node.child_by_field_name("name"))
9696            .or_else(|| last_named_child(node))
9697            .and_then(cpp_declarator_label_node),
9698        _ => None,
9699    }
9700}
9701
9702fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
9703    let base_type = parameter
9704        .child_by_field_name("type")
9705        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
9706        .unwrap_or_default();
9707    let declarator = cpp_parameter_declarator(parameter);
9708    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
9709    // are discarded, so `f(const int)` and `f(int)` declare one function. A
9710    // qualifier written next to the parameter's type is only top-level when
9711    // the declarator adds no indirection; behind a pointer, reference or array
9712    // declarator the same qualifier belongs to the pointee, referent or
9713    // element and keeps distinguishing the type (#1827).
9714    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
9715    let mut cursor = parameter.walk();
9716    let qualifiers = parameter
9717        .named_children(&mut cursor)
9718        .filter(|child| child.kind() == "type_qualifier")
9719        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
9720        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
9721        .collect::<Vec<_>>()
9722        .join(" ");
9723    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
9724        (true, _) => base_type,
9725        (_, true) => qualifiers,
9726        (false, false) => format!("{qualifiers} {base_type}"),
9727    };
9728    let declarator_suffix = declarator
9729        .map(|node| cpp_declarator_suffix_without_name(node, source))
9730        .unwrap_or_default();
9731
9732    let combined = if type_text.is_empty() {
9733        declarator_suffix
9734    } else if declarator_suffix.is_empty() {
9735        type_text
9736    } else {
9737        format!("{type_text} {declarator_suffix}")
9738    };
9739    normalize_cpp_type_text(&combined)
9740}
9741
9742fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
9743    parameter.child_by_field_name("declarator").or_else(|| {
9744        // Some unnamed prototype parameters expose their abstract declarator
9745        // as a direct named child without the grammar's `declarator` field.
9746        // Recover only the structured abstract-declarator node; the parameter's
9747        // type and qualifiers are distinct children and must not be guessed from
9748        // source text.
9749        let mut cursor = parameter.walk();
9750        parameter
9751            .named_children(&mut cursor)
9752            .find(|child| is_cpp_abstract_declarator(child.kind()))
9753    })
9754}
9755
9756/// Whether a parameter's declarator chain adds indirection - a pointer,
9757/// reference, array or function declarator - to the parameter's written type.
9758pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
9759    let mut current = Some(declarator);
9760    while let Some(node) = current {
9761        if matches!(
9762            node.kind(),
9763            "pointer_declarator"
9764                | "abstract_pointer_declarator"
9765                | "reference_declarator"
9766                | "abstract_reference_declarator"
9767                | "array_declarator"
9768                | "abstract_array_declarator"
9769                | "function_declarator"
9770                | "abstract_function_declarator"
9771        ) {
9772            return true;
9773        }
9774        current = cpp_nested_declarator(node);
9775    }
9776    false
9777}
9778
9779fn is_cpp_abstract_declarator(kind: &str) -> bool {
9780    matches!(
9781        kind,
9782        "abstract_pointer_declarator"
9783            | "abstract_reference_declarator"
9784            | "abstract_array_declarator"
9785            | "abstract_function_declarator"
9786            | "abstract_parenthesized_declarator"
9787    )
9788}
9789
9790fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
9791    node.child_by_field_name("declarator").or_else(|| {
9792        if is_cpp_abstract_declarator(node.kind()) {
9793            let mut cursor = node.walk();
9794            node.named_children(&mut cursor)
9795                .find(|child| is_cpp_abstract_declarator(child.kind()))
9796        } else {
9797            // Named declarators historically use their last named child when
9798            // tree-sitter omits the field. Keep that broad fallback for
9799            // attributed, variadic, and recovered named shapes.
9800            last_named_child(node)
9801        }
9802    })
9803}
9804
9805fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
9806    match node.kind() {
9807        "identifier" | "field_identifier" => String::new(),
9808        "pointer_declarator" | "abstract_pointer_declarator" => {
9809            let inner = cpp_nested_declarator(node)
9810                .map(|child| cpp_declarator_suffix_without_name(child, source))
9811                .unwrap_or_default();
9812            format!("*{inner}")
9813        }
9814        "reference_declarator" | "abstract_reference_declarator" => {
9815            let inner = cpp_nested_declarator(node)
9816                .map(|child| cpp_declarator_suffix_without_name(child, source))
9817                .unwrap_or_default();
9818            let reference = node
9819                .children(&mut node.walk())
9820                .find(|child| matches!(child.kind(), "&" | "&&"))
9821                .map(|child| node_text(child, source))
9822                .unwrap_or("&");
9823            format!("{reference}{inner}")
9824        }
9825        "array_declarator" | "abstract_array_declarator" => {
9826            let inner = cpp_nested_declarator(node)
9827                .map(|child| cpp_declarator_suffix_without_name(child, source))
9828                .unwrap_or_default();
9829            let size = node
9830                .child_by_field_name("size")
9831                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
9832                .unwrap_or_default();
9833            format!("{inner}[{size}]")
9834        }
9835        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
9836            let inner = cpp_nested_declarator(node);
9837            inner
9838                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
9839                .unwrap_or_default()
9840        }
9841        "function_declarator" | "abstract_function_declarator" => {
9842            let inner = cpp_nested_declarator(node)
9843                .map(|child| cpp_declarator_suffix_without_name(child, source))
9844                .unwrap_or_default();
9845            let params = node
9846                .child_by_field_name("parameters")
9847                .map(|child| cpp_parameter_signature(child, source))
9848                .unwrap_or_else(|| "()".to_string());
9849            format!("{inner}{params}")
9850        }
9851        _ => {
9852            let text = normalize_cpp_whitespace(node_text(node, source));
9853            let name = extract_declarator_name(node, source);
9854            if name.is_empty() {
9855                text
9856            } else {
9857                text.replace(&name, "").trim().to_string()
9858            }
9859        }
9860    }
9861}
9862
9863fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
9864    collapse_cpp_whitespace(
9865        suffix
9866            .trim()
9867            .trim_start_matches("->")
9868            .trim_start_matches('{')
9869            .trim_end_matches(';'),
9870    )
9871}
9872
9873pub fn normalize_cpp_whitespace(value: &str) -> String {
9874    collapse_cpp_whitespace(value)
9875}
9876
9877fn normalize_cpp_type_text(value: &str) -> String {
9878    collapse_cpp_whitespace(value)
9879        .replace(", ", ",")
9880        .replace(" <", "<")
9881        .replace("< ", "<")
9882        .replace(" >", ">")
9883}
9884
9885fn collapse_cpp_whitespace(value: &str) -> String {
9886    let mut result = String::new();
9887    let mut prev_space = false;
9888    for ch in value.chars() {
9889        if ch.is_whitespace() {
9890            if !prev_space {
9891                result.push(' ');
9892            }
9893            prev_space = true;
9894        } else {
9895            result.push(ch);
9896            prev_space = false;
9897        }
9898    }
9899    result.trim().to_string()
9900}
9901
9902pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
9903    node_source_text(node, source)
9904}
9905
9906pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
9907    walk_named_tree_preorder(node, true, |node| {
9908        match node.kind() {
9909            "type_identifier" | "identifier" | "qualified_identifier" => {
9910                let text = node_text(node, source).trim();
9911                if !text.is_empty() {
9912                    identifiers.insert(text.to_string());
9913                }
9914            }
9915            _ => {}
9916        }
9917        WalkControl::Continue
9918    });
9919}
9920
9921fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
9922    node.child_by_field_name("body").or_else(|| {
9923        let mut cursor = node.walk();
9924        node.named_children(&mut cursor).find(|child| {
9925            matches!(
9926                child.kind(),
9927                "declaration_list" | "field_declaration_list" | "enumerator_list"
9928            )
9929        })
9930    })
9931}
9932
9933/// Return a class body's actual closing brace when the parser supplied one.
9934///
9935/// A malformed namespace sentinel can leave a class node carrying unrelated
9936/// parser errors even though its own class body is complete.  `has_error()` is
9937/// therefore too coarse an admission predicate for sentinel ownership.  The
9938/// body list, however, exposes the opening and closing punctuation directly;
9939/// a real (non-missing) final `}` proves that the class did not borrow the
9940/// enclosing namespace's close.  Requiring the body to end before its parent
9941/// container also rejects a recovered node whose body swallowed that outer
9942/// boundary.
9943fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
9944    if !matches!(
9945        node.kind(),
9946        "class_specifier" | "struct_specifier" | "union_specifier"
9947    ) {
9948        return None;
9949    }
9950    let body = cpp_body_node(node)?;
9951    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
9952        return None;
9953    }
9954    let open = body.child(0)?;
9955    let close = body.child(body.child_count().checked_sub(1)?)?;
9956    if open.kind() != "{"
9957        || open.is_missing()
9958        || close.kind() != "}"
9959        || close.is_missing()
9960        || close.end_byte() != body.end_byte()
9961        || body.end_byte() > node.end_byte()
9962        || node
9963            .parent()
9964            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
9965    {
9966        return None;
9967    }
9968    Some(close)
9969}
9970
9971fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
9972    if node.kind() == "namespace_definition" {
9973        return true;
9974    }
9975    let mut cursor = node.walk();
9976    node.named_children(&mut cursor)
9977        .any(cpp_contains_namespace_definition)
9978}
9979
9980struct CppNestedNamespaceSentinel<'tree> {
9981    function: Node<'tree>,
9982    body: Node<'tree>,
9983    namespace_components: Vec<String>,
9984}
9985
9986/// Owned structural recovery metadata for a namespace-sentinel region.
9987///
9988/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
9989/// instead of the namespace/class scopes that the declaration visitor restores.
9990/// The inverted usage walk has the original CST, so it needs the same ownership
9991/// evidence without borrowing parser nodes across its file scan.  Keep this
9992/// descriptor deliberately source-range based: callers can match a reference
9993/// node by containment and then resolve its structured type spelling in the
9994/// recovered class scope.
9995#[derive(Debug, Clone)]
9996pub struct CppSentinelRecoveredOwner {
9997    pub range: Range,
9998    /// Start of the qualified owner name (`btree<P>::method`).  A leading
9999    /// return type before this byte is looked up from the namespace; parameters,
10000    /// trailing returns, and the body use the member owner scope.
10001    pub owner_name_start_byte: usize,
10002    /// Number of leading components belonging to the namespace rather than
10003    /// the qualified class owner.  A leading return type is looked up before
10004    /// every owner component, not merely before the innermost class.
10005    pub namespace_component_count: usize,
10006    pub scope_components: Vec<String>,
10007}
10008
10009#[derive(Debug, Clone)]
10010pub struct CppSentinelRecoveredClass {
10011    pub namespace_range: Range,
10012    pub namespace_scope_components: Vec<String>,
10013    pub class_range: Range,
10014    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
10015    pub scope_components: Vec<String>,
10016    /// Qualified out-of-line member definitions owned by this class.  Their
10017    /// ranges may extend beyond `class_range` when the malformed sentinel
10018    /// swallowed the namespace close and left definitions as function siblings.
10019    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
10020}
10021
10022/// Resolve the lexical scope restored for a node in a malformed
10023/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
10024/// outrank class spans, which in turn outrank the surviving namespace body.
10025/// The class ancestor suffix is recovered from the original CST so nested
10026/// members keep their complete `Outer::Inner` owner chain.
10027pub fn cpp_sentinel_recovered_scope_for_node(
10028    node: Node<'_>,
10029    source: &str,
10030    recovered_classes: &[CppSentinelRecoveredClass],
10031) -> Option<Vec<String>> {
10032    let contains =
10033        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
10034    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
10035    for recovered in recovered_classes {
10036        for owner in recovered
10037            .owner_ranges
10038            .iter()
10039            .filter(|owner| contains(owner.range))
10040        {
10041            let replace = best_owner.is_none_or(|existing| {
10042                owner.range.end_byte.saturating_sub(owner.range.start_byte)
10043                    < existing
10044                        .range
10045                        .end_byte
10046                        .saturating_sub(existing.range.start_byte)
10047            });
10048            if replace {
10049                best_owner = Some(owner);
10050            }
10051        }
10052    }
10053    if let Some(owner) = best_owner {
10054        let mut scope = owner.scope_components.clone();
10055        if node.start_byte() < owner.owner_name_start_byte {
10056            scope.truncate(owner.namespace_component_count);
10057        }
10058        return Some(scope);
10059    }
10060
10061    let class = recovered_classes
10062        .iter()
10063        .filter(|recovered| contains(recovered.class_range))
10064        .min_by_key(|recovered| {
10065            recovered
10066                .class_range
10067                .end_byte
10068                .saturating_sub(recovered.class_range.start_byte)
10069        });
10070    let class_scope = class.is_some();
10071    let mut scope = if let Some(class) = class {
10072        class.scope_components.clone()
10073    } else {
10074        let namespace = recovered_classes
10075            .iter()
10076            .filter(|recovered| contains(recovered.namespace_range))
10077            .min_by_key(|recovered| {
10078                recovered
10079                    .namespace_range
10080                    .end_byte
10081                    .saturating_sub(recovered.namespace_range.start_byte)
10082            })?;
10083        let mut scope = namespace.namespace_scope_components.clone();
10084        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
10085        let common_prefix = scope
10086            .iter()
10087            .zip(&parser_namespace)
10088            .take_while(|(recovered, parser)| recovered == parser)
10089            .count();
10090        scope.extend(parser_namespace.into_iter().skip(common_prefix));
10091        scope
10092    };
10093    if class_scope {
10094        let mut ancestor_components = Vec::new();
10095        let mut ancestor = node.parent();
10096        while let Some(current) = ancestor {
10097            if matches!(
10098                current.kind(),
10099                "class_specifier" | "struct_specifier" | "union_specifier"
10100            ) && let Some(name) = current.child_by_field_name("name")
10101                && let Some(name_components) = cpp_name_components(name, source)
10102            {
10103                ancestor_components.push(
10104                    name_components
10105                        .into_iter()
10106                        .map(|component| component.name)
10107                        .collect::<Vec<_>>(),
10108                );
10109            }
10110            ancestor = current.parent();
10111        }
10112        ancestor_components.reverse();
10113        let base_len = scope.len();
10114        for component in ancestor_components.into_iter().flatten() {
10115            if scope.len() >= base_len && scope.last() == Some(&component) {
10116                continue;
10117            }
10118            scope.push(component);
10119        }
10120    }
10121    Some(scope)
10122}
10123
10124struct CppSentinelFragmentedClassTail<'tree> {
10125    class_node: Node<'tree>,
10126    template_node: Option<Node<'tree>>,
10127    name: String,
10128    raw_supertypes: Option<Vec<String>>,
10129    fragmented: FragmentedExportBody,
10130    consumed_start: usize,
10131}
10132
10133struct CppSentinelFragmentedClassErrorPrefix<'tree> {
10134    name: String,
10135    open: Node<'tree>,
10136    raw_supertypes: Option<Vec<String>>,
10137}
10138
10139struct CppSentinelDirectBodyClassRegion {
10140    namespace_components: Vec<String>,
10141    class_start: usize,
10142    class_start_line: usize,
10143    class_close_end: usize,
10144    class_close_line: usize,
10145    name: String,
10146}
10147
10148fn cpp_sentinel_body_class_candidate<'tree>(
10149    child: Node<'tree>,
10150) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
10151    if matches!(
10152        child.kind(),
10153        "class_specifier" | "struct_specifier" | "union_specifier"
10154    ) {
10155        return Some((child, None));
10156    }
10157    if child.kind() != "template_declaration" {
10158        if child.kind() == "declaration" {
10159            return Some((first_class_like_child(child)?, None));
10160        }
10161        return None;
10162    }
10163    let mut cursor = child.walk();
10164    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
10165        if matches!(
10166            candidate.kind(),
10167            "class_specifier" | "struct_specifier" | "union_specifier"
10168        ) {
10169            Some(candidate)
10170        } else if candidate.kind() == "declaration" {
10171            first_class_like_child(candidate)
10172        } else {
10173            None
10174        }
10175    })?;
10176    Some((class_node, Some(child)))
10177}
10178
10179/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
10180/// namespace-sentinel body when a later member macro ends the bogus sentinel
10181/// function before the real class close. The anonymous class/open tokens and
10182/// direct identifier are the structural proof; a retained direct close would
10183/// be an ordinary malformed class rather than the fragmented tail handled here.
10184fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
10185    node: Node<'tree>,
10186    source: &str,
10187) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
10188    let name = malformed_class_error_owner_name(node, source)?;
10189    let mut cursor = node.walk();
10190    let children = node.children(&mut cursor).collect::<Vec<_>>();
10191    let keyword = children.first()?;
10192    let open_index = children.iter().position(|child| child.kind() == "{")?;
10193    if children[open_index + 1..]
10194        .iter()
10195        .any(|child| child.kind() == "}")
10196    {
10197        return None;
10198    }
10199    let raw_supertypes =
10200        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
10201    Some(CppSentinelFragmentedClassErrorPrefix {
10202        name,
10203        open: children[open_index],
10204        raw_supertypes,
10205    })
10206}
10207
10208fn cpp_sentinel_direct_body_class_candidate<'tree>(
10209    child: Node<'tree>,
10210) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
10211    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
10212        return Some(candidate);
10213    }
10214    if child.kind() != "template_declaration" {
10215        return None;
10216    }
10217    let mut cursor = child.walk();
10218    let wrapper = child
10219        .named_children(&mut cursor)
10220        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
10221    Some((first_class_like_child(wrapper)?, Some(child)))
10222}
10223
10224fn cpp_sentinel_direct_namespace_components(
10225    function: Node<'_>,
10226    body: Node<'_>,
10227    source: &str,
10228) -> Option<Vec<String>> {
10229    let mut cursor = function.walk();
10230    let children = function
10231        .named_children(&mut cursor)
10232        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
10233        .collect::<Vec<_>>();
10234    let sentinel_index = children.iter().rposition(|child| {
10235        direct_identifier_name(*child, source)
10236            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
10237    })?;
10238    let mut identifiers = Vec::new();
10239    let mut stack = children[sentinel_index + 1..]
10240        .iter()
10241        .rev()
10242        .copied()
10243        .collect::<Vec<_>>();
10244    while let Some(current) = stack.pop() {
10245        if let Some(name) = direct_identifier_name(current, source) {
10246            identifiers.push(name);
10247            continue;
10248        }
10249        let mut cursor = current.walk();
10250        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
10251        stack.extend(children.into_iter().rev());
10252    }
10253    let [keyword, namespace] = identifiers.as_slice() else {
10254        return None;
10255    };
10256    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
10257        .then(|| vec![namespace.clone()])
10258}
10259
10260fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
10261    let mut sibling = class_semicolon.next_named_sibling();
10262    let namespace_close = loop {
10263        let Some(current) = sibling else {
10264            return false;
10265        };
10266        sibling = current.next_named_sibling();
10267        if current.kind() != "comment" {
10268            break current;
10269        }
10270    };
10271    if !cpp_is_stray_close_brace(namespace_close, source) {
10272        return false;
10273    }
10274    loop {
10275        let Some(current) = sibling else {
10276            return false;
10277        };
10278        sibling = current.next_named_sibling();
10279        if current.kind() == "comment" {
10280            continue;
10281        }
10282        return direct_identifier_name(current, source)
10283            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
10284    }
10285}
10286
10287fn cpp_sentinel_macro_body_class_region<'tree>(
10288    node: Node<'tree>,
10289    source: &str,
10290    ancestry: &ParentIndex<'tree>,
10291) -> Option<CppSentinelDirectBodyClassRegion> {
10292    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
10293        return None;
10294    };
10295    if node.kind() != "function_definition" || !node.has_error() {
10296        return None;
10297    }
10298    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
10299    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
10300    let mut cursor = body.walk();
10301    let candidates = body
10302        .named_children(&mut cursor)
10303        .filter_map(cpp_sentinel_direct_body_class_candidate)
10304        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
10305        .collect::<Vec<_>>();
10306    let [(class_node, template_node)] = candidates.as_slice() else {
10307        return None;
10308    };
10309    let original_body = cpp_body_node(*class_node)?;
10310    let name = class_like_name(*class_node, source, ancestry)?;
10311    if name.is_empty() || cpp_export_macro_token(&name) {
10312        return None;
10313    }
10314
10315    let mut sibling = node.next_named_sibling();
10316    let (class_close_start, class_close_end, class_close_line) = loop {
10317        let current = sibling?;
10318        let next = current.next_named_sibling();
10319        if cpp_is_stray_close_brace(current, source)
10320            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
10321        {
10322            let semicolon = next.expect("checked above");
10323            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
10324                return None;
10325            }
10326            break (
10327                current.start_byte(),
10328                semicolon.end_byte(),
10329                semicolon.end_position().row + 1,
10330            );
10331        }
10332        sibling = next;
10333    };
10334    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
10335    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
10336    let root = tree.root_node();
10337    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
10338    // The region reparse is its own tree, so it needs its own parent index;
10339    // the caller's index answers nothing about these nodes.
10340    let reparsed_ancestry = ParentIndex::new(root);
10341    let reparsed =
10342        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
10343    if reparsed.name != name
10344        || reparsed.declaration_node.start_byte() != class_node.start_byte()
10345        || reparsed.body.start_byte() != original_body.start_byte()
10346        || class_close_start <= reparsed.body.end_byte()
10347        || class_close_end <= class_node.end_byte()
10348    {
10349        return None;
10350    }
10351    Some(CppSentinelDirectBodyClassRegion {
10352        namespace_components,
10353        class_start: reparse_start,
10354        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
10355            node.start_position().row + 1
10356        }),
10357        class_close_end,
10358        class_close_line,
10359        name,
10360    })
10361}
10362
10363/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
10364/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
10365///
10366/// The parser puts the namespace opener and the malformed function in one root
10367/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
10368/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
10369/// the malformed function must begin with an all-caps type, then an ERROR whose
10370/// sole identifier is `namespace`, followed by the inner namespace identifier
10371/// and a compound body; and that body must contain a complete named class or a
10372/// structurally fragmented class prefix. A text reparse cannot prove any of
10373/// those ownership boundaries.
10374fn cpp_nested_namespace_sentinel<'tree>(
10375    node: Node<'tree>,
10376    source: &str,
10377    ancestry: &ParentIndex<'tree>,
10378) -> Option<CppNestedNamespaceSentinel<'tree>> {
10379    if !node.has_error() {
10380        return None;
10381    }
10382
10383    let (function, mut namespace_components) = if node.kind() == "ERROR" {
10384        let mut cursor = node.walk();
10385        let functions = node
10386            .named_children(&mut cursor)
10387            .filter(|child| child.kind() == "function_definition")
10388            .collect::<Vec<_>>();
10389        let [function] = functions.as_slice() else {
10390            return None;
10391        };
10392        if !function.has_error() {
10393            return None;
10394        }
10395        let mut cursor = node.walk();
10396        let children = node.children(&mut cursor).collect::<Vec<_>>();
10397        let function_index = children
10398            .iter()
10399            .position(|child| same_node(*child, *function))?;
10400        let [outer_keyword, outer_name, outer_open] =
10401            children.get(function_index.checked_sub(3)?..function_index)?
10402        else {
10403            return None;
10404        };
10405        if outer_keyword.kind() != "namespace"
10406            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
10407            || outer_open.kind() != "{"
10408        {
10409            return None;
10410        }
10411        (
10412            *function,
10413            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
10414        )
10415    } else if node.kind() == "function_definition" {
10416        let declaration_list = node.parent()?;
10417        let namespace = declaration_list.parent()?;
10418        if declaration_list.kind() != "declaration_list"
10419            || namespace.kind() != "namespace_definition"
10420            || namespace.child_by_field_name("body") != Some(declaration_list)
10421        {
10422            return None;
10423        }
10424        (node, Vec::new())
10425    } else {
10426        return None;
10427    };
10428
10429    let mut cursor = function.walk();
10430    let named = function
10431        .named_children(&mut cursor)
10432        .filter(|child| child.kind() != "comment")
10433        .collect::<Vec<_>>();
10434    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
10435        return None;
10436    };
10437    if first_type.kind() != "type_identifier" {
10438        return None;
10439    }
10440    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
10441    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
10442        return None;
10443    }
10444    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
10445        return None;
10446    }
10447    let inner_keyword = inner_error.named_child(0)?;
10448    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
10449        return None;
10450    }
10451    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
10452        return None;
10453    }
10454    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
10455    if inner_name.is_empty() || body.kind() != "compound_statement" {
10456        return None;
10457    }
10458    namespace_components.push(inner_name);
10459
10460    let mut cursor = body.walk();
10461    let has_complete_class = body.named_children(&mut cursor).any(|child| {
10462        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
10463            cpp_body_node(class_node).is_some()
10464                && class_like_name(class_node, source, ancestry)
10465                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
10466        })
10467    });
10468    if !has_complete_class
10469        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
10470    {
10471        return None;
10472    }
10473
10474    Some(CppNestedNamespaceSentinel {
10475        function,
10476        body: *body,
10477        namespace_components,
10478    })
10479}
10480
10481/// Recognize a namespace-begin sentinel directly beneath the translation unit.
10482///
10483/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
10484/// function whose type is the sentinel, whose declarator is the structured
10485/// qualified name `namespace::a::b`, and whose body contains the namespace
10486/// items. Declaration indexing already reparses this bounded region. The
10487/// inverse scanner retains the original tree, so recover the same namespace
10488/// components from the declarator fields for its lexical-scope metadata.
10489fn cpp_root_namespace_sentinel<'tree>(
10490    node: Node<'tree>,
10491    source: &str,
10492    ancestry: &ParentIndex<'tree>,
10493) -> Option<CppNestedNamespaceSentinel<'tree>> {
10494    if node.kind() != "function_definition"
10495        || !node.has_error()
10496        || node.parent()?.kind() != "translation_unit"
10497    {
10498        return None;
10499    }
10500    let first_type = node.child_by_field_name("type")?;
10501    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
10502    if first_type.kind() != "type_identifier"
10503        || sentinel.is_empty()
10504        || !cpp_export_macro_token(&sentinel)
10505    {
10506        return None;
10507    }
10508    let declarator = node.child_by_field_name("declarator")?;
10509    let body = node.child_by_field_name("body")?;
10510    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
10511        return None;
10512    }
10513    let mut cursor = node.walk();
10514    let named = node
10515        .named_children(&mut cursor)
10516        .filter(|child| child.kind() != "comment")
10517        .collect::<Vec<_>>();
10518    let [named_type, named_declarator, named_body] = named.as_slice() else {
10519        return None;
10520    };
10521    if !same_node(*named_type, first_type)
10522        || !same_node(*named_declarator, declarator)
10523        || !same_node(*named_body, body)
10524    {
10525        return None;
10526    }
10527    let mut declarator_components = Vec::new();
10528    let mut valid_components = true;
10529    walk_named_tree_preorder(declarator, true, |component| {
10530        if !matches!(
10531            component.kind(),
10532            "identifier" | "namespace_identifier" | "type_identifier"
10533        ) {
10534            return WalkControl::Continue;
10535        }
10536        let Some(component) = canonical_cpp_qualified_component(component, source) else {
10537            valid_components = false;
10538            return WalkControl::Break;
10539        };
10540        declarator_components.push(component.name);
10541        WalkControl::SkipChildren
10542    });
10543    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
10544        return None;
10545    }
10546    declarator_components.remove(0);
10547    let namespace_components = declarator_components;
10548    if namespace_components.is_empty()
10549        || namespace_components
10550            .iter()
10551            .any(|component| component.is_empty() || cpp_export_macro_token(component))
10552    {
10553        return None;
10554    }
10555
10556    let mut cursor = body.walk();
10557    let has_complete_class = body.named_children(&mut cursor).any(|child| {
10558        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
10559            cpp_body_node(class_node).is_some()
10560                && class_like_name(class_node, source, ancestry)
10561                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
10562        })
10563    });
10564    if !has_complete_class
10565        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
10566    {
10567        return None;
10568    }
10569
10570    Some(CppNestedNamespaceSentinel {
10571        function: node,
10572        body,
10573        namespace_components,
10574    })
10575}
10576
10577/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
10578/// malformed namespace-sentinel function.  The recovery is deliberately
10579/// structural: the class must be a direct body item, its own class node must be
10580/// erroneous and end before a unique anonymous `}` in the enclosing
10581/// declaration-list, and that namespace's next sibling must be a standalone
10582/// `;`.  The complete interior must pass the existing member-shaped reparse
10583/// gate. This avoids source brace scans and does not borrow a close from an
10584/// unrelated later declaration.
10585fn cpp_sentinel_fragmented_class_tail<'tree>(
10586    function: Node<'tree>,
10587    body: Node<'tree>,
10588    source: &str,
10589    ancestry: &ParentIndex<'tree>,
10590) -> Option<CppSentinelFragmentedClassTail<'tree>> {
10591    let mut cursor = body.walk();
10592    let candidates = body
10593        .named_children(&mut cursor)
10594        .filter_map(|child| {
10595            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
10596                let class_body = cpp_body_node(class_node)?;
10597                if !class_node.has_error() {
10598                    return None;
10599                }
10600                let name = class_like_name(class_node, source, ancestry)?;
10601                let raw_supertypes =
10602                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
10603                        .then(|| extract_cpp_supertypes(class_node, source));
10604                return Some((
10605                    class_node,
10606                    template_node,
10607                    name,
10608                    class_body,
10609                    class_body.start_byte().checked_add(1)?,
10610                    raw_supertypes,
10611                ));
10612            }
10613            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
10614            Some((
10615                child,
10616                None,
10617                prefix.name,
10618                prefix.open,
10619                prefix.open.end_byte(),
10620                prefix.raw_supertypes,
10621            ))
10622        })
10623        .collect::<Vec<_>>();
10624    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
10625        candidates.as_slice()
10626    else {
10627        return None;
10628    };
10629    if name.is_empty() || cpp_export_macro_token(name) {
10630        return None;
10631    }
10632
10633    let (close, semicolon) =
10634        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
10635
10636    let reparse_end = close.start_byte();
10637    if *reparse_start >= reparse_end {
10638        return None;
10639    }
10640    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
10641    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
10642        return None;
10643    }
10644    let class_range = Range {
10645        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
10646        end_byte: semicolon.end_byte(),
10647        start_line: template_node.map_or(class_node.start_position().row, |node| {
10648            node.start_position().row
10649        }) + 1,
10650        end_line: semicolon.end_position().row + 1,
10651    };
10652    Some(CppSentinelFragmentedClassTail {
10653        class_node: *class_node,
10654        template_node: *template_node,
10655        name: name.clone(),
10656        raw_supertypes: raw_supertypes.clone(),
10657        fragmented: FragmentedExportBody {
10658            reparse_start: *reparse_start,
10659            reparse_end,
10660            class_range,
10661        },
10662        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
10663    })
10664}
10665
10666/// Recover the class and out-of-line owner scopes from every malformed
10667/// namespace-sentinel region in `root`.
10668///
10669/// This is the shared structural counterpart to
10670/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
10671/// reuses the visitor's sentinel/class admission predicates instead of parsing
10672/// source text a second time.  The returned values own only ranges and names, so
10673/// they can be retained by an inverted usage scan after the tree borrow ends.
10674pub fn cpp_sentinel_recovered_classes(
10675    root: Node<'_>,
10676    source: &str,
10677) -> Vec<CppSentinelRecoveredClass> {
10678    if !root.has_error() {
10679        return Vec::new();
10680    }
10681    // This scan owns its walk of `root`, so it owns the parent index that walk
10682    // asks its ancestor questions through. Built after the error gate: a clean
10683    // tree returns without paying for one.
10684    let ancestry = ParentIndex::new(root);
10685    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
10686    let mut stack = vec![root];
10687    while let Some(current) = stack.pop() {
10688        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
10689            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
10690        {
10691            let namespace_components = cpp_sentinel_recovered_namespace_components(
10692                recovered.function,
10693                &recovered.namespace_components,
10694                source,
10695            );
10696            let fragmented = cpp_sentinel_fragmented_class_tail(
10697                recovered.function,
10698                recovered.body,
10699                source,
10700                &ancestry,
10701            );
10702            let mut class_candidates = Vec::new();
10703            let mut cursor = recovered.body.walk();
10704            for (class_node, template_node) in recovered
10705                .body
10706                .named_children(&mut cursor)
10707                .filter_map(cpp_sentinel_body_class_candidate)
10708            {
10709                let Some(name) = class_like_name(class_node, source, &ancestry) else {
10710                    continue;
10711                };
10712                if name.is_empty() || cpp_export_macro_token(&name) {
10713                    continue;
10714                }
10715                let is_fragmented = fragmented
10716                    .as_ref()
10717                    .is_some_and(|tail| same_node(tail.class_node, class_node));
10718                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
10719                    continue;
10720                }
10721                let class_range = if is_fragmented {
10722                    fragmented
10723                        .as_ref()
10724                        .map(|tail| tail.fragmented.class_range)
10725                        .expect("fragmented class range is present when class matches")
10726                } else {
10727                    cpp_declaration_range(template_node.unwrap_or(class_node))
10728                };
10729                class_candidates.push((class_range, name));
10730            }
10731            if let Some(fragmented) = fragmented
10732                .as_ref()
10733                .filter(|tail| tail.class_node.kind() == "ERROR")
10734            {
10735                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
10736            }
10737
10738            let mut owner_ranges =
10739                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
10740            cpp_sentinel_extend_unique_owner_ranges(
10741                &mut owner_ranges,
10742                cpp_sentinel_recovered_sibling_owner_ranges(
10743                    recovered.function,
10744                    &namespace_components,
10745                    source,
10746                ),
10747            );
10748            for (class_range, name) in class_candidates {
10749                push_cpp_sentinel_recovered_class(
10750                    &mut recovered_classes,
10751                    cpp_declaration_range(recovered.body),
10752                    &namespace_components,
10753                    class_range,
10754                    name,
10755                    &owner_ranges,
10756                );
10757            }
10758
10759            if let Some(declaration_list) = recovered
10760                .function
10761                .parent()
10762                .filter(|parent| parent.kind() == "declaration_list")
10763            {
10764                let outer_namespace =
10765                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
10766                push_cpp_sentinel_sibling_classes(
10767                    &mut recovered_classes,
10768                    declaration_list,
10769                    recovered.function,
10770                    &outer_namespace,
10771                    source,
10772                    &ancestry,
10773                );
10774            }
10775        } else if let Some(region) =
10776            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
10777        {
10778            let namespace_components = cpp_sentinel_recovered_namespace_components(
10779                current,
10780                &region.namespace_components,
10781                source,
10782            );
10783            let owner_container = current
10784                .parent()
10785                .filter(|parent| parent.kind() == "declaration_list")
10786                .unwrap_or(current);
10787            let owner_ranges =
10788                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
10789            push_cpp_sentinel_recovered_class(
10790                &mut recovered_classes,
10791                cpp_declaration_range(owner_container),
10792                &namespace_components,
10793                Range {
10794                    start_byte: region.class_start,
10795                    end_byte: region.class_close_end,
10796                    start_line: region.class_start_line,
10797                    end_line: region.class_close_line,
10798                },
10799                region.name,
10800                &owner_ranges,
10801            );
10802        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
10803            // A generic sentinel-prefixed class can be reduced as a malformed
10804            // function/ERROR without the explicit `namespace X` token pair.
10805            // Reuse the declaration visitor's bounded reparse and retain only
10806            // the recovered class identity/range here.
10807            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
10808                region;
10809            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
10810                continue;
10811            };
10812            let root = tree.root_node();
10813            let template_node = cpp_sentinel_reparsed_leading_template(root);
10814            // A region reparse is its own tree and needs its own parent index.
10815            let reparsed_ancestry = ParentIndex::new(root);
10816            let Some(reparsed_class) =
10817                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
10818            else {
10819                continue;
10820            };
10821            let class_node = reparsed_class.declaration_node;
10822            let name = reparsed_class.name;
10823            let namespace_components =
10824                cpp_sentinel_recovered_namespace_components(current, &[], source);
10825            let owner_container = current
10826                .parent()
10827                .filter(|parent| parent.kind() == "declaration_list")
10828                .unwrap_or(current);
10829            let mut owner_ranges =
10830                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
10831            cpp_sentinel_extend_unique_owner_ranges(
10832                &mut owner_ranges,
10833                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
10834            );
10835            push_cpp_sentinel_recovered_class(
10836                &mut recovered_classes,
10837                cpp_declaration_range(owner_container),
10838                &namespace_components,
10839                Range {
10840                    start_byte: class_start,
10841                    end_byte: close_end,
10842                    start_line: class_node.start_position().row + 1,
10843                    end_line: class_node.end_position().row + 1,
10844                },
10845                name,
10846                &owner_ranges,
10847            );
10848            if owner_container.kind() == "declaration_list" {
10849                push_cpp_sentinel_sibling_classes(
10850                    &mut recovered_classes,
10851                    owner_container,
10852                    current,
10853                    &namespace_components,
10854                    source,
10855                    &ancestry,
10856                );
10857            }
10858        }
10859
10860        let mut cursor = current.walk();
10861        stack.extend(current.named_children(&mut cursor));
10862    }
10863    // A shallower sentinel can expose nested classes as apparent namespace
10864    // siblings even after a deeper sentinel proves that a containing class
10865    // owns their ranges. Drop those shadow descriptors; scope recovery starts
10866    // from the proven containing class and appends parser-visible class
10867    // ancestors, preserving the full `Outer::Inner` chain.
10868    let shadowed = recovered_classes
10869        .iter()
10870        .map(|candidate| {
10871            recovered_classes.iter().any(|container| {
10872                container.class_range.start_byte <= candidate.class_range.start_byte
10873                    && container.class_range.end_byte >= candidate.class_range.end_byte
10874                    && container.class_range != candidate.class_range
10875                    && container.namespace_scope_components.len()
10876                        > candidate.namespace_scope_components.len()
10877                    && container
10878                        .namespace_scope_components
10879                        .starts_with(&candidate.namespace_scope_components)
10880            })
10881        })
10882        .collect::<Vec<_>>();
10883    let mut index = 0usize;
10884    recovered_classes.retain(|_| {
10885        let keep = !shadowed[index];
10886        index += 1;
10887        keep
10888    });
10889    recovered_classes
10890}
10891
10892/// A flat sentinel can swallow the first class while leaving later classes and
10893/// their out-of-line definitions as ordinary declaration-list siblings.  Once
10894/// the malformed class proves the sentinel envelope, retain those structurally
10895/// complete sibling classes under the same surviving namespace so every member
10896/// owner in the region uses one recovery contract.
10897fn push_cpp_sentinel_sibling_classes<'tree>(
10898    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
10899    declaration_list: Node<'tree>,
10900    sentinel_node: Node<'tree>,
10901    namespace_components: &[String],
10902    source: &str,
10903    ancestry: &ParentIndex<'tree>,
10904) {
10905    let owner_ranges =
10906        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
10907    let namespace_range = cpp_declaration_range(declaration_list);
10908    let mut cursor = declaration_list.walk();
10909    for (class_node, template_node) in declaration_list
10910        .named_children(&mut cursor)
10911        .filter(|child| !same_node(*child, sentinel_node))
10912        .filter_map(cpp_sentinel_body_class_candidate)
10913    {
10914        let Some(name) = class_like_name(class_node, source, ancestry) else {
10915            continue;
10916        };
10917        if name.is_empty()
10918            || cpp_export_macro_token(&name)
10919            || cpp_complete_class_body_close(class_node).is_none()
10920        {
10921            continue;
10922        }
10923        push_cpp_sentinel_recovered_class(
10924            recovered_classes,
10925            namespace_range,
10926            namespace_components,
10927            cpp_declaration_range(template_node.unwrap_or(class_node)),
10928            name,
10929            &owner_ranges,
10930        );
10931    }
10932}
10933
10934fn push_cpp_sentinel_recovered_class(
10935    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
10936    namespace_range: Range,
10937    namespace_components: &[String],
10938    class_range: Range,
10939    name: String,
10940    owner_ranges: &[CppSentinelRecoveredOwner],
10941) {
10942    let mut scope_components = namespace_components.to_vec();
10943    scope_components.push(name);
10944    let owner_ranges = owner_ranges
10945        .iter()
10946        .filter(|owner| owner.scope_components.starts_with(&scope_components))
10947        .cloned()
10948        .collect::<Vec<_>>();
10949    if recovered_classes.iter().any(|existing| {
10950        existing.class_range == class_range && existing.scope_components == scope_components
10951    }) {
10952        return;
10953    }
10954    recovered_classes.push(CppSentinelRecoveredClass {
10955        namespace_range,
10956        namespace_scope_components: namespace_components.to_vec(),
10957        class_range,
10958        scope_components,
10959        owner_ranges,
10960    });
10961}
10962
10963fn cpp_sentinel_recovered_namespace_components(
10964    function: Node<'_>,
10965    recovered_components: &[String],
10966    source: &str,
10967) -> Vec<String> {
10968    let mut ancestor_components = Vec::new();
10969    let mut ancestor = function.parent();
10970    while let Some(current) = ancestor {
10971        if current.kind() == "namespace_definition"
10972            && let Some(name_node) = current.child_by_field_name("name")
10973            && let Some(components) = cpp_name_components(name_node, source)
10974        {
10975            ancestor_components.push(
10976                components
10977                    .into_iter()
10978                    .map(|component| component.name)
10979                    .collect::<Vec<_>>(),
10980            );
10981        }
10982        ancestor = current.parent();
10983    }
10984    ancestor_components.reverse();
10985    let mut ancestors = ancestor_components
10986        .into_iter()
10987        .flatten()
10988        .collect::<Vec<_>>();
10989
10990    let overlap = (0..=ancestors.len().min(recovered_components.len()))
10991        .rev()
10992        .find(|length| {
10993            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
10994        })
10995        .unwrap_or(0);
10996    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
10997    ancestors
10998}
10999
11000fn cpp_sentinel_recovered_owner_ranges(
11001    body: Node<'_>,
11002    namespace_components: &[String],
11003    source: &str,
11004) -> Vec<CppSentinelRecoveredOwner> {
11005    let mut owners = Vec::new();
11006    walk_named_tree_preorder(body, true, |node| {
11007        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
11008    });
11009    owners
11010}
11011
11012fn cpp_sentinel_collect_owner_range(
11013    node: Node<'_>,
11014    namespace_components: &[String],
11015    source: &str,
11016    owners: &mut Vec<CppSentinelRecoveredOwner>,
11017) -> WalkControl {
11018    if node.kind() != "function_definition" {
11019        return WalkControl::Continue;
11020    }
11021    let Some(function_declarator) = extract_function_declarator(node) else {
11022        return WalkControl::Continue;
11023    };
11024    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
11025        return WalkControl::Continue;
11026    };
11027    let Some(mut components) = cpp_name_components(name_node, source) else {
11028        return WalkControl::Continue;
11029    };
11030    if components.len() <= 1 {
11031        return WalkControl::Continue;
11032    }
11033    components.pop();
11034    let mut owner_components = components
11035        .into_iter()
11036        .map(|component| component.name)
11037        .collect::<Vec<_>>();
11038    let overlap = (0..=namespace_components.len().min(owner_components.len()))
11039        .rev()
11040        .find(|length| {
11041            owner_components[..*length]
11042                == namespace_components[namespace_components.len().saturating_sub(*length)..]
11043        })
11044        .unwrap_or(0);
11045    let mut scope_components = namespace_components.to_vec();
11046    scope_components.extend(owner_components.drain(overlap..));
11047    if scope_components.len() <= namespace_components.len() {
11048        return WalkControl::Continue;
11049    }
11050    let range = cpp_declaration_range(node);
11051    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
11052        existing.range == range && existing.scope_components == scope_components
11053    }) {
11054        owners.push(CppSentinelRecoveredOwner {
11055            range,
11056            owner_name_start_byte: name_node.start_byte(),
11057            namespace_component_count: namespace_components.len(),
11058            scope_components,
11059        });
11060    }
11061    WalkControl::Continue
11062}
11063
11064fn cpp_sentinel_extend_unique_owner_ranges(
11065    owners: &mut Vec<CppSentinelRecoveredOwner>,
11066    additional: Vec<CppSentinelRecoveredOwner>,
11067) {
11068    for owner in additional {
11069        if !owners.iter().any(|existing| {
11070            existing.range == owner.range && existing.scope_components == owner.scope_components
11071        }) {
11072            owners.push(owner);
11073        }
11074    }
11075}
11076
11077fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
11078    if node.kind() != "ERROR" || node.named_child_count() != 1 {
11079        return false;
11080    }
11081    let Some(end_name) = node.named_child(0) else {
11082        return false;
11083    };
11084    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
11085        return false;
11086    }
11087    let mut cursor = node.walk();
11088    node.children(&mut cursor)
11089        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
11090}
11091
11092/// Collect owner definitions that the malformed sentinel left as later
11093/// declaration-list siblings. Parser-visible namespace siblings are a hard
11094/// boundary: their declarations must keep their own lexical namespace.
11095fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
11096    parent: Node<'_>,
11097    sentinel_node: Node<'_>,
11098    namespace_components: &[String],
11099    source: &str,
11100) -> Vec<CppSentinelRecoveredOwner> {
11101    let mut owners = Vec::new();
11102    let mut after_sentinel = false;
11103    let mut cursor = parent.walk();
11104    for child in parent.named_children(&mut cursor) {
11105        if !after_sentinel {
11106            if same_node(child, sentinel_node) {
11107                after_sentinel = true;
11108            }
11109            continue;
11110        }
11111        walk_named_tree_preorder(child, true, |node| {
11112            if node.kind() == "namespace_definition" {
11113                return WalkControl::SkipChildren;
11114            }
11115            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
11116        });
11117    }
11118    owners
11119}
11120
11121/// Collect owner definitions after a malformed namespace, stopping only at
11122/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
11123/// enclosing container is not trusted to belong to the recovered namespace.
11124fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
11125    parent: Node<'_>,
11126    sentinel_node: Node<'_>,
11127    namespace_components: &[String],
11128    source: &str,
11129) -> Option<Vec<CppSentinelRecoveredOwner>> {
11130    let mut owners = Vec::new();
11131    let mut after_namespace = false;
11132    let mut cursor = parent.walk();
11133    for child in parent.named_children(&mut cursor) {
11134        if !after_namespace {
11135            if same_node(child, sentinel_node) {
11136                after_namespace = true;
11137            }
11138            continue;
11139        }
11140        if cpp_sentinel_namespace_end(child, source) {
11141            return Some(owners);
11142        }
11143        walk_named_tree_preorder(child, true, |node| {
11144            if node.kind() == "namespace_definition" {
11145                return WalkControl::SkipChildren;
11146            }
11147            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
11148        });
11149    }
11150    None
11151}
11152
11153fn cpp_sentinel_recovered_sibling_owner_ranges(
11154    sentinel_node: Node<'_>,
11155    namespace_components: &[String],
11156    source: &str,
11157) -> Vec<CppSentinelRecoveredOwner> {
11158    let Some(declaration_list) = sentinel_node
11159        .parent()
11160        .filter(|parent| parent.kind() == "declaration_list")
11161    else {
11162        return Vec::new();
11163    };
11164    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
11165        declaration_list,
11166        sentinel_node,
11167        namespace_components,
11168        source,
11169    );
11170
11171    let Some(namespace) = declaration_list
11172        .parent()
11173        .filter(|parent| parent.kind() == "namespace_definition")
11174    else {
11175        return owners;
11176    };
11177    let Some(outer_parent) = namespace.parent() else {
11178        return owners;
11179    };
11180    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
11181        outer_parent,
11182        namespace,
11183        namespace_components,
11184        source,
11185    ) {
11186        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
11187    }
11188    owners
11189}
11190
11191fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
11192    let mut current = function_declarator.child_by_field_name("declarator")?;
11193    loop {
11194        if matches!(
11195            current.kind(),
11196            "qualified_identifier"
11197                | "scoped_identifier"
11198                | "scoped_type_identifier"
11199                | "identifier"
11200                | "field_identifier"
11201                | "operator_name"
11202                | "destructor_name"
11203                | "literal_operator_name"
11204        ) {
11205            return Some(current);
11206        }
11207        current = current
11208            .child_by_field_name("declarator")
11209            .or_else(|| current.child_by_field_name("name"))
11210            .or_else(|| last_named_child(current))?;
11211    }
11212}
11213
11214fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
11215    match node.kind() {
11216        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11217            let mut components = match node.child_by_field_name("scope") {
11218                Some(scope) => cpp_name_components(scope, source)?,
11219                None => Vec::new(),
11220            };
11221            let name = node.child_by_field_name("name")?;
11222            components.push(canonical_cpp_qualified_component(name, source)?);
11223            Some(components)
11224        }
11225        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
11226    }
11227}
11228
11229fn cpp_sentinel_fragment_boundary<'tree>(
11230    function: Node<'tree>,
11231    class_node: Node<'tree>,
11232    class_body: Node<'tree>,
11233    source: &str,
11234) -> Option<(Node<'tree>, Node<'tree>)> {
11235    let declaration_list = function.parent()?;
11236    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
11237        return None;
11238    }
11239    let namespace = declaration_list.parent()?;
11240    if namespace.kind() != "namespace_definition"
11241        || namespace.child_by_field_name("body") != Some(declaration_list)
11242    {
11243        return None;
11244    }
11245    let mut cursor = declaration_list.walk();
11246    let closes = declaration_list
11247        .children(&mut cursor)
11248        .filter(|child| {
11249            !child.is_named()
11250                && child.kind() == "}"
11251                && child.start_byte() >= function.end_byte()
11252                && child.start_byte() > class_node.end_byte()
11253                && child.start_byte() > class_body.start_byte()
11254        })
11255        .collect::<Vec<_>>();
11256    let [close] = closes.as_slice() else {
11257        return None;
11258    };
11259    let semicolon = namespace.next_named_sibling()?;
11260    if !cpp_is_stray_semicolon(semicolon, source)
11261        || close.end_byte() != namespace.end_byte()
11262        || semicolon.start_byte() < namespace.end_byte()
11263    {
11264        return None;
11265    }
11266    Some((*close, semicolon))
11267}
11268
11269/// Detect the bogus declaration/function tree that tree-sitter recovers for a
11270/// region prefixed by an object-like macro sentinel the parser cannot see
11271/// (issue #941), and return the byte range `[start, end)` of the swallowed
11272/// declaration interior to reparse.
11273///
11274/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
11275/// `function_definition` whose first non-comment named child is the sentinel
11276/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
11277/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
11278/// the real items.
11279/// `start` is the end of the sentinel identifier -- everything after it is the
11280/// genuine source. `end` is the node's end, extended across any trailing empty
11281/// `;` statement the mis-parse displaced past the node (the class/struct closing
11282/// semicolon), so the reparse sees a complete, brace-balanced item.
11283///
11284/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
11285/// node (`has_error`). Unknown annotation/export macros can make a real callable
11286/// error-recovered even though tree-sitter still preserves its declarator, so a
11287/// preserved callable is admitted only when a displaced class keyword precedes
11288/// that declarator. The clean-reparse-to-items gate in
11289/// `cpp_reparsed_items_are_indexable` is the final arbiter.
11290/// Return the reparse start and, when present, the structurally recovered class
11291/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
11292/// separately from the reparse start because an opaque template-declaration
11293/// macro may precede it.
11294fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
11295    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
11296    {
11297        return None;
11298    }
11299    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
11300    // retain a valid function declarator despite the unknown export macro making
11301    // the outer node erroneous. Remember that declarator for the ordering gate
11302    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
11303    // class keyword precedes a spurious callable assembled from a later member.
11304    let mut declarator_cursor = node.walk();
11305    let preserved_callable = node
11306        .children_by_field_name("declarator", &mut declarator_cursor)
11307        .find_map(extract_function_declarator);
11308    // Leading documentation comments are attached to the malformed
11309    // `function_definition` as named children.  They are not part of the
11310    // sentinel prefix, so select the first non-comment child structurally
11311    // rather than requiring the sentinel to be child zero.  This is the shape
11312    // emitted for nlohmann/json's `basic_json`: its class documentation comment
11313    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
11314    // envelope otherwise ends at the first nested union.
11315    let mut cursor = node.walk();
11316    let first = node
11317        .named_children(&mut cursor)
11318        .find(|child| child.kind() != "comment")?;
11319    if first.kind() != "type_identifier" {
11320        return None;
11321    }
11322    let sentinel = normalize_cpp_whitespace(node_text(first, source));
11323    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
11324        return None;
11325    }
11326    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
11327    // makes the trailing sentinel of one region and the leading sentinel of the
11328    // next both land as bare macro-token identifiers ahead of the real content.
11329    // Advance past every leading macro-token identifier so the reparse begins at
11330    // genuine source rather than another sentinel that would re-form the bogus
11331    // shape and fail the reparse gate.
11332    let mut start = first.end_byte();
11333    let mut after_first = false;
11334    let mut cursor = node.walk();
11335    for child in node.named_children(&mut cursor) {
11336        if !after_first {
11337            if same_node(child, first) {
11338                after_first = true;
11339            }
11340            continue;
11341        }
11342        if matches!(child.kind(), "identifier" | "type_identifier")
11343            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
11344        {
11345            start = child.end_byte();
11346        } else {
11347            break;
11348        }
11349    }
11350    // An additional opaque template-declaration macro before a class can be
11351    // folded into the bogus function's qualified declarator.  In that shape
11352    // the macro is not a direct sibling we can skip above; tree-sitter exposes
11353    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
11354    // Reparse from that keyword (or a real preceding `template` keyword) so the
11355    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
11356    // a class nested in a genuine sentinel-wrapped namespace lies after the
11357    // body opening and must not change the established region start.
11358    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
11359    let mut class_start = None;
11360    let mut template_start = None;
11361    let mut stack = vec![node];
11362    while let Some(current) = stack.pop() {
11363        if current.start_byte() >= prefix_end {
11364            continue;
11365        }
11366        if matches!(
11367            current.kind(),
11368            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
11369        ) {
11370            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
11371                "class" | "struct" | "union" | "enum" => {
11372                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
11373                        seen.min(current.start_byte())
11374                    }));
11375                }
11376                "template" => {
11377                    template_start =
11378                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
11379                            seen.min(current.start_byte())
11380                        }));
11381                }
11382                _ => {}
11383            }
11384        }
11385        let mut cursor = current.walk();
11386        stack.extend(current.children(&mut cursor));
11387    }
11388    if preserved_callable.is_some_and(|callable| {
11389        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
11390    }) {
11391        return None;
11392    }
11393    if let Some(class_start) = class_start {
11394        start = template_start
11395            .filter(|template_start| *template_start < class_start)
11396            .unwrap_or(class_start);
11397    }
11398    Some((start, class_start))
11399}
11400
11401/// Locate a sentinel-prefixed class whose malformed declaration was split across
11402/// root-level siblings. The true class close is represented structurally as a
11403/// lone `}` error followed by the class's displaced `;`; nested method/body
11404/// errors are not direct siblings of the sentinel node and therefore cannot
11405/// satisfy this pair.
11406fn cpp_sentinel_macro_class_region<'tree>(
11407    node: Node<'tree>,
11408    source: &str,
11409) -> Option<(usize, usize, usize, usize, usize, usize)> {
11410    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
11411        return None;
11412    };
11413    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
11414        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
11415        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
11416    if class_start >= body_open_start {
11417        return None;
11418    }
11419    let sibling_close = {
11420        let mut sibling = node.next_named_sibling();
11421        let mut found = None;
11422        while let Some(current) = sibling {
11423            let next = current.next_named_sibling();
11424            if cpp_is_stray_close_brace(current, source)
11425                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
11426            {
11427                let semicolon = next.expect("checked above");
11428                found = Some((
11429                    current.start_byte(),
11430                    semicolon.end_byte(),
11431                    semicolon.end_position().row + 1,
11432                ));
11433                break;
11434            }
11435            sibling = next;
11436        }
11437        found
11438    };
11439    // A stray `};` sibling is this class's close only when the bounded reparse
11440    // agrees the first body-bearing class ENDS there. When the malformed
11441    // envelope swallowed the class's true close, the scan can promote a much
11442    // later scope's close instead -- in protobuf-generated headers
11443    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
11444    // `struct TableStruct_*` paired with the first message class's `};`, making
11445    // the recovered "class body" span whole `namespace {}` blocks and minting
11446    // namespace-scope classes as nested members of the recovered class, which
11447    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
11448    // (#2275). On disagreement, fall through to the suffix-reparse boundary
11449    // below, which derives the close from the class node's own balanced body
11450    // range.
11451    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
11452        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
11453            return false;
11454        };
11455        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
11456        // A region reparse is its own tree and needs its own parent index.
11457        let reparsed_ancestry = ParentIndex::new(tree.root_node());
11458        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
11459            tree.root_node(),
11460            template_node,
11461            source,
11462            &reparsed_ancestry,
11463        ) else {
11464            return false;
11465        };
11466        let body = reparsed_class.body;
11467        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
11468    });
11469    let (class_close_start, class_close_end, class_close_line) =
11470        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
11471            (class_close_start, class_close_end, class_close_line)
11472        } else {
11473            // When the malformed envelope itself is an ERROR, tree-sitter can
11474            // leave the class's balanced close in the source while promoting
11475            // all following members to siblings. Reparse the complete suffix
11476            // and use the first body-bearing class node's own field range as
11477            // the partition boundary. This keeps balancing in tree-sitter and
11478            // preserves the source's original byte offsets.
11479            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
11480            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
11481            // A region reparse is its own tree and needs its own parent index.
11482            let reparsed_ancestry = ParentIndex::new(tree.root_node());
11483            let reparsed_class = cpp_sentinel_reparsed_class(
11484                tree.root_node(),
11485                template_node,
11486                source,
11487                &reparsed_ancestry,
11488            )?;
11489            let body = reparsed_class.body;
11490            let class_close_end = body.end_byte();
11491            let class_close_start = class_close_end.checked_sub(1)?;
11492            let class_close_line = body.end_position().row + 1;
11493            (class_close_start, class_close_end, class_close_line)
11494        };
11495    if class_close_start <= class_start {
11496        return None;
11497    }
11498
11499    // Reparse only far enough to expose the class body opening. This is a
11500    // structured check that the candidate really begins with a body-bearing
11501    // class-like item; the original malformed tree cannot provide that node.
11502    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
11503    let class_root = tree.root_node();
11504    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
11505    // A region reparse is its own tree and needs its own parent index.
11506    let reparsed_ancestry = ParentIndex::new(class_root);
11507    let reparsed_class =
11508        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
11509    let body = reparsed_class.body;
11510    // The class body opening must agree with the malformed wrapper's structured
11511    // body field. This rejects an inner nested class while permitting later
11512    // members to remain fragmented as root-level siblings in the bounded parse.
11513    if body.start_byte() != body_open_start {
11514        return None;
11515    }
11516    let body_start = body.start_byte().checked_add(1)?;
11517    (body_start < class_close_start).then_some((
11518        reparse_start,
11519        class_start,
11520        body_start,
11521        class_close_start,
11522        class_close_end,
11523        class_close_line,
11524    ))
11525}
11526
11527/// Find the `{` token immediately following the class/struct/union/enum token
11528/// at `class_start` in the malformed tree. The token is anonymous in the C++
11529/// grammar, so this deliberately walks all children (not only named children)
11530/// and relies on sibling structure rather than source-text searching.
11531fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
11532    let mut stack = vec![node];
11533    while let Some(current) = stack.pop() {
11534        if current.start_byte() == class_start
11535            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
11536        {
11537            let mut sibling = current.next_sibling();
11538            while let Some(candidate) = sibling {
11539                if candidate.kind() == "{" {
11540                    return Some(candidate.start_byte());
11541                }
11542                sibling = candidate.next_sibling();
11543            }
11544        }
11545        let mut cursor = current.walk();
11546        stack.extend(current.children(&mut cursor));
11547    }
11548    None
11549}
11550
11551/// The class body that tree-sitter displaced out of a sentinel-prefixed
11552/// declaration and left as the malformed node's next sibling.
11553///
11554/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
11555/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
11556/// the last child of that `ERROR` and its `{` opens a sibling
11557/// `compound_statement` instead. The body is still the malformed tree's own
11558/// structured token, which is what the caller's `body.start_byte() !=
11559/// body_open_start` agreement check needs; it just is not reachable by walking
11560/// forward from the class token inside the node.
11561fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
11562    node.next_named_sibling()
11563        .filter(|sibling| sibling.kind() == "compound_statement")
11564}
11565
11566fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
11567    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
11568    let mut end = if class_start.is_some() {
11569        cpp_macro_prefixed_class_end(source, start)?
11570    } else {
11571        node.end_byte()
11572    };
11573    if class_start.is_none()
11574        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
11575    {
11576        end = end.max(namespace_end);
11577    }
11578    let mut sibling = node.next_named_sibling();
11579    while let Some(current) = sibling {
11580        if !cpp_is_stray_semicolon(current, source) {
11581            break;
11582        }
11583        end = current.end_byte();
11584        sibling = current.next_named_sibling();
11585    }
11586    (start < end).then_some((start, end))
11587}
11588
11589/// Extend a sentinel reparse through a following namespace that tree-sitter
11590/// flattened into the sentinel node's sibling list.
11591///
11592/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
11593/// unknown macro becomes a false function return type and consumes the first
11594/// namespace body. A second `namespace detail` then loses its enclosing node:
11595/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
11596/// attaches its declarations to the surrounding error tree. Reparse from that
11597/// structured keyword so tree-sitter, rather than a source-text brace scan,
11598/// supplies the complete namespace boundary.
11599fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
11600    let mut sibling = node.next_sibling();
11601    let keyword = loop {
11602        let candidate = sibling?;
11603        sibling = candidate.next_sibling();
11604        if candidate.kind() != "comment" {
11605            break candidate;
11606        }
11607    };
11608    if keyword.kind() != "namespace" {
11609        return None;
11610    }
11611    let name = loop {
11612        let candidate = sibling?;
11613        sibling = candidate.next_sibling();
11614        if candidate.kind() != "comment" {
11615            break candidate;
11616        }
11617    };
11618    if cpp_namespace_name_components(name, source).is_empty() {
11619        return None;
11620    }
11621    let open = loop {
11622        let candidate = sibling?;
11623        sibling = candidate.next_sibling();
11624        if candidate.kind() != "comment" {
11625            break candidate;
11626        }
11627    };
11628    if open.kind() != "{" {
11629        return None;
11630    }
11631
11632    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
11633    let root = tree.root_node();
11634    let mut cursor = root.walk();
11635    let namespace = root
11636        .named_children(&mut cursor)
11637        .find(|candidate| candidate.kind() != "comment")?;
11638    (namespace.kind() == "namespace_definition"
11639        && namespace.start_byte() == keyword.start_byte()
11640        && namespace.child_by_field_name("body").is_some())
11641    .then_some(namespace.end_byte())
11642}
11643
11644/// Parse the source suffix beginning at a structurally recovered class/template
11645/// keyword and return the end of its first body-bearing class item.  The parser,
11646/// rather than a brace scanner, owns nested-body balancing.  This is needed when
11647/// the original error tree truncates the class and scatters later members as
11648/// top-level siblings.
11649fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
11650    let tree = cpp_reparse_region_items(source, start, source.len())?;
11651    let root = tree.root_node();
11652    let mut cursor = root.walk();
11653    for item in root.named_children(&mut cursor) {
11654        if item.end_byte() <= start || item.kind() == "comment" {
11655            continue;
11656        }
11657        let mut stack = vec![item];
11658        while let Some(current) = stack.pop() {
11659            if matches!(
11660                current.kind(),
11661                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
11662            ) && cpp_body_node(current).is_some()
11663            {
11664                return Some(current.end_byte());
11665            }
11666            let mut cursor = current.walk();
11667            stack.extend(current.named_children(&mut cursor));
11668        }
11669        // The recovered prefix is required to begin with the class item.  If
11670        // the first real item is something else, fail closed rather than skip
11671        // arbitrary source looking for a later class.
11672        return None;
11673    }
11674    None
11675}
11676
11677/// An empty `;` statement: the displaced closing semicolon of a struct/class that
11678/// the sentinel mis-parse split off past the bogus function node.
11679fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
11680    node.kind() == "expression_statement"
11681        && node.named_child_count() == 0
11682        && node_text(node, source).trim() == ";"
11683}
11684
11685/// Recover the real field name when a leading object-like annotation macro
11686/// displaces a qualified type into tree-sitter's bit-field recovery shape.
11687///
11688/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
11689/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
11690/// `bitfield_clause` containing an error plus an assignment.  The assignment's
11691/// left field is the only structured declaration name in that malformed tail.
11692/// A real bit-field is excluded by the all-caps macro type and required error.
11693fn recovered_macro_qualified_field_declarators<'tree>(
11694    node: Node<'tree>,
11695    source: &str,
11696) -> Option<Vec<Node<'tree>>> {
11697    if node.kind() != "field_declaration" {
11698        return None;
11699    }
11700    let macro_type = node.child_by_field_name("type")?;
11701    if macro_type.kind() != "type_identifier"
11702        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11703    {
11704        return None;
11705    }
11706    let pseudo_declarator = node.child_by_field_name("declarator")?;
11707    if pseudo_declarator.kind() != "field_identifier" {
11708        return None;
11709    }
11710    let mut cursor = node.walk();
11711    let clause = node
11712        .named_children(&mut cursor)
11713        .find(|child| child.kind() == "bitfield_clause")?;
11714    if !(0..clause.named_child_count()).any(|index| {
11715        clause
11716            .named_child(index)
11717            .is_some_and(|child| child.kind() == "ERROR")
11718    }) {
11719        return None;
11720    }
11721    let mut recovered = Vec::new();
11722    let mut stack = vec![clause];
11723    while let Some(current) = stack.pop() {
11724        if current.kind() == "assignment_expression"
11725            && let Some(left) = current.child_by_field_name("left")
11726            && extract_variable_name(left, source).is_some()
11727        {
11728            recovered.push(left);
11729            break;
11730        }
11731        let mut cursor = current.walk();
11732        stack.extend(current.named_children(&mut cursor));
11733    }
11734    if recovered.is_empty() {
11735        return None;
11736    }
11737    let mut cursor = node.walk();
11738    recovered.extend(
11739        node.children_by_field_name("declarator", &mut cursor)
11740            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
11741    );
11742    Some(recovered)
11743}
11744
11745/// Recover a macro-qualified constructor that tree-sitter represents as one
11746/// field declaration. The constructor call remains inside the direct recovery
11747/// error, while each member initializer becomes a false function declarator.
11748/// The class owner proves the constructor name and lets the caller ignore those
11749/// initializer declarators.
11750fn recovered_macro_qualified_constructor_call<'tree>(
11751    node: Node<'tree>,
11752    class_name: &str,
11753    source: &str,
11754) -> Option<Node<'tree>> {
11755    if node.kind() != "field_declaration" {
11756        return None;
11757    }
11758    let macro_type = node.child_by_field_name("type")?;
11759    if macro_type.kind() != "type_identifier"
11760        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11761    {
11762        return None;
11763    }
11764    let mut cursor = node.walk();
11765    let bitfield = node
11766        .named_children(&mut cursor)
11767        .find(|child| child.kind() == "bitfield_clause")?;
11768    let error = bitfield
11769        .named_child(0)
11770        .filter(|child| child.kind() == "ERROR")?;
11771    let mut stack = vec![error];
11772    while let Some(current) = stack.pop() {
11773        if current.kind() == "call_expression"
11774            && current
11775                .child_by_field_name("function")
11776                .is_some_and(|function| node_text(function, source) == class_name)
11777            && current
11778                .child_by_field_name("arguments")
11779                .is_some_and(|arguments| arguments.kind() == "argument_list")
11780        {
11781            return Some(current);
11782        }
11783        let mut cursor = current.walk();
11784        stack.extend(current.named_children(&mut cursor));
11785    }
11786    None
11787}
11788
11789/// Recover a macro-qualified member function declaration that tree-sitter
11790/// represents as a pseudo-field. An object-like export macro before a qualified
11791/// return type can displace the namespace and type into an ERROR/bitfield
11792/// recovery, leaving the callable as a structured `call_expression`.
11793///
11794/// The caller must route this shape before ordinary declarator classification;
11795/// otherwise the displaced namespace identifier is published as a field.
11796fn recovered_macro_qualified_function_call<'tree>(
11797    node: Node<'tree>,
11798    source: &str,
11799) -> Option<Node<'tree>> {
11800    if node.kind() != "field_declaration" {
11801        return None;
11802    }
11803    let macro_type = node.child_by_field_name("type")?;
11804    if macro_type.kind() != "type_identifier"
11805        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11806    {
11807        return None;
11808    }
11809    let declarator = node.child_by_field_name("declarator")?;
11810    if declarator.kind() != "field_identifier" {
11811        return None;
11812    }
11813    let mut cursor = node.walk();
11814    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11815    if !named.iter().any(|child| {
11816        child.kind() == "storage_class_specifier"
11817            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
11818    }) {
11819        return None;
11820    }
11821    let bitfield = named
11822        .iter()
11823        .find(|child| child.kind() == "bitfield_clause")?;
11824    let mut bitfield_cursor = bitfield.walk();
11825    let payload = bitfield
11826        .named_children(&mut bitfield_cursor)
11827        .collect::<Vec<_>>();
11828    let [displaced_error, call] = payload.as_slice() else {
11829        return None;
11830    };
11831    if displaced_error.kind() != "ERROR"
11832        || displaced_error.named_child_count() != 1
11833        || displaced_error
11834            .named_child(0)
11835            .is_none_or(|child| child.kind() != "identifier")
11836        || call.kind() != "call_expression"
11837        || call
11838            .child_by_field_name("function")
11839            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
11840        || call
11841            .child_by_field_name("arguments")
11842            .is_none_or(|arguments| arguments.kind() != "argument_list")
11843    {
11844        return None;
11845    }
11846    Some(*call)
11847}
11848
11849fn recovered_macro_qualified_function_parameters(
11850    arguments: Node<'_>,
11851    source: &str,
11852) -> Option<(String, Vec<String>)> {
11853    if arguments.kind() != "argument_list" {
11854        return None;
11855    }
11856    let mut cursor = arguments.walk();
11857    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
11858    if named.is_empty() {
11859        return Some(("()".to_string(), Vec::new()));
11860    }
11861    let mut types = Vec::new();
11862    let mut labels = Vec::new();
11863    let mut index = 0;
11864    while index < named.len() {
11865        let parameter_type = named[index];
11866        let parameter_name = named.get(index + 1).copied()?;
11867        if !matches!(
11868            parameter_type.kind(),
11869            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
11870        ) || parameter_name.kind() != "ERROR"
11871            || parameter_name.named_child_count() != 1
11872            || parameter_name
11873                .named_child(0)
11874                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
11875        {
11876            return None;
11877        }
11878        let parameter_name = parameter_name.named_child(0)?;
11879        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
11880        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
11881        index += 2;
11882    }
11883    Some((format!("({})", types.join(", ")), labels))
11884}
11885
11886/// Recognize the phantom field tree-sitter emits for a macro-qualified
11887/// function return type.  For example,
11888/// `static API result_type ThresholdForSmallA() { ... }` can become a
11889/// `field_declaration` (`API` as the type and `result_type` as a field name)
11890/// followed by a clean `function_definition` for `ThresholdForSmallA`.
11891///
11892/// Keep this predicate entirely tied to the CST envelope: the type must be an
11893/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
11894/// the declaration must carry a missing semicolon rather than a real one, and
11895/// the immediate named sibling must expose a function declarator.  A real
11896/// macro-decorated field with an explicit semicolon therefore remains a field.
11897pub fn recovered_macro_return_type_node<'tree>(
11898    node: Node<'tree>,
11899    source: &str,
11900) -> Option<Node<'tree>> {
11901    if node.kind() != "field_declaration" {
11902        return None;
11903    }
11904    let macro_type = node.child_by_field_name("type")?;
11905    if macro_type.kind() != "type_identifier"
11906        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11907    {
11908        return None;
11909    }
11910    let declarator = node.child_by_field_name("declarator")?;
11911    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
11912        return None;
11913    }
11914    let mut has_missing_semicolon = false;
11915    let mut has_real_semicolon = false;
11916    for index in 0..node.child_count() {
11917        let Some(child) = node.child(index) else {
11918            continue;
11919        };
11920        if child.kind() != ";" {
11921            continue;
11922        }
11923        if child.is_missing() {
11924            has_missing_semicolon = true;
11925        } else {
11926            has_real_semicolon = true;
11927        }
11928    }
11929    if !has_missing_semicolon || has_real_semicolon {
11930        return None;
11931    }
11932    let mut next = node.next_named_sibling();
11933    while next.is_some_and(|sibling| sibling.kind() == "comment") {
11934        next = next.and_then(|sibling| sibling.next_named_sibling());
11935    }
11936    let next = next?;
11937    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
11938        return None;
11939    }
11940    let function_declarator = next.child_by_field_name("declarator")?;
11941    extract_function_declarator(function_declarator).map(|_| declarator)
11942}
11943
11944/// Whether `name` is a type parameter of a template declaration that lexically
11945/// encloses `node`. The malformed macro-return field uses the parameter name as
11946/// its pseudo-declarator; preserving that field is necessary to publish a
11947/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
11948/// ancestors instead of interpreting source text so nested templates and
11949/// parser-recovered regions retain their real lexical scopes.
11950pub(crate) fn cpp_active_template_type_parameter<'tree>(
11951    node: Node<'tree>,
11952    name: &str,
11953    source: &str,
11954    ancestry: &ParentIndex<'tree>,
11955) -> bool {
11956    let mut ancestor = ancestry.parent(node);
11957    while let Some(current) = ancestor {
11958        if current.kind() == "template_declaration"
11959            && let Some(parameters) = current.child_by_field_name("parameters")
11960        {
11961            let mut cursor = parameters.walk();
11962            if parameters.named_children(&mut cursor).any(|parameter| {
11963                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
11964                    && cpp_template_parameter_name(parameter, source)
11965                        .is_some_and(|parameter_name| parameter_name == name)
11966            }) {
11967                return true;
11968            }
11969        }
11970        ancestor = ancestry.parent(current);
11971    }
11972    false
11973}
11974
11975/// Reparse the region `[start, end)` of `source` as C++, confined to the region
11976/// via included ranges so every reparsed node keeps its original byte offset and
11977/// line number. The existing visitors read node text from the original source,
11978/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
11979/// `parse_rust_region_tree` technique.
11980fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
11981    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
11982}
11983
11984fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
11985    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
11986        return None;
11987    }
11988    let semicolon = node.next_sibling()?;
11989    if semicolon.kind() != ";" || semicolon.is_missing() {
11990        return None;
11991    }
11992    let row = node.start_position().row;
11993    let mut start = node.start_byte();
11994    let mut sibling = node.prev_sibling();
11995    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
11996        if previous.kind() == ";" {
11997            break;
11998        }
11999        start = previous.start_byte();
12000        sibling = previous.prev_sibling();
12001    }
12002    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
12003}
12004
12005fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
12006    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
12007        return false;
12008    }
12009    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
12010        return false;
12011    }
12012    let Some(declarator) = (if node.kind() == "function_definition" {
12013        node.child_by_field_name("declarator")
12014            .and_then(extract_function_declarator)
12015    } else {
12016        node.named_child(0)
12017            .filter(|child| child.kind() == "function_declarator")
12018    }) else {
12019        return false;
12020    };
12021    let Some(name) = cpp_function_declarator_name_node(declarator) else {
12022        return false;
12023    };
12024    declarator.start_byte() == node.start_byte()
12025        && name.kind() == "identifier"
12026        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
12027}
12028
12029/// Reparse a fragmented class-body interior while preserving its original byte
12030/// and line offsets, confined to the region by tree-sitter included ranges.
12031///
12032/// This used to materialize the region's whole file prefix as whitespace and
12033/// make the lexer walk it, the technique #1309 replaced on the other reparse
12034/// path: O(file) per fragmented-class recovery, on files that are already
12035/// error-recovered and already slow (#2788). Included ranges give the parser
12036/// the same view -- the region's bytes, at their original offsets and
12037/// line/column positions -- without materializing or lexing anything before it.
12038///
12039/// The equality of the two views is the claim, so a debug build parses both and
12040/// asserts the trees agree node for node.
12041fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
12042    let region = cpp_reparse_region_items(source, start, end);
12043
12044    #[cfg(debug_assertions)]
12045    assert_eq!(
12046        region.as_ref().map(cpp_tree_shape),
12047        cpp_reparse_padded_class_body(source, start, end)
12048            .as_ref()
12049            .map(cpp_tree_shape),
12050        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
12051         prefix produces"
12052    );
12053
12054    region
12055}
12056
12057/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
12058/// replaces, kept as the oracle a debug build asserts every region reparse
12059/// against and as the release-mode parity tests' reference (#2788).
12060#[cfg(any(debug_assertions, test))]
12061fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
12062    if start >= end {
12063        // The included-range parser refuses an empty region; the padded one
12064        // returned a tree holding nothing, which every caller read as "no
12065        // members here".
12066        return None;
12067    }
12068    let bytes = source.as_bytes();
12069    let prefix = bytes.get(..start)?;
12070    let interior = bytes.get(start..end)?;
12071    let mut padded = Vec::with_capacity(end);
12072    padded.extend(
12073        prefix
12074            .iter()
12075            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
12076    );
12077    padded.extend_from_slice(interior);
12078    let padded = String::from_utf8(padded).ok()?;
12079    let mut parser = Parser::new();
12080    parser
12081        .set_language(&tree_sitter_cpp::LANGUAGE.into())
12082        .ok()?;
12083    parser.parse(&padded, None)
12084}
12085
12086/// Every node of `tree` in preorder, by kind, span and position, which is what
12087/// two reparses of one region have to agree on for their callers to read the
12088/// same declarations out of either (#2788).
12089#[cfg(any(debug_assertions, test))]
12090fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
12091    let mut shape = Vec::new();
12092    let mut cursor = tree.root_node().walk();
12093    let mut stack = vec![tree.root_node()];
12094    while let Some(node) = stack.pop() {
12095        shape.push((
12096            node.kind(),
12097            node.start_byte(),
12098            node.end_byte(),
12099            node.start_position().row,
12100            node.start_position().column,
12101            node.is_named(),
12102            node.is_missing(),
12103        ));
12104        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
12105        stack.extend(children.into_iter().rev());
12106    }
12107    shape
12108}
12109
12110/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
12111/// reparsed interior is indexed only when every top-level named node is a
12112/// well-formed C++ item (or a comment) and at least one real item is present.
12113/// Expression/statement soup surfaces as a top-level `ERROR` or
12114/// `expression_statement`, neither of which is an item kind, so it is rejected.
12115///
12116/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
12117/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
12118/// swallowed by a preceding dangling sentinel) reparses to a real
12119/// `namespace_definition` whose body still holds a bogus `function_definition`,
12120/// so the subtree legitimately carries an error. Container items are admitted
12121/// even with an internal error; the inner bogus function is recovered recursively
12122/// when `visit_function_definition` walks it. Each recursion strips at least one
12123/// leading sentinel, so the region strictly shrinks and recovery terminates.
12124///
12125/// A top-level `function_definition` is the one place we stay strict: it is
12126/// admitted only when it is clean or is itself a sentinel candidate. A function
12127/// that has an error and is not a sentinel is a real callable with a broken body,
12128/// so we refuse the whole reparse and let the ordinary path handle it (preserving
12129/// its real return type rather than re-deriving an implicit one).
12130fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
12131    let mut cursor = root.walk();
12132    let mut saw_item = false;
12133    for child in root.named_children(&mut cursor) {
12134        match child.kind() {
12135            "comment" => {}
12136            "function_definition" => {
12137                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
12138                    return false;
12139                }
12140                saw_item = true;
12141            }
12142            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
12143            _ => return false,
12144        }
12145    }
12146    saw_item
12147}
12148
12149/// Robustness gate for a reparsed fragmented multiple-base export class body
12150/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
12151/// kinds a class body produces when reparsed at translation-unit scope: the
12152/// access-specifier label preceding the first member surfaces as a
12153/// `labeled_statement` wrapping that member, and members surface as
12154/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
12155/// Statement or expression soup surfaces as other top-level kinds and is rejected,
12156/// so only a genuinely member-shaped body is ever re-owned as members; anything
12157/// ambiguous falls back to indexing the class alone.
12158fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
12159    if node.kind() != "ERROR" {
12160        return false;
12161    }
12162    let mut stack = Vec::new();
12163    let mut saw_function_declarator = false;
12164    let mut cursor = node.walk();
12165    for child in node.named_children(&mut cursor) {
12166        stack.push(child);
12167    }
12168    while let Some(current) = stack.pop() {
12169        match current.kind() {
12170            // Tree-sitter may wrap adjacent copy-control declarations in a
12171            // nested ERROR. Keep descending only through ERROR wrappers; the
12172            // actual declaration payload must be a function_declarator.
12173            "ERROR" => {
12174                let mut cursor = current.walk();
12175                stack.extend(current.named_children(&mut cursor));
12176            }
12177            "function_declarator" => saw_function_declarator = true,
12178            _ => return false,
12179        }
12180    }
12181    saw_function_declarator
12182}
12183
12184fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
12185    if node.kind() != "ERROR" {
12186        return false;
12187    }
12188    let mut cursor = node.walk();
12189    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12190    let [explicit, constructor_error, destructor] = named.as_slice() else {
12191        return false;
12192    };
12193    let Some(constructor) = constructor_error.named_child(0) else {
12194        return false;
12195    };
12196    let Some(constructor_name) =
12197        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
12198    else {
12199        return false;
12200    };
12201    let Some(destructor_name) =
12202        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
12203    else {
12204        return false;
12205    };
12206    let Some(destroyed_type) = destructor_name.named_child(0) else {
12207        return false;
12208    };
12209    explicit.kind() == "explicit_function_specifier"
12210        && constructor_error.kind() == "ERROR"
12211        && constructor_error.named_child_count() == 1
12212        && constructor.kind() == "function_declarator"
12213        && constructor_name.kind() == "identifier"
12214        && destructor.kind() == "function_declarator"
12215        && destructor_name.kind() == "destructor_name"
12216        && destroyed_type.kind() == "identifier"
12217        && node_text(constructor_name, source) == node_text(destroyed_type, source)
12218}
12219
12220fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
12221    if node.kind() != "compound_statement" {
12222        return false;
12223    }
12224    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
12225        return false;
12226    };
12227    if prefix.kind() == "labeled_statement"
12228        && prefix.named_child(0).is_some_and(|label| {
12229            matches!(
12230                node_text(label, source).trim(),
12231                "public" | "private" | "protected"
12232            )
12233        })
12234    {
12235        return prefix.named_children(&mut prefix.walk()).any(|child| {
12236            child.kind() == "declaration"
12237                && child.has_error()
12238                && child
12239                    .named_children(&mut child.walk())
12240                    .any(cpp_reparsed_member_error_is_indexable)
12241        });
12242    }
12243    // A malformed constructor initializer can be split into a declaration
12244    // followed by its compound body when the class prefix already contains
12245    // realistic members. Keep this admission tied to that exact structured
12246    // declaration/error/body chain rather than accepting arbitrary blocks.
12247    prefix.kind() == "declaration"
12248        && prefix.has_error()
12249        && prefix
12250            .named_children(&mut prefix.walk())
12251            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
12252}
12253
12254fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
12255    if !cpp_reparsed_member_error_is_indexable(node) {
12256        return false;
12257    }
12258    let Some(preproc) = node.next_named_sibling() else {
12259        return false;
12260    };
12261    preproc.kind() == "preproc_if"
12262        && preproc.has_error()
12263        && preproc
12264            .named_children(&mut preproc.walk())
12265            .any(|child| child.kind() == "expression_statement" && child.has_error())
12266        && preproc
12267            .next_named_sibling()
12268            .is_some_and(|body| body.kind() == "compound_statement")
12269}
12270
12271/// Return a function body whose braces and ownership are explicit in the
12272/// reparsed class-member tree. An error below a real function envelope is
12273/// recoverable by the ordinary function visitor; a missing/deferred body is
12274/// not, because accepting it would let statement soup masquerade as a member.
12275fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
12276    if node.kind() != "function_definition" {
12277        return None;
12278    }
12279    let body = node.child_by_field_name("body")?;
12280    if body.kind() != "compound_statement" {
12281        return None;
12282    }
12283    let open = body.child(0)?;
12284    let close = body.child(body.child_count().checked_sub(1)?)?;
12285    if open.kind() != "{"
12286        || open.is_missing()
12287        || close.kind() != "}"
12288        || close.is_missing()
12289        || close.end_byte() != body.end_byte()
12290        || body.end_byte() != node.end_byte()
12291    {
12292        return None;
12293    }
12294    Some(body)
12295}
12296
12297fn cpp_reparsed_member_function_errors_are_in_body(
12298    node: Node<'_>,
12299    body: Node<'_>,
12300    source: &str,
12301) -> bool {
12302    let mut cursor = node.walk();
12303    node.children(&mut cursor).all(|child| {
12304        same_node(child, body)
12305            || cpp_reparsed_member_attribute_error(child, source)
12306            || cpp_reparsed_member_signature_identifier_errors(child)
12307            || (!child.has_error() && !child.is_error() && !child.is_missing())
12308    })
12309}
12310
12311/// A complete callable can still carry parser errors in its signature when a
12312/// project annotation is not part of the C++ grammar (`nonneg int`,
12313/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
12314/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
12315/// leaves inside the already-proven callable envelope; structured statements,
12316/// literals, missing tokens, and other malformed signature payload remain
12317/// rejected.
12318fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
12319    if !node.has_error() && !node.is_error() && !node.is_missing() {
12320        return false;
12321    }
12322    let mut stack = vec![node];
12323    let mut saw_error = false;
12324    while let Some(current) = stack.pop() {
12325        if current.is_missing() {
12326            return false;
12327        }
12328        if current.kind() == "ERROR" {
12329            saw_error = true;
12330            let mut cursor = current.walk();
12331            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
12332            if children
12333                .iter()
12334                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
12335            {
12336                return false;
12337            }
12338            stack.extend(children);
12339            continue;
12340        }
12341        let mut cursor = current.walk();
12342        stack.extend(current.children(&mut cursor));
12343    }
12344    saw_error
12345}
12346
12347fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
12348    node.kind() == "ERROR"
12349        && node.named_child_count() == 1
12350        && node.named_child(0).is_some_and(|attribute| {
12351            attribute.kind() == "identifier"
12352                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
12353        })
12354}
12355
12356/// A C++ attribute placed between a member's declarator and body can make
12357/// tree-sitter expose the callable as
12358/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
12359/// Keep this admission tied to that exact node geometry. In particular, an
12360/// arbitrary ERROR or identifier before a compound statement is not enough.
12361fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
12362    let Some(body) = cpp_reparsed_member_function_body(node) else {
12363        return false;
12364    };
12365    let mut cursor = node.walk();
12366    let named = node
12367        .named_children(&mut cursor)
12368        .filter(|child| child.kind() != "comment")
12369        .collect::<Vec<_>>();
12370    let [type_node, error, attribute, body_node] = named.as_slice() else {
12371        return false;
12372    };
12373    if !same_node(*body_node, body)
12374        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
12375        || attribute.kind() != "identifier"
12376        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
12377        || error.kind() != "ERROR"
12378        || error.named_child_count() != 1
12379    {
12380        return false;
12381    }
12382    error
12383        .named_child(0)
12384        .is_some_and(cpp_reparsed_attribute_callable_declarator)
12385}
12386
12387fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
12388    cpp_structured_type_path(node, source).is_some()
12389        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
12390}
12391
12392fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
12393    let Some(body) = cpp_reparsed_member_function_body(node) else {
12394        return false;
12395    };
12396    let mut cursor = node.walk();
12397    let named = node
12398        .named_children(&mut cursor)
12399        .filter(|child| child.kind() != "comment")
12400        .collect::<Vec<_>>();
12401    let [friend, return_error, declarator, body_node] = named.as_slice() else {
12402        return false;
12403    };
12404    let Some(return_type) = return_error.named_child(0) else {
12405        return false;
12406    };
12407    same_node(*body_node, body)
12408        && friend.kind() == "type_identifier"
12409        && node_text(*friend, source) == "friend"
12410        && return_error.kind() == "ERROR"
12411        && return_error.named_child_count() == 1
12412        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
12413        && extract_function_declarator(*declarator)
12414            .and_then(cpp_function_declarator_name_node)
12415            .is_some()
12416}
12417
12418fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
12419    let Some(body) = cpp_reparsed_member_function_body(node) else {
12420        return false;
12421    };
12422    let mut cursor = node.walk();
12423    let named = node
12424        .named_children(&mut cursor)
12425        .filter(|child| child.kind() != "comment")
12426        .collect::<Vec<_>>();
12427    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
12428        return false;
12429    };
12430    let Some(return_type) = return_error.named_child(0) else {
12431        return false;
12432    };
12433    same_node(*body_node, body)
12434        && prefix
12435            .iter()
12436            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
12437        && attribute.kind() == "type_identifier"
12438        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
12439        && return_error.kind() == "ERROR"
12440        && return_error.named_child_count() == 1
12441        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
12442        && extract_function_declarator(*declarator)
12443            .and_then(cpp_function_declarator_name_node)
12444            .is_some()
12445}
12446
12447/// An included-range reparse that begins inside a malformed class can merge an
12448/// access label and following template member. Tree-sitter then emits the label
12449/// as the `template_type` name, the template parameter list as its arguments,
12450/// an ERROR-wrapped return type, the callable declarator, and its complete
12451/// body. Admit only that exact structured displacement.
12452fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
12453    let Some(body) = cpp_reparsed_member_function_body(node) else {
12454        return false;
12455    };
12456    let mut cursor = node.walk();
12457    let named = node
12458        .named_children(&mut cursor)
12459        .filter(|child| child.kind() != "comment")
12460        .collect::<Vec<_>>();
12461    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
12462        return false;
12463    };
12464    let Some(template_name) = template_type.child_by_field_name("name") else {
12465        return false;
12466    };
12467    let Some(arguments) = template_type.child_by_field_name("arguments") else {
12468        return false;
12469    };
12470    let Some(return_type) = return_error.named_child(0) else {
12471        return false;
12472    };
12473    let mut cursor = template_type.walk();
12474    let template_errors = template_type
12475        .named_children(&mut cursor)
12476        .filter(|child| child.kind() == "ERROR")
12477        .collect::<Vec<_>>();
12478    let [comment_error] = template_errors.as_slice() else {
12479        return false;
12480    };
12481    let mut cursor = comment_error.walk();
12482    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
12483    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
12484        return false;
12485    };
12486    same_node(*body_node, body)
12487        && template_type.kind() == "template_type"
12488        && template_name.kind() == "type_identifier"
12489        && matches!(
12490            node_text(template_name, source).trim(),
12491            "public" | "private" | "protected"
12492        )
12493        && arguments.kind() == "template_argument_list"
12494        && arguments.named_child_count() > 0
12495        && !arguments.has_error()
12496        && !colon.is_named()
12497        && colon.kind() == ":"
12498        && comments.iter().all(|child| child.kind() == "comment")
12499        && !template_keyword.is_named()
12500        && template_keyword.kind() == "template"
12501        && return_error.kind() == "ERROR"
12502        && return_error.named_child_count() == 1
12503        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
12504        && extract_function_declarator(*declarator)
12505            .and_then(cpp_function_declarator_name_node)
12506            .is_some()
12507}
12508
12509/// Return the constructor declaration tree-sitter can merge into an access
12510/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
12511/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
12512/// declaration's apparent type; the callable name must still exactly match the
12513/// recovered class, so unrelated labeled statements are never re-owned.
12514fn cpp_reparsed_preprocessor_constructor<'tree>(
12515    node: Node<'tree>,
12516    class_name: &str,
12517    source: &str,
12518) -> Option<Node<'tree>> {
12519    if node.kind() != "labeled_statement" {
12520        return None;
12521    }
12522    let mut cursor = node.walk();
12523    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12524    let [label, directive_error, declaration] = named.as_slice() else {
12525        return None;
12526    };
12527    if label.kind() != "statement_identifier"
12528        || !matches!(
12529            node_text(*label, source),
12530            "public" | "private" | "protected"
12531        )
12532        || directive_error.kind() != "ERROR"
12533        || directive_error.child_count() != 1
12534        || directive_error
12535            .child(0)
12536            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
12537        || declaration.kind() != "declaration"
12538        || declaration.named_child_count() != 2
12539    {
12540        return None;
12541    }
12542    let apparent_type = declaration.child_by_field_name("type")?;
12543    if apparent_type.kind() != "type_identifier"
12544        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
12545    {
12546        return None;
12547    }
12548    let declarator = declaration.child_by_field_name("declarator")?;
12549    let function = extract_function_declarator(declarator)?;
12550    let name = cpp_function_declarator_name_node(function)?;
12551    (node_text(name, source) == class_name).then_some(*declaration)
12552}
12553
12554fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
12555    if extract_function_declarator(node)
12556        .and_then(cpp_function_declarator_name_node)
12557        .is_some()
12558    {
12559        return true;
12560    }
12561    node.kind() == "init_declarator"
12562        && node
12563            .child_by_field_name("declarator")
12564            .is_some_and(|declarator| declarator.kind() == "identifier")
12565        && node
12566            .child_by_field_name("value")
12567            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
12568}
12569
12570/// Return true for the constrained/attribute form that tree-sitter splits into
12571/// an ERROR declaration, a preprocessor `requires` clause, and a following
12572/// compound statement. The three nodes must remain immediate named siblings;
12573/// this deliberately does not search source text or skip unrelated statements.
12574fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
12575    if node.kind() != "ERROR" || node.named_child_count() != 3 {
12576        return false;
12577    }
12578    let mut cursor = node.walk();
12579    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12580    let [type_node, function_declarator, attribute] = named.as_slice() else {
12581        return false;
12582    };
12583    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
12584        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
12585        || attribute.kind() != "identifier"
12586        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
12587    {
12588        return false;
12589    }
12590    let Some(preproc) =
12591        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
12592    else {
12593        return false;
12594    };
12595    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
12596        .filter(|sibling| sibling.kind() == "compound_statement")
12597    else {
12598        return false;
12599    };
12600    let Some(open) = body.child(0) else {
12601        return false;
12602    };
12603    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
12604        return false;
12605    };
12606    let Some(condition) = preproc.child_by_field_name("condition") else {
12607        return false;
12608    };
12609    let mut cursor = preproc.walk();
12610    let payload = preproc
12611        .named_children(&mut cursor)
12612        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
12613        .collect::<Vec<_>>();
12614    let [requires_statement] = payload.as_slice() else {
12615        return false;
12616    };
12617    let requires_clause = requires_statement.named_child(0);
12618
12619    open.kind() == "{"
12620        && !open.is_missing()
12621        && close.kind() == "}"
12622        && !close.is_missing()
12623        && close.end_byte() == body.end_byte()
12624        && requires_statement.kind() == "expression_statement"
12625        && requires_statement.named_child_count() == 1
12626        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
12627}
12628
12629fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
12630    let mut sibling = node.next_named_sibling();
12631    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
12632        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
12633    }
12634    sibling
12635}
12636
12637fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
12638    let mut sibling = node.prev_named_sibling();
12639    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
12640        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
12641    }
12642    sibling
12643}
12644
12645fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
12646    let Some(preproc) =
12647        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
12648    else {
12649        return false;
12650    };
12651    let Some(error) =
12652        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
12653    else {
12654        return false;
12655    };
12656    cpp_reparsed_attribute_requires_error(error, source)
12657}
12658
12659fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
12660    node: Node<'tree>,
12661    source: &str,
12662) -> Option<Node<'tree>> {
12663    if node.kind() != "ERROR" {
12664        return None;
12665    }
12666    let mut cursor = node.walk();
12667    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12668    let [parameter, macro_name, message] = named.as_slice() else {
12669        return None;
12670    };
12671    let parameter_name = parameter.named_child(0)?;
12672    (parameter.kind() == "type_parameter_declaration"
12673        && parameter_name.kind() == "type_identifier"
12674        && macro_name.kind() == "type_identifier"
12675        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
12676        && message.kind() == "string_literal")
12677        .then_some(parameter_name)
12678}
12679
12680/// Recognize the alternate constraint-macro prefix where tree-sitter retains
12681/// the complete qualified constraint as a fourth child instead of moving it
12682/// into the following function. Keep the gate tied to a two-type template
12683/// constraint that names the declared type parameter.
12684fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
12685    node: Node<'tree>,
12686    source: &str,
12687) -> Option<Node<'tree>> {
12688    if node.kind() != "ERROR" {
12689        return None;
12690    }
12691    let mut cursor = node.walk();
12692    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12693    let [parameter, macro_name, message, constraint] = named.as_slice() else {
12694        return None;
12695    };
12696    let parameter_name = parameter.named_child(0)?;
12697    let constraint_scope = constraint.child_by_field_name("scope")?;
12698    let constraint_template = constraint.child_by_field_name("name")?;
12699    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
12700    let mut argument_cursor = constraint_arguments.walk();
12701    let constraint_types = constraint_arguments
12702        .named_children(&mut argument_cursor)
12703        .collect::<Vec<_>>();
12704    if parameter.kind() != "type_parameter_declaration"
12705        || parameter_name.kind() != "type_identifier"
12706        || macro_name.kind() != "type_identifier"
12707        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
12708        || message.kind() != "string_literal"
12709        || constraint.kind() != "qualified_identifier"
12710        || constraint_scope.kind() != "namespace_identifier"
12711        || !matches!(
12712            constraint_template.kind(),
12713            "template_function" | "template_type"
12714        )
12715        || !matches!(constraint_types.as_slice(), [left, right]
12716            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
12717        || constraint_arguments.has_error()
12718    {
12719        return None;
12720    }
12721    let parameter_text = node_text(parameter_name, source);
12722    let mut stack = constraint_types;
12723    while let Some(current) = stack.pop() {
12724        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
12725            return Some(parameter_name);
12726        }
12727        let mut cursor = current.walk();
12728        stack.extend(current.named_children(&mut cursor));
12729    }
12730    None
12731}
12732
12733fn cpp_reparsed_template_macro_companion_is_indexable(
12734    node: Node<'_>,
12735    parameter_name: Node<'_>,
12736    source: &str,
12737) -> bool {
12738    let Some(body) = cpp_reparsed_member_function_body(node) else {
12739        return false;
12740    };
12741    let mut cursor = node.walk();
12742    let named = node
12743        .named_children(&mut cursor)
12744        .filter(|child| child.kind() != "comment")
12745        .collect::<Vec<_>>();
12746    let [
12747        constraint,
12748        close_error,
12749        storage,
12750        return_error,
12751        declarator,
12752        body_node,
12753    ] = named.as_slice()
12754    else {
12755        return false;
12756    };
12757    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
12758        return false;
12759    };
12760    let Some(constraint_template) = constraint.child_by_field_name("name") else {
12761        return false;
12762    };
12763    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
12764        return false;
12765    };
12766    let Some(return_type) = return_error.named_child(0) else {
12767        return false;
12768    };
12769    let mut cursor = constraint_arguments.walk();
12770    let constraint_types = constraint_arguments
12771        .named_children(&mut cursor)
12772        .collect::<Vec<_>>();
12773    same_node(*body_node, body)
12774        && constraint.kind() == "qualified_identifier"
12775        && constraint_scope.kind() == "namespace_identifier"
12776        && constraint_template.kind() == "template_type"
12777        && matches!(constraint_types.as_slice(), [left, right]
12778            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
12779        && !constraint_arguments.has_error()
12780        && close_error.kind() == "ERROR"
12781        && close_error.named_child_count() == 0
12782        && storage.kind() == "storage_class_specifier"
12783        && return_error.kind() == "ERROR"
12784        && return_error.named_child_count() == 1
12785        && return_type.kind() == "identifier"
12786        && node_text(return_type, source) == node_text(parameter_name, source)
12787        && extract_function_declarator(*declarator)
12788            .and_then(cpp_function_declarator_name_node)
12789            .is_some()
12790}
12791
12792fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
12793    node: Node<'tree>,
12794    parameter_name: Node<'_>,
12795    source: &str,
12796) -> Option<Node<'tree>> {
12797    let body = cpp_reparsed_member_function_body(node)?;
12798    let constraint = node.child_by_field_name("type")?;
12799    let constraint_template = constraint.child_by_field_name("name")?;
12800    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
12801    let mut argument_cursor = constraint_arguments.walk();
12802    let constraint_types = constraint_arguments
12803        .named_children(&mut argument_cursor)
12804        .collect::<Vec<_>>();
12805    if constraint.kind() != "qualified_identifier"
12806        || constraint_template.kind() != "template_type"
12807        || !matches!(constraint_types.as_slice(), [left, right]
12808            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
12809        || constraint_arguments.has_error()
12810        || node
12811            .child_by_field_name("body")
12812            .is_none_or(|candidate| !same_node(candidate, body))
12813    {
12814        return None;
12815    }
12816
12817    let mut cursor = node.walk();
12818    let recovery_errors = node
12819        .named_children(&mut cursor)
12820        .filter(|child| child.kind() == "ERROR")
12821        .collect::<Vec<_>>();
12822    if !recovery_errors
12823        .iter()
12824        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
12825        || !recovery_errors.iter().all(|error| {
12826            error.named_child_count() == 0
12827                || cpp_reparsed_constraint_macro_error(*error, source)
12828                || (error.named_child_count() == 1
12829                    && error
12830                        .named_child(0)
12831                        .is_some_and(|child| child.kind() == "function_declarator"))
12832        })
12833    {
12834        return None;
12835    }
12836
12837    let parameter_text = node_text(parameter_name, source);
12838    let mut declarators = node
12839        .child_by_field_name("declarator")
12840        .and_then(extract_function_declarator)
12841        .into_iter()
12842        .collect::<Vec<_>>();
12843    for error in recovery_errors {
12844        let mut stack = vec![error];
12845        while let Some(current) = stack.pop() {
12846            if current.kind() == "function_declarator" {
12847                declarators.push(current);
12848            }
12849            let mut cursor = current.walk();
12850            stack.extend(current.named_children(&mut cursor));
12851        }
12852    }
12853    declarators.into_iter().find(|declarator| {
12854        cpp_function_declarator_name_node(*declarator)
12855            .is_some_and(|name| name.kind() == "identifier")
12856            && declarator
12857                .child_by_field_name("parameters")
12858                .is_some_and(|parameters| {
12859                    parameters
12860                        .named_children(&mut parameters.walk())
12861                        .filter_map(|parameter| parameter.child_by_field_name("type"))
12862                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
12863                })
12864    })
12865}
12866
12867fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
12868    node: Node<'_>,
12869    parameter_name: Node<'_>,
12870    source: &str,
12871) -> bool {
12872    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
12873}
12874
12875fn cpp_reparsed_template_macro_function_companion_is_indexable(
12876    node: Node<'_>,
12877    parameter_name: Node<'_>,
12878    source: &str,
12879) -> bool {
12880    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
12881        return false;
12882    }
12883    let Some(return_type) = node.child_by_field_name("type") else {
12884        return false;
12885    };
12886    let Some(function_declarator) = node
12887        .child_by_field_name("declarator")
12888        .and_then(extract_function_declarator)
12889    else {
12890        return false;
12891    };
12892    if cpp_function_declarator_name_node(function_declarator).is_none()
12893        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
12894    {
12895        return false;
12896    }
12897    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
12898        return false;
12899    };
12900    let parameter_text = node_text(parameter_name, source);
12901    parameters
12902        .named_children(&mut parameters.walk())
12903        .any(|parameter| {
12904            parameter
12905                .child_by_field_name("type")
12906                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
12907        })
12908}
12909
12910fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
12911    if node.kind() != "ERROR" {
12912        return false;
12913    }
12914    let mut stack = vec![node];
12915    while let Some(current) = stack.pop() {
12916        let macro_shape = match current.kind() {
12917            "call_expression" => current
12918                .child_by_field_name("function")
12919                .zip(current.child_by_field_name("arguments")),
12920            "init_declarator" => current
12921                .child_by_field_name("declarator")
12922                .zip(current.child_by_field_name("value")),
12923            _ => None,
12924        };
12925        if let Some((name, arguments)) = macro_shape
12926            && name.kind() == "identifier"
12927            && arguments.kind() == "argument_list"
12928            && arguments.named_child_count() >= 2
12929            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
12930        {
12931            return true;
12932        }
12933        let mut cursor = current.walk();
12934        stack.extend(current.named_children(&mut cursor));
12935    }
12936    false
12937}
12938
12939fn cpp_recovered_template_macro_constructor<'tree>(
12940    node: Node<'tree>,
12941    source: &str,
12942) -> Option<(Node<'tree>, Node<'tree>)> {
12943    let mut prefix = node.prev_named_sibling()?;
12944    while prefix.kind() == "comment" {
12945        prefix = prefix.prev_named_sibling()?;
12946    }
12947    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
12948    let parameter = parameter_name
12949        .parent()
12950        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
12951    let declarator =
12952        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
12953    Some((declarator, parameter))
12954}
12955
12956fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
12957    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
12958        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
12959            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
12960                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
12961                    function,
12962                    parameter_name,
12963                    source,
12964                )
12965        });
12966    }
12967    let Some(parameter_name) =
12968        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
12969    else {
12970        return false;
12971    };
12972    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
12973        cpp_reparsed_template_macro_function_companion_is_indexable(
12974            function,
12975            parameter_name,
12976            source,
12977        )
12978    })
12979}
12980
12981fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
12982    let function_name = node
12983        .child_by_field_name("declarator")
12984        .and_then(extract_function_declarator)
12985        .and_then(cpp_function_declarator_name_node);
12986    if let Some(body) = cpp_reparsed_member_function_body(node)
12987        && function_name.is_some()
12988        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
12989    {
12990        return true;
12991    }
12992    cpp_reparsed_attribute_member_function(node, source)
12993        || cpp_reparsed_friend_function_is_indexable(node, source)
12994        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
12995        || cpp_reparsed_access_template_function_is_indexable(node, source)
12996        || cpp_recovered_template_macro_constructor(node, source).is_some()
12997}
12998
12999/// Recognize the three top-level nodes produced when an unknown attribute
13000/// macro separates an inline member's declarator from its body in a reparsed
13001/// class interior: an errorful declaration with a missing semicolon, the macro
13002/// call expression, and the complete compound body. Their adjacency and exact
13003/// structured shapes prove one recoverable member envelope; arbitrary calls or
13004/// blocks do not pass this gate.
13005fn cpp_reparsed_macro_attribute_member_sequence(
13006    children: &[Node<'_>],
13007    index: usize,
13008    source: &str,
13009) -> bool {
13010    let Some(prefix) = children.get(index).copied() else {
13011        return false;
13012    };
13013    let declaration = if prefix.kind() == "labeled_statement" {
13014        prefix
13015            .named_child(prefix.named_child_count().saturating_sub(1))
13016            .filter(|child| child.kind() == "declaration")
13017    } else {
13018        (prefix.kind() == "declaration").then_some(prefix)
13019    };
13020    let Some(declaration) = declaration else {
13021        return false;
13022    };
13023    if !declaration.has_error()
13024        || declaration
13025            .child_by_field_name("declarator")
13026            .and_then(extract_function_declarator)
13027            .and_then(cpp_function_declarator_name_node)
13028            .is_none()
13029    {
13030        return false;
13031    }
13032    let Some(attribute_statement) = children.get(index + 1).copied() else {
13033        return false;
13034    };
13035    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
13036        .then(|| attribute_statement.named_child(0))
13037        .flatten()
13038        .filter(|child| child.kind() == "call_expression")
13039    else {
13040        return false;
13041    };
13042    let Some(attribute_name) = attribute_call
13043        .child_by_field_name("function")
13044        .filter(|function| function.kind() == "identifier")
13045        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
13046    else {
13047        return false;
13048    };
13049    if !cpp_export_macro_token(&attribute_name) {
13050        return false;
13051    }
13052    let Some(body) = children.get(index + 2).copied() else {
13053        return false;
13054    };
13055    body.kind() == "compound_statement"
13056        && body.child(0).is_some_and(|open| open.kind() == "{")
13057        && body
13058            .child(body.child_count().saturating_sub(1))
13059            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
13060        && declaration.end_byte() <= attribute_statement.start_byte()
13061        && attribute_statement.end_byte() <= body.start_byte()
13062}
13063
13064fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
13065    let mut cursor = root.walk();
13066    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
13067    let mut saw_member = false;
13068    let mut index = 0;
13069    while index < children.len() {
13070        let child = children[index];
13071        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
13072            saw_member = true;
13073            index += 3;
13074            continue;
13075        }
13076        if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
13077            let Some(tree) = cpp_reparse_fragmented_class_body(
13078                source,
13079                fragmented.reparse_start,
13080                fragmented.reparse_end,
13081            ) else {
13082                return false;
13083            };
13084            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
13085                return false;
13086            }
13087            saw_member = true;
13088            index += 1;
13089            while index < children.len()
13090                && children[index].end_byte() <= fragmented.class_range.end_byte
13091            {
13092                index += 1;
13093            }
13094            continue;
13095        }
13096        match child.kind() {
13097            "comment" => {}
13098            "labeled_statement" => saw_member = true,
13099            "function_definition" => {
13100                if child.has_error()
13101                    && !cpp_reparsed_member_function_is_indexable(child, source)
13102                    && cpp_sentinel_macro_region(child, source).is_none()
13103                {
13104                    return false;
13105                }
13106                saw_member = true;
13107            }
13108            "ERROR"
13109                if (cpp_reparsed_member_error_is_indexable(child)
13110                    || cpp_reparsed_adjacent_copy_control_error(child, source))
13111                    && (child
13112                        .next_named_sibling()
13113                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
13114                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
13115            {
13116                saw_member = true;
13117            }
13118            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
13119                saw_member = true;
13120            }
13121            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
13122                saw_member = true;
13123            }
13124            "expression_statement"
13125                if cpp_is_stray_semicolon(child, source)
13126                    && child.prev_named_sibling().is_some_and(|error| {
13127                        cpp_reparsed_member_error_is_indexable(error)
13128                            || cpp_reparsed_adjacent_copy_control_error(error, source)
13129                    }) =>
13130            {
13131                saw_member = true;
13132            }
13133            "compound_statement"
13134                if cpp_reparsed_constructor_body_is_indexable(child, source)
13135                    || cpp_reparsed_attribute_requires_body(child, source) =>
13136            {
13137                saw_member = true;
13138            }
13139            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
13140            _ => return false,
13141        }
13142        index += 1;
13143    }
13144    saw_member
13145}
13146
13147/// Detect the malformed constructor shape that tree-sitter exposes as an
13148/// access-label statement followed by initializer-looking declarations. The
13149/// declarations are not class members: visiting their `location(loc)` and
13150/// `string(s)` function declarators would publish synthetic functions. The
13151/// export-class fallback keeps the original sibling nodes and therefore avoids
13152/// this parser artifact. The returned range identifies the real constructor
13153/// header, which can be reparsed independently as a structured declarator.
13154fn cpp_reparsed_synthetic_initializer_constructor_range(
13155    root: Node<'_>,
13156    class_name: &str,
13157    source: &str,
13158    constructor_end: usize,
13159) -> Option<std::ops::Range<usize>> {
13160    let mut stack = {
13161        let mut cursor = root.walk();
13162        root.named_children(&mut cursor).collect::<Vec<_>>()
13163    };
13164    while let Some(current) = stack.pop() {
13165        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
13166            current,
13167            class_name,
13168            source,
13169            constructor_end,
13170        ) {
13171            return Some(range);
13172        }
13173        if current.kind() == "ERROR" {
13174            let mut cursor = current.walk();
13175            stack.extend(current.named_children(&mut cursor));
13176        }
13177    }
13178    None
13179}
13180
13181/// Recover an inline constructor that a function-like export macro makes
13182/// tree-sitter merge with the following overload. In the reparsed class-body
13183/// region, the access label wraps one declaration whose ERROR contains the
13184/// constructor declarator and its base-initializer/body, while the declaration's
13185/// ordinary declarator is the following overload. Every boundary below comes
13186/// from that CST; no source syntax is reparsed by hand.
13187fn cpp_reparsed_merged_inline_constructor<'tree>(
13188    root: Node<'tree>,
13189    class_name: &str,
13190    source: &str,
13191) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
13192    let mut stack = vec![root];
13193    while let Some(current) = stack.pop() {
13194        if current.kind() != "labeled_statement" {
13195            let mut cursor = current.walk();
13196            stack.extend(current.named_children(&mut cursor));
13197            continue;
13198        }
13199        let declaration = current
13200            .named_children(&mut current.walk())
13201            .find(|child| child.kind() == "declaration")?;
13202        if declaration
13203            .child_by_field_name("type")
13204            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
13205        {
13206            continue;
13207        }
13208        let following = declaration
13209            .child_by_field_name("declarator")
13210            .and_then(extract_function_declarator)
13211            .and_then(cpp_function_declarator_name_node);
13212        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
13213            continue;
13214        }
13215        let mut declaration_cursor = declaration.walk();
13216        let Some(error) = declaration
13217            .named_children(&mut declaration_cursor)
13218            .find(|child| child.kind() == "ERROR")
13219        else {
13220            continue;
13221        };
13222        let mut error_cursor = error.walk();
13223        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
13224        let Some(constructor) = error_children.iter().copied().find(|child| {
13225            child.kind() == "function_declarator"
13226                && cpp_function_declarator_name_node(*child)
13227                    .is_some_and(|name| node_text(name, source).trim() == class_name)
13228        }) else {
13229            continue;
13230        };
13231        let Some(body) = error_children.iter().copied().find_map(|child| {
13232            (child.kind() == "init_declarator")
13233                .then(|| child.child_by_field_name("value"))
13234                .flatten()
13235                .filter(|value| value.kind() == "initializer_list")
13236        }) else {
13237            continue;
13238        };
13239        if constructor.end_byte() > body.start_byte() {
13240            continue;
13241        }
13242        return Some((constructor.start_byte()..body.end_byte(), body));
13243    }
13244    None
13245}
13246
13247fn cpp_reparsed_synthetic_initializer_constructor(
13248    node: Node<'_>,
13249    class_name: &str,
13250    source: &str,
13251    constructor_end: usize,
13252) -> Option<std::ops::Range<usize>> {
13253    if node.kind() != "labeled_statement" {
13254        return None;
13255    }
13256    let mut cursor = node.walk();
13257    let named = node
13258        .named_children(&mut cursor)
13259        .filter(|child| child.kind() != "comment")
13260        .collect::<Vec<_>>();
13261    let label = named.first()?;
13262    if label.kind() != "statement_identifier"
13263        || !matches!(
13264            node_text(*label, source).trim(),
13265            "public" | "private" | "protected"
13266        )
13267    {
13268        return None;
13269    }
13270    let call_error_index = named.iter().position(|child| {
13271        if child.kind() != "ERROR" {
13272            return false;
13273        }
13274        let mut stack = vec![*child];
13275        while let Some(current) = stack.pop() {
13276            if current.kind() == "call_expression"
13277                && current
13278                    .child_by_field_name("function")
13279                    .is_some_and(|function| {
13280                        function.kind() == "identifier"
13281                            && node_text(function, source).trim() == class_name
13282                    })
13283            {
13284                return true;
13285            }
13286            let mut cursor = current.walk();
13287            stack.extend(current.named_children(&mut cursor));
13288        }
13289        false
13290    })?;
13291    let constructor_call = {
13292        let mut stack = vec![named[call_error_index]];
13293        let mut found = None;
13294        while let Some(current) = stack.pop() {
13295            if current.kind() == "call_expression"
13296                && current
13297                    .child_by_field_name("function")
13298                    .is_some_and(|function| {
13299                        function.kind() == "identifier"
13300                            && node_text(function, source).trim() == class_name
13301                    })
13302            {
13303                found = Some(current);
13304                break;
13305            }
13306            let mut cursor = current.walk();
13307            stack.extend(current.named_children(&mut cursor));
13308        }
13309        found
13310    };
13311    let constructor_call = constructor_call?;
13312    named.iter().skip(call_error_index + 1).find(|child| {
13313        child.kind() == "declaration" && child.has_error() && {
13314            let mut cursor = child.walk();
13315            child.named_children(&mut cursor).any(|declarator| {
13316                declarator.kind() == "init_declarator"
13317                    && declarator
13318                        .child_by_field_name("declarator")
13319                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
13320                    && declarator
13321                        .child_by_field_name("value")
13322                        .is_some_and(|value| value.kind() == "initializer_list")
13323            })
13324        }
13325    })?;
13326    Some(constructor_call.start_byte()..constructor_end)
13327}
13328
13329fn cpp_reparsed_exact_constructor_declarator<'tree>(
13330    root: Node<'tree>,
13331    start: usize,
13332    class_name: &str,
13333    source: &str,
13334) -> Option<Node<'tree>> {
13335    let mut candidate = None;
13336    let mut stack = vec![root];
13337    while let Some(current) = stack.pop() {
13338        if current.kind() == "function_declarator"
13339            && current.start_byte() == start
13340            && cpp_function_declarator_name_node(current)
13341                .is_some_and(|name| node_text(name, source).trim() == class_name)
13342        {
13343            if candidate.is_some() {
13344                return None;
13345            }
13346            candidate = Some(current);
13347            continue;
13348        }
13349        let mut cursor = current.walk();
13350        stack.extend(current.named_children(&mut cursor));
13351    }
13352    candidate
13353}
13354
13355fn cpp_is_indexable_item_kind(kind: &str) -> bool {
13356    matches!(
13357        kind,
13358        "namespace_definition"
13359            | "class_specifier"
13360            | "struct_specifier"
13361            | "union_specifier"
13362            | "enum_specifier"
13363            | "function_definition"
13364            | "template_declaration"
13365            | "declaration"
13366            | "field_declaration"
13367            | "alias_declaration"
13368            | "static_assert_declaration"
13369            | "type_definition"
13370            | "using_declaration"
13371            | "linkage_specification"
13372            | "preproc_def"
13373            | "preproc_function_def"
13374            | "preproc_include"
13375            | "preproc_if"
13376            | "preproc_ifdef"
13377            | "preproc_call"
13378    )
13379}
13380
13381#[cfg(test)]
13382mod tests {
13383    use super::*;
13384    use crate::adapter::parse_cpp_file;
13385    use brokk_bifrost_core::analyzer::parsed_file::{
13386        finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
13387        start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
13388    };
13389    use std::fmt::Write;
13390
13391    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
13392        let mut parser = tree_sitter::Parser::new();
13393        parser
13394            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13395            .unwrap();
13396        let tree = parser.parse(source, None).unwrap();
13397        let file = ProjectFile::new(std::env::temp_dir(), name);
13398        parse_cpp_file(&file, source, &tree)
13399    }
13400
13401    #[test]
13402    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
13403        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
13404        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
13405        let mut macros = parsed
13406            .declarations()
13407            .iter()
13408            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
13409            .collect::<Vec<_>>();
13410        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
13411
13412        assert_eq!(macros.len(), 2, "{macros:#?}");
13413        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
13414        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
13415        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
13416        assert_eq!(
13417            parsed.declaration_ranges(macros[1])[0].start_byte,
13418            source.rfind("#define VALUE 2").expect("second definition")
13419        );
13420    }
13421
13422    #[test]
13423    fn identifies_export_macro_class_base_displaced_into_declarator() {
13424        let source = r#"#define PROJECT_API_
13425namespace project {
13426namespace internal {
13427template <typename T>
13428class Base {};
13429}
13430template <typename T>
13431class Wrapper;
13432template <>
13433class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
13434}
13435"#;
13436        let mut parser = tree_sitter::Parser::new();
13437        parser
13438            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13439            .unwrap();
13440        let tree = parser.parse(source, None).unwrap();
13441        let start = source.find("internal::Base<int>").expect("base");
13442        let mut base = tree
13443            .root_node()
13444            .descendant_for_byte_range(start, start + 8)
13445            .expect("base syntax");
13446        while base.kind() != "qualified_identifier" {
13447            base = base.parent().expect("qualified base ancestor");
13448        }
13449        assert!(
13450            is_recovered_exported_class_base_type_node(base, source),
13451            "{}",
13452            tree.root_node().to_sexp()
13453        );
13454    }
13455
13456    #[test]
13457    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
13458        let source = r#"namespace control {
13459template <typename T>
13460class AnySpan;
13461template <typename T>
13462class ABSL_ATTRIBUTE_VIEW AnySpan {
13463 public:
13464  int begin() const;
13465};
13466}
13467
13468namespace absl {
13469ABSL_NAMESPACE_BEGIN
13470template <typename T>
13471class ABSL_ATTRIBUTE_VIEW Span {
13472 public:
13473  int begin() const;
13474  int back() const;
13475};
13476
13477int begin();
13478int back();
13479}
13480"#;
13481        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
13482        let declarations = parsed.declarations();
13483        assert!(
13484            declarations
13485                .iter()
13486                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
13487        );
13488        for method in ["begin", "back"] {
13489            assert!(declarations.iter().any(|unit| {
13490                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
13491            }));
13492            assert!(
13493                declarations.iter().any(|unit| {
13494                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
13495                })
13496            );
13497        }
13498        assert!(
13499            declarations
13500                .iter()
13501                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
13502        );
13503        assert!(
13504            declarations
13505                .iter()
13506                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
13507        );
13508        assert!(
13509            declarations
13510                .iter()
13511                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
13512        );
13513    }
13514
13515    #[test]
13516    fn explicit_global_member_definition_has_canonical_package_boundary() {
13517        let source = r#"
13518namespace arangodb::aql {
13519class ExecutionPlan {
13520 public:
13521  template<class... Args> Node* createNode(Args&&... args);
13522};
13523}
13524
13525template<class... Args>
13526Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
13527"#;
13528        let parsed = parse_cpp_declarations(source, "global-member.cpp");
13529
13530        assert!(parsed.declarations().iter().any(|unit| {
13531            unit.is_function()
13532                && unit.package_name() == "arangodb::aql"
13533                && unit.short_name() == "ExecutionPlan.createNode"
13534                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
13535        }));
13536    }
13537
13538    #[test]
13539    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
13540        let source = r#"
13541#ifndef TINYXML2_INCLUDED
13542#define TINYXML2_INCLUDED
13543namespace tinyxml2 {
13544class TINYXML2_LIB XMLUtil {
13545 public:
13546  static const char* SkipWhiteSpace(const char* p) {
13547    while (*p) {
13548      if (*p == ' ') {
13549        ++p;
13550      }
13551    }
13552    return p;
13553  }
13554  static bool StringEqual(const char* p, const char* q) {
13555    return p == q;
13556  }
13557  class TINYXML2_LIB Helper {
13558   public:
13559    void Touch();
13560  };
13561  static void ToStr(int value, char* buffer);
13562 private:
13563  static const char* writeBoolTrue;
13564};
13565
13566class TINYXML2_LIB XMLNode {
13567 public:
13568  virtual XMLNode* ShallowClone() const = 0;
13569  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
13570};
13571}
13572#endif
13573"#;
13574        let mut parser = tree_sitter::Parser::new();
13575        parser
13576            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13577            .unwrap();
13578        let tree = parser.parse(source, None).unwrap();
13579        let mut boundary_found = false;
13580        walk_named_tree_preorder(tree.root_node(), true, |node| {
13581            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
13582                && name == "XMLUtil"
13583            {
13584                boundary_found = fragmented_export_sibling_class_boundary(node, source)
13585                    .and_then(|boundary| {
13586                        recover_exported_class_function_definition(boundary, source)
13587                    })
13588                    .is_some_and(|(_, name, _)| name == "XMLNode");
13589            }
13590            WalkControl::Continue
13591        });
13592        assert!(
13593            boundary_found,
13594            "fixture must exercise the recovered sibling boundary"
13595        );
13596
13597        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
13598        assert!(
13599            parsed
13600                .declarations()
13601                .iter()
13602                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
13603            "{:#?}",
13604            parsed.declarations()
13605        );
13606        assert!(
13607            parsed
13608                .declarations()
13609                .iter()
13610                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
13611            "{:#?}",
13612            parsed.declarations()
13613        );
13614        assert!(parsed.declarations().iter().any(|unit| {
13615            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
13616        }));
13617        assert!(
13618            parsed
13619                .declarations()
13620                .iter()
13621                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
13622        );
13623        assert!(
13624            parsed
13625                .declarations()
13626                .iter()
13627                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
13628        );
13629    }
13630
13631    #[test]
13632    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
13633        // Clang's diagnostic suite intentionally contains this ill-formed
13634        // spelling. The analyzer must retain the parser's explicit-global AST
13635        // boundary instead of constructing `cwg311::::cwg311::X`.
13636        let parsed = parse_cpp_declarations(
13637            r#"
13638namespace cwg311 {
13639namespace X { namespace Y {} }
13640namespace ::cwg311::X {}
13641}
13642"#,
13643            "explicit-global-namespace.cpp",
13644        );
13645
13646        assert!(parsed.declarations().iter().any(|unit| {
13647            unit.kind() == CodeUnitType::Module
13648                && unit.short_name() == "cwg311::X"
13649                && unit.fq_name() == "cwg311::X"
13650        }));
13651        assert!(
13652            parsed
13653                .declarations()
13654                .iter()
13655                .all(|unit| !unit.short_name().contains("::::")),
13656            "recovered namespace names must not retain empty scope components: {:#?}",
13657            parsed.declarations()
13658        );
13659    }
13660
13661    #[test]
13662    fn repeated_scope_separator_does_not_create_empty_function_owner() {
13663        let scope = ScopeInfo {
13664            package_name: "X".to_string(),
13665            module: None,
13666            class_unit: None,
13667            template_signature: None,
13668            template_metadata: None,
13669            declarations_are_fields: false,
13670            recovered_specialization_member_scope: false,
13671            visible_using_namespaces: Vec::new(),
13672        };
13673
13674        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
13675
13676        assert!(owner.is_none());
13677        assert_eq!(name, "doit");
13678        assert_eq!(package, "X");
13679    }
13680
13681    #[test]
13682    fn trailing_decltype_expression_is_not_a_function_declarator() {
13683        let source = r#"
13684namespace boost { namespace detail {
13685#if ! defined(BOOST_NO_SFINAE_EXPR) && \
13686    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
13687    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
13688#define BOOST_THREAD_PROVIDES_INVOKE
13689#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
13690template <class Fp, class A0, class ...Args>
13691inline auto
13692invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
13693       BOOST_THREAD_RV_REF(Args) ...args)
13694    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
13695{
13696    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
13697}
13698#endif
13699#endif
13700}}
13701"#;
13702        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
13703
13704        assert!(
13705            parsed
13706                .declarations()
13707                .iter()
13708                .all(|unit| unit.short_name() != ".*f")
13709        );
13710    }
13711
13712    fn find_class_named<'tree>(
13713        root: Node<'tree>,
13714        source: &str,
13715        expected_name: &str,
13716    ) -> Option<Node<'tree>> {
13717        let mut stack = vec![root];
13718        while let Some(node) = stack.pop() {
13719            if node.kind() == "class_specifier"
13720                && node
13721                    .child_by_field_name("name")
13722                    .is_some_and(|name| node_text(name, source) == expected_name)
13723            {
13724                return Some(node);
13725            }
13726            let mut cursor = node.walk();
13727            stack.extend(node.named_children(&mut cursor));
13728        }
13729        None
13730    }
13731
13732    #[test]
13733    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
13734        let source = r#"EXPORT void definition(struct Value value) {}
13735EXPORT void prototype(struct Value value);
13736"#;
13737        let mut parser = tree_sitter::Parser::new();
13738        parser
13739            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13740            .unwrap();
13741        let tree = parser.parse(source, None).unwrap();
13742        let root = tree.root_node();
13743        let mut cursor = root.walk();
13744        let callables = root
13745            .named_children(&mut cursor)
13746            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
13747            .collect::<Vec<_>>();
13748
13749        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
13750        for callable in callables {
13751            assert!(callable.has_error(), "fixture must exercise error recovery");
13752            assert!(
13753                cpp_sentinel_macro_parts(callable, source).is_none(),
13754                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
13755            );
13756        }
13757    }
13758
13759    #[test]
13760    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
13761        let source = r#"namespace absl {
13762ABSL_NAMESPACE_BEGIN
13763// Generate a floating-point variate conforming to a Beta distribution:
13764template <typename RealType = double>
13765class beta_distribution {
13766 public:
13767  using result_type = RealType;
13768
13769
13770  beta_distribution() : beta_distribution(1) {}
13771
13772  explicit beta_distribution(result_type alpha, result_type beta = 1)
13773      : param_(alpha, beta) {}
13774
13775  explicit beta_distribution(const param_type& p) : param_(p) {}
13776
13777  void reset() {}
13778
13779  // Generating functions
13780  template <typename URBG>
13781  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
13782    return (*this)(g, param_);
13783  }
13784
13785};
13786ABSL_NAMESPACE_END
13787}  // namespace absl
13788"#;
13789        let mut parser = tree_sitter::Parser::new();
13790        parser
13791            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13792            .unwrap();
13793        let tree = parser.parse(source, None).unwrap();
13794        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
13795        let body = namespace
13796            .child_by_field_name("body")
13797            .expect("fixture namespace body");
13798        let sentinel = body.named_child(0).expect("sentinel envelope");
13799        let callable = sentinel
13800            .child_by_field_name("declarator")
13801            .and_then(extract_function_declarator)
13802            .and_then(cpp_function_declarator_name_node)
13803            .expect("preserved callable name");
13804
13805        assert_eq!(sentinel.kind(), "function_definition");
13806        assert_eq!(callable.kind(), "operator_name");
13807        assert!(
13808            cpp_sentinel_macro_parts(sentinel, source).is_some(),
13809            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
13810        );
13811    }
13812
13813    #[test]
13814    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
13815        let source = r#"namespace absl {
13816ABSL_NAMESPACE_BEGIN
13817// absl::discrete_distribution
13818//
13819// A discrete distribution produces random integers i, where 0 <= i < n
13820template <typename IntType = int>
13821class discrete_distribution {
13822 public:
13823  using result_type = IntType;
13824  class param_type {
13825   public:
13826    param_type() { init(); }
13827    template <typename InputIterator>
13828    explicit param_type(InputIterator begin, InputIterator end)
13829        : p_(begin, end) {
13830      init();
13831    }
13832  };
13833  discrete_distribution() : param_() {}
13834  explicit discrete_distribution(const param_type& p) : param_(p) {}
13835};
13836ABSL_NAMESPACE_END
13837}  // namespace absl
13838"#;
13839        let mut parser = tree_sitter::Parser::new();
13840        parser
13841            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13842            .unwrap();
13843        let tree = parser.parse(source, None).unwrap();
13844        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
13845        let body = namespace
13846            .child_by_field_name("body")
13847            .expect("fixture namespace body");
13848        let sentinel = body.named_child(0).expect("sentinel envelope");
13849        let callable = sentinel
13850            .child_by_field_name("declarator")
13851            .and_then(extract_function_declarator)
13852            .and_then(cpp_function_declarator_name_node)
13853            .expect("preserved callable name");
13854
13855        assert_eq!(sentinel.kind(), "function_definition");
13856        assert_eq!(callable.kind(), "identifier");
13857        assert!(
13858            cpp_sentinel_macro_parts(sentinel, source).is_some(),
13859            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
13860        );
13861    }
13862
13863    #[test]
13864    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
13865        let source = r#"
13866#define CPPCHECKLIB
13867class Library {
13868    struct Container {
13869        CPPCHECKLIB static std::string toString(Yield yield);
13870        CPPCHECKLIB static std::string toString(Action action);
13871    };
13872};
13873"#;
13874        let mut parser = tree_sitter::Parser::new();
13875        parser
13876            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13877            .unwrap();
13878        let tree = parser.parse(source, None).unwrap();
13879        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
13880        let parsed = parse_cpp_file(&file, source, &tree);
13881        assert!(
13882            parsed
13883                .declarations()
13884                .iter()
13885                .all(|unit| unit.fq_name() != "Library$Container.std"),
13886            "the qualified return-type namespace must not become a field: {:#?}",
13887            parsed.declarations()
13888        );
13889        for expected in ["(Yield)", "(Action)"] {
13890            assert!(
13891                parsed.declarations().iter().any(|unit| {
13892                    unit.is_function()
13893                        && unit.fq_name() == "Library$Container.toString"
13894                        && unit.signature() == Some(expected)
13895                }),
13896                "recovered toString overload {expected} is missing: {:#?}",
13897                parsed.declarations()
13898            );
13899        }
13900    }
13901
13902    #[test]
13903    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
13904        let source = r#"
13905#define SIMPLECPP_LIB
13906namespace simplecpp {
13907using TokenString = std::string;
13908struct Location { int line{}; };
13909class SIMPLECPP_LIB Token {
13910  TokenString prefix;
13911  void prefix_method() {}
13912 public:
13913  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
13914      whitespaceahead(wsahead), location(loc), string(s)
13915      // The comment must not hide the constructor body from recovery.
13916      {
13917      flags();
13918  }
13919  TokenString string;
13920  bool whitespaceahead;
13921  Location location;
13922  Token *previous{};
13923 private:
13924  void flags() {
13925      whitespaceahead = true;
13926  }
13927};
13928}
13929"#;
13930        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
13931
13932        let location_fields = parsed
13933            .declarations()
13934            .iter()
13935            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
13936            .collect::<Vec<_>>();
13937        assert_eq!(
13938            location_fields.len(),
13939            1,
13940            "location should have one class-owned declaration: {:#?}",
13941            parsed.declarations()
13942        );
13943        assert!(
13944            location_fields[0].is_field(),
13945            "location has wrong kind: {:#?}",
13946            parsed.declarations()
13947        );
13948        assert!(
13949            parsed.declarations().iter().all(|unit| {
13950                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
13951            })
13952        );
13953        assert!(
13954            parsed.declarations().iter().all(|unit| {
13955                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
13956            })
13957        );
13958        assert!(
13959            parsed
13960                .declarations()
13961                .iter()
13962                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
13963        );
13964        assert!(
13965            parsed
13966                .declarations()
13967                .iter()
13968                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
13969            "the recovered class must retain its constructor: {:#?}",
13970            parsed.declarations()
13971        );
13972        assert!(
13973            parsed
13974                .declarations()
13975                .iter()
13976                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
13977        );
13978        assert!(parsed.declarations().iter().any(|unit| {
13979            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
13980        }));
13981        let constructor = parsed
13982            .declarations()
13983            .iter()
13984            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
13985            .expect("recovered constructor");
13986        let constructor_start = source.find("Token(const").expect("constructor start");
13987        let constructor_end = source
13988            .get(
13989                ..source
13990                    .find("  TokenString string;")
13991                    .expect("constructor end"),
13992            )
13993            .expect("constructor slice")
13994            .trim_end()
13995            .len();
13996        assert!(
13997            parsed
13998                .navigation_ranges
13999                .get(constructor)
14000                .is_some_and(|ranges| {
14001                    ranges.iter().any(|range| {
14002                        range.start_byte == constructor_start && range.end_byte == constructor_end
14003                    })
14004                }),
14005            "constructor navigation must span the full body: {:#?}",
14006            parsed.navigation_ranges
14007        );
14008        assert_eq!(
14009            parsed
14010                .signature_metadata
14011                .get(constructor)
14012                .and_then(|metadata| metadata.first())
14013                .and_then(SignatureMetadata::callable_linkage),
14014            Some(CallableLinkage::External)
14015        );
14016        let token_class = parsed
14017            .declarations()
14018            .iter()
14019            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
14020            .expect("recovered Token class");
14021        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
14022        assert!(
14023            parsed
14024                .navigation_ranges
14025                .get(token_class)
14026                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
14027            "class navigation must include the terminating semicolon: {:#?}",
14028            parsed.navigation_ranges
14029        );
14030    }
14031
14032    #[test]
14033    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
14034        let source = r#"
14035#define SIMPLECPP_LIB
14036namespace simplecpp {
14037using TokenString = std::string;
14038class Macro;
14039struct Location {
14040  unsigned int fileIndex{};
14041  unsigned int line{};
14042  unsigned int col{};
14043};
14044struct Output {
14045  int type;
14046};
14047class SIMPLECPP_LIB Token {
14048 public:
14049  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
14050      whitespaceahead(wsahead), location(loc), string(s) {
14051      flags();
14052  }
14053  Token(const Token &tok) :
14054      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
14055      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
14056      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
14057  Token &operator=(const Token &tok) = delete;
14058  const TokenString& str() const { return string; }
14059  void setstr(const std::string &s) { string = s; flags(); }
14060  bool isOneOf(const char ops[]) const;
14061  TokenString macro;
14062  char op;
14063  bool comment;
14064  bool name;
14065  bool number;
14066  bool whitespaceahead;
14067  Location location;
14068  Token *previous{};
14069  Token *next{};
14070 private:
14071  void flags() {
14072      name = !string.empty();
14073      comment = false;
14074      number = false;
14075      op = 0;
14076  }
14077  TokenString string;
14078};
14079}
14080struct Following {
14081  int type;
14082};
14083class SIMPLECPP_LIB Later {
14084 public:
14085  Later(int value) : value(value) {}
14086  int value;
14087};
14088"#;
14089        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
14090        assert!(
14091            parsed
14092                .declarations()
14093                .iter()
14094                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
14095        );
14096        assert!(
14097            !parsed
14098                .declarations()
14099                .iter()
14100                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
14101        );
14102        assert!(
14103            parsed
14104                .declarations()
14105                .iter()
14106                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
14107        );
14108        assert!(
14109            !parsed
14110                .declarations()
14111                .iter()
14112                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
14113        );
14114        assert!(
14115            parsed
14116                .declarations()
14117                .iter()
14118                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
14119        );
14120        assert!(
14121            parsed
14122                .declarations()
14123                .iter()
14124                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
14125        );
14126        assert!(
14127            parsed
14128                .declarations()
14129                .iter()
14130                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
14131        );
14132        assert!(
14133            parsed
14134                .declarations()
14135                .iter()
14136                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
14137        );
14138        assert!(
14139            parsed
14140                .declarations()
14141                .iter()
14142                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
14143        );
14144        assert!(
14145            parsed
14146                .declarations()
14147                .iter()
14148                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
14149        );
14150        assert!(parsed.declarations().iter().all(|unit| {
14151            !matches!(
14152                unit.fq_name().as_str(),
14153                "simplecpp.Token.Following" | "simplecpp.Token.Later"
14154            )
14155        }));
14156        assert!(
14157            !parsed
14158                .declarations()
14159                .iter()
14160                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
14161            "the following struct must remain outside the recovered Token class"
14162        );
14163    }
14164
14165    #[test]
14166    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
14167        let source = r#"
14168#define SIMPLECPP_LIB
14169namespace {
14170namespace simplecpp {
14171using TokenString = std::string;
14172struct Location { int line{}; };
14173class SIMPLECPP_LIB HiddenToken {
14174 public:
14175  HiddenToken(const TokenString &s, const Location &loc) :
14176      location(loc), string(s) {
14177      flags();
14178  }
14179  TokenString string;
14180  Location location;
14181  HiddenToken *previous{};
14182 private:
14183  void flags() {}
14184};
14185}
14186}
14187"#;
14188        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
14189        let constructor = parsed
14190            .declarations()
14191            .iter()
14192            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
14193            .expect("recovered anonymous-namespace constructor");
14194        assert_eq!(
14195            parsed
14196                .signature_metadata
14197                .get(constructor)
14198                .and_then(|metadata| metadata.first())
14199                .and_then(SignatureMetadata::callable_linkage),
14200            Some(CallableLinkage::Internal)
14201        );
14202    }
14203
14204    #[test]
14205    fn macro_qualified_static_field_keeps_real_declarator() {
14206        let source = r#"#define JSON_INLINE_VARIABLE
14207struct Reader {
14208static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
14209static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
14210static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
14211};"#;
14212        let mut parser = tree_sitter::Parser::new();
14213        parser
14214            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14215            .unwrap();
14216        let tree = parser.parse(source, None).unwrap();
14217        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
14218        let parsed = parse_cpp_file(&file, source, &tree);
14219        for expected in [
14220            "Reader.npos",
14221            "Reader.other",
14222            "Reader.pointer",
14223            "Reader.reference",
14224        ] {
14225            assert!(
14226                parsed
14227                    .declarations()
14228                    .iter()
14229                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
14230                "real macro-decorated field {expected} is missing: {:#?}",
14231                parsed.declarations()
14232            );
14233        }
14234        assert!(
14235            parsed
14236                .declarations()
14237                .iter()
14238                .all(|unit| unit.fq_name() != "Reader.std"),
14239            "qualified type prefix became a pseudo-field: {:#?}",
14240            parsed.declarations()
14241        );
14242        let root = tree.root_node();
14243        let mut stack = vec![root];
14244        let mut signatures = Vec::new();
14245        while let Some(current) = stack.pop() {
14246            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
14247            {
14248                signatures.extend(
14249                    declarators
14250                        .into_iter()
14251                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
14252                );
14253            }
14254            let mut cursor = current.walk();
14255            stack.extend(current.named_children(&mut cursor));
14256        }
14257        signatures.sort();
14258        assert_eq!(
14259            signatures,
14260            [
14261                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
14262                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
14263                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
14264                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
14265            ]
14266        );
14267    }
14268
14269    fn member_function_linkage(source: &str) -> CallableLinkage {
14270        let mut parser = tree_sitter::Parser::new();
14271        parser
14272            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14273            .unwrap();
14274        let tree = parser.parse(source, None).unwrap();
14275        let ancestry = ParentIndex::new(tree.root_node());
14276        let mut stack = vec![tree.root_node()];
14277        while let Some(node) = stack.pop() {
14278            if node.kind() == "function_definition" {
14279                let mut current = node.parent();
14280                while let Some(parent) = current {
14281                    if matches!(
14282                        parent.kind(),
14283                        "class_specifier" | "struct_specifier" | "union_specifier"
14284                    ) {
14285                        return cpp_callable_linkage(node, source, &ancestry);
14286                    }
14287                    current = parent.parent();
14288                }
14289            }
14290            let mut cursor = node.walk();
14291            stack.extend(node.named_children(&mut cursor));
14292        }
14293        panic!("fixture has no member function definition");
14294    }
14295
14296    #[test]
14297    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
14298        assert_eq!(
14299            member_function_linkage("struct Named { int method() { return 1; } };"),
14300            CallableLinkage::External
14301        );
14302        assert_eq!(
14303            member_function_linkage(
14304                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
14305            ),
14306            CallableLinkage::Internal
14307        );
14308        assert_eq!(
14309            member_function_linkage("struct { int method() { return 1; } } instance;"),
14310            CallableLinkage::Internal
14311        );
14312        assert_eq!(
14313            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
14314            CallableLinkage::Internal
14315        );
14316    }
14317
14318    #[test]
14319    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
14320        let source = r#"
14321#ifndef PROTON_VALUE_HPP
14322#define PROTON_VALUE_HPP
14323namespace proton {
14324namespace internal {
14325class value_base {
14326  protected:
14327    internal::data& data();
14328    internal::data data_;
14329  friend class codec::encoder;
14330  friend class codec::decoder;
14331};
14332}
14333class value : public internal::value_base, private internal::comparable<value> {
14334  private:
14335    template<class T, class U=void> struct assignable :
14336        public std::enable_if<codec::is_encodable<T>::value, U> {};
14337    template<class U> struct assignable<value, U> {};
14338  public:
14339    PN_CPP_EXTERN value();
14340    PN_CPP_EXTERN value(const value&);
14341    PN_CPP_EXTERN value& operator=(const value&);
14342    PN_CPP_EXTERN value(value&&);
14343    PN_CPP_EXTERN value& operator=(value&&);
14344    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
14345    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
14346        codec::encoder e(*this);
14347        e << x;
14348        return *this;
14349    }
14350    PN_CPP_EXTERN type_id type() const;
14351    PN_CPP_EXTERN bool empty() const;
14352    PN_CPP_EXTERN void clear();
14353    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
14354    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
14355  friend PN_CPP_EXTERN void swap(value&, value&);
14356  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
14357  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
14358  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
14359    value(pn_data_t* d);
14360    void reset(pn_data_t* d = 0);
14361};
14362}
14363#endif
14364"#;
14365        let mut parser = tree_sitter::Parser::new();
14366        parser
14367            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14368            .unwrap();
14369        let tree = parser.parse(source, None).unwrap();
14370        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
14371        let parsed = parse_cpp_file(&file, source, &tree);
14372        let macro_constructors = parsed
14373            .signature_metadata
14374            .iter()
14375            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
14376            .flat_map(|(_, metadata)| metadata)
14377            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
14378            .collect::<Vec<_>>();
14379
14380        assert_eq!(
14381            macro_constructors.len(),
14382            3,
14383            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
14384            parsed.declarations()
14385        );
14386        assert!(
14387            macro_constructors.iter().all(|metadata| {
14388                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
14389            }),
14390            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
14391        );
14392    }
14393
14394    #[test]
14395    fn recovered_export_class_typedef_uses_displaced_alias_name() {
14396        let source = r#"
14397namespace spi {
14398class Filter {
14399public:
14400    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
14401};
14402}
14403namespace filter {
14404class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
14405{
14406public:
14407    typedef spi::Filter BASE_CLASS;
14408    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
14409    BEGIN_LOG4CXX_CAST_MAP()
14410    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
14411    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
14412    END_LOG4CXX_CAST_MAP()
14413    FilterDecision decide() const;
14414};
14415}
14416"#;
14417        let mut parser = tree_sitter::Parser::new();
14418        parser
14419            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14420            .unwrap();
14421        let tree = parser.parse(source, None).unwrap();
14422        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
14423        let parsed = parse_cpp_file(&file, source, &tree);
14424        assert!(
14425            parsed.declarations().iter().any(|unit| {
14426                unit.is_class()
14427                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
14428                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
14429            }),
14430            "the displaced typedef alias must retain its declared name: {:#?}",
14431            parsed.declarations()
14432        );
14433        assert!(
14434            parsed
14435                .declarations()
14436                .iter()
14437                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
14438            "the qualified underlying type must not become a false nested alias: {:#?}",
14439            parsed.declarations()
14440        );
14441    }
14442
14443    #[test]
14444    fn exported_single_base_recovery_uses_displaced_class_name() {
14445        let source = r#"
14446class CORE_EXPORT QgsPoint : public AbstractGeometry
14447{
14448    Q_GADGET
14449
14450    Q_PROPERTY( double x READ x WRITE setX )
14451    Q_PROPERTY( double y READ y WRITE setY )
14452    Q_PROPERTY( double z READ z WRITE setZ )
14453    Q_PROPERTY( double m READ m WRITE setM )
14454
14455  public:
14456#ifndef SIP_RUN
14457    QgsPoint(
14458      double x = std::numeric_limits<double>::quiet_NaN(),
14459      double y = std::numeric_limits<double>::quiet_NaN(),
14460      double z = std::numeric_limits<double>::quiet_NaN(),
14461      double m = std::numeric_limits<double>::quiet_NaN(),
14462      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
14463    );
14464#else
14465    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 )];
14466    % MethodCode
14467    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
14468    {
14469      int state;
14470      sipIsErr = 0;
14471      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
14472      if ( !sipIsErr )
14473      {
14474        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
14475      }
14476      sipReleaseType( p, sipType_QgsPointXY, state );
14477    }
14478    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
14479    {
14480      int state;
14481      sipIsErr = 0;
14482
14483      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
14484      if ( !sipIsErr )
14485      {
14486        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
14487      }
14488      sipReleaseType( p, sipType_QPointF, state );
14489    }
14490    else if (
14491      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
14492      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
14493      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
14494      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
14495    {
14496      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
14497      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
14498      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
14499      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
14500      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
14501      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
14502    }
14503    else // Invalid ctor arguments
14504    {
14505      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
14506      sipIsErr = 1;
14507    }
14508    % End
14509#endif
14510
14511    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
14512    explicit QgsPoint( QPointF p ) SIP_SKIP;
14513    explicit QgsPoint(
14514      Qgis::WkbType wkbType,
14515      double x = std::numeric_limits<double>::quiet_NaN(),
14516      double y = std::numeric_limits<double>::quiet_NaN(),
14517      double z = std::numeric_limits<double>::quiet_NaN(),
14518      double m = std::numeric_limits<double>::quiet_NaN()
14519    ) SIP_SKIP;
14520    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
14521    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
14522    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
14523#ifndef SIP_RUN
14524  private:
14525    bool fuzzyHelper(
14526      double epsilon,
14527      const AbstractGeometry &other,
14528      bool is3DFlag,
14529      bool isMeasureFlag
14530    ) const
14531    {
14532      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
14533    }
14534#endif
14535};
14536class Ordinary : public Base { public: Ordinary(); };
14537class API_EXPORT Plain { public: Plain(); };
14538class API_EXPORT : public Base {};
14539class
14540PN_CPP_CLASS_EXTERN Sender : public Link {
14541    Sender();
14542};
14543class thread_ctx_t {};
14544class ctx_t ZMQ_FINAL : public thread_ctx_t {
14545    bool start();
14546};
14547"#;
14548        let mut parser = tree_sitter::Parser::new();
14549        parser
14550            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14551            .unwrap();
14552        let tree = parser.parse(source, None).unwrap();
14553        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
14554        let parsed = parse_cpp_file(&file, source, &tree);
14555        let declarations = parsed.declarations();
14556
14557        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
14558            assert!(
14559                declarations
14560                    .iter()
14561                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
14562                "missing recovered class {expected}: {declarations:#?}"
14563            );
14564        }
14565        let qgs_point = declarations
14566            .iter()
14567            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
14568            .expect("recovered QgsPoint class");
14569        assert_eq!(
14570            parsed.raw_supertypes.get(qgs_point),
14571            Some(&vec!["AbstractGeometry".to_string()]),
14572            "single-base export recovery must retain its displaced base"
14573        );
14574        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
14575        assert!(
14576            parsed
14577                .navigation_ranges
14578                .get(qgs_point)
14579                .is_some_and(|ranges| {
14580                    !ranges.is_empty()
14581                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
14582                }),
14583            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
14584            parsed.navigation_ranges.get(qgs_point)
14585        );
14586        let sender = declarations
14587            .iter()
14588            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
14589            .expect("recovered Sender class");
14590        assert_eq!(
14591            parsed.raw_supertypes.get(sender),
14592            Some(&vec!["Link".to_string()]),
14593            "post-declarator export recovery must retain its displaced base"
14594        );
14595        let ctx = declarations
14596            .iter()
14597            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
14598            .expect("recovered ctx_t class");
14599        assert_eq!(
14600            parsed.raw_supertypes.get(ctx),
14601            Some(&vec!["thread_ctx_t".to_string()]),
14602            "postfix export-macro recovery must retain its displaced base"
14603        );
14604        assert!(
14605            declarations.iter().any(|unit| {
14606                unit.is_function()
14607                    && unit.fq_name() == "QgsPoint.QgsPoint"
14608                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
14609            }),
14610            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
14611        );
14612        assert!(
14613            declarations.iter().all(|unit| {
14614                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
14615            }),
14616            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
14617        );
14618    }
14619
14620    #[test]
14621    fn function_like_export_macro_classes_keep_names_and_base_edges() {
14622        let source = r#"
14623namespace api {
14624class PROJECT_PUBLIC_API(2, 0) Prelude {
14625  public:
14626    Prelude();
14627};
14628class PROJECT_PUBLIC_API(2, 0) Base {
14629  public:
14630    Base(int value);
14631};
14632class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
14633  public:
14634    Derived(int value);
14635};
14636} // namespace api
14637"#;
14638        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
14639        let declarations = parsed.declarations();
14640        let base = declarations
14641            .iter()
14642            .find(|unit| unit.is_class() && unit.fq_name() == "api.Base")
14643            .expect("function-like export macro base class");
14644        let derived = declarations
14645            .iter()
14646            .find(|unit| unit.is_class() && unit.fq_name() == "api.Derived")
14647            .expect("function-like export macro derived class");
14648
14649        assert_eq!(
14650            parsed.raw_supertypes.get(derived),
14651            Some(&vec!["Base".to_string()])
14652        );
14653        assert!(
14654            declarations
14655                .iter()
14656                .all(|unit| unit.fq_name() != "PROJECT_PUBLIC_API"),
14657            "the export macro must not become a declaration: {declarations:#?}"
14658        );
14659        assert!(
14660            parsed
14661                .navigation_ranges
14662                .get(base)
14663                .is_some_and(|ranges| !ranges.is_empty()),
14664            "the recovered base must retain a navigable declaration range"
14665        );
14666    }
14667
14668    #[test]
14669    fn function_like_export_class_survives_a_preceding_malformed_body() {
14670        let source = r#"
14671namespace api {
14672class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
14673   public:
14674      /** Return a descriptive string. */
14675      const char* what() const noexcept override { return m_msg.c_str(); }
14676
14677      /** Return the type of error. */
14678      virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
14679
14680      /** Return an associated error code. */
14681      virtual int error_code() const noexcept { return 0; }
14682
14683      /** Avoid throwing the base directly. */
14684      explicit Exception(std::string_view msg);
14685
14686      /** Avoid throwing the base directly. */
14687      Exception(const char* prefix, std::string_view msg);
14688
14689      /** Avoid throwing the base directly. */
14690      Exception(std::string_view msg, const std::exception& e);
14691
14692   private:
14693      std::string m_msg;
14694};
14695
14696class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
14697   public:
14698      explicit Invalid_Argument(std::string_view msg);
14699
14700      explicit Invalid_Argument(std::string_view msg, std::string_view where);
14701
14702      Invalid_Argument(std::string_view msg, const std::exception& e);
14703
14704      ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
14705};
14706} // namespace api
14707"#;
14708        let mut parser = Parser::new();
14709        parser
14710            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14711            .expect("set C++ grammar");
14712        let tree = parser.parse(source, None).expect("parse fixture");
14713        let mut stack = vec![tree.root_node()];
14714        let mut saw_embedded_shape = false;
14715        while let Some(node) = stack.pop() {
14716            saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
14717                .iter()
14718                .any(|recovered| recovered.name == "Invalid_Argument");
14719            let mut cursor = node.walk();
14720            stack.extend(node.named_children(&mut cursor));
14721        }
14722        assert!(
14723            saw_embedded_shape,
14724            "fixture must retain the embedded error geometry: {}",
14725            tree.root_node().to_sexp()
14726        );
14727
14728        let parsed = parse_cpp_file(
14729            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
14730            source,
14731            &tree,
14732        );
14733        let declarations = parsed.declarations();
14734        let exception = declarations
14735            .iter()
14736            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
14737            .expect("qualified-base export class");
14738        let invalid = declarations
14739            .iter()
14740            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
14741            .expect("class embedded in the preceding malformed body");
14742
14743        assert_eq!(
14744            parsed.raw_supertypes.get(exception),
14745            Some(&vec!["std::exception".to_string()])
14746        );
14747        assert_eq!(
14748            parsed.raw_supertypes.get(invalid),
14749            Some(&vec!["Exception".to_string()])
14750        );
14751        assert!(
14752            parsed.materialization_records.iter().any(|record| matches!(
14753                record,
14754                MaterializationRecord::RecoveredDeclaration { unit, .. }
14755                    if unit == invalid
14756            )),
14757            "the embedded class must retain recovery provenance: {:#?}",
14758            parsed.materialization_records
14759        );
14760    }
14761
14762    #[test]
14763    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
14764        let source = r#"
14765public:
14766   explicit Lookup_Error(std::string_view err) : Exception(err) {}
14767
14768   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
14769"#;
14770        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
14771            .expect("reparse merged constructor body");
14772        let (range, body) =
14773            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
14774                .unwrap_or_else(|| {
14775                    panic!(
14776                        "the merged constructor must retain its structured declarator/body: {}",
14777                        tree.root_node().to_sexp()
14778                    )
14779                });
14780        assert_eq!(
14781            source.get(range).expect("constructor range"),
14782            "Lookup_Error(std::string_view err) : Exception(err) {}"
14783        );
14784        assert_eq!(node_text(body, source), "{}");
14785    }
14786
14787    #[test]
14788    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
14789        let positive_source =
14790            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
14791        let mut parser = tree_sitter::Parser::new();
14792        parser
14793            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14794            .unwrap();
14795        let positive_tree = parser.parse(positive_source, None).unwrap();
14796        assert!(cpp_reparsed_members_are_indexable(
14797            positive_tree.root_node(),
14798            positive_source
14799        ));
14800
14801        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
14802        let negative_tree = parser.parse(negative_source, None).unwrap();
14803        assert!(!cpp_reparsed_members_are_indexable(
14804            negative_tree.root_node(),
14805            negative_source
14806        ));
14807    }
14808
14809    #[test]
14810    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
14811        let copy_control_source = r#"
14812public:
14813    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
14814    explicit Token(const Token* tok);
14815    ~Token();
14816    Token* astOperand1() { return nullptr; }
14817"#;
14818        let constraint_source = r#"
14819private:
14820    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
14821    static T *tokAtImpl(T *tok, int index) {
14822        return tok;
14823    }
14824
14825    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
14826    static T *linkAtImpl(T *tok, int index) {
14827        return tok;
14828    }
14829
14830public:
14831    int late() const { return 1; }
14832"#;
14833        let mut parser = tree_sitter::Parser::new();
14834        parser
14835            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14836            .unwrap();
14837        let copy_control_tree = parser
14838            .parse(copy_control_source, None)
14839            .expect("parse copy-control fixture");
14840        assert!(
14841            copy_control_tree.root_node().has_error(),
14842            "fixture must exercise adjacent copy-control recovery"
14843        );
14844        assert!(
14845            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
14846            "a complete late getter must remain recoverable after adjacent copy-control declarations"
14847        );
14848        let mut cursor = copy_control_tree.root_node().walk();
14849        assert!(
14850            copy_control_tree
14851                .root_node()
14852                .named_children(&mut cursor)
14853                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
14854            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
14855            copy_control_tree.root_node().to_sexp()
14856        );
14857        let constraint_tree = parser
14858            .parse(constraint_source, None)
14859            .expect("parse constraint-macro fixture");
14860        assert!(constraint_tree.root_node().has_error());
14861        assert!(
14862            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
14863            "complete constraint-macro members must not hide a later ordinary member"
14864        );
14865        let mut cursor = constraint_tree.root_node().walk();
14866        assert!(
14867            constraint_tree
14868                .root_node()
14869                .named_children(&mut cursor)
14870                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
14871                    child,
14872                    constraint_source
14873                )),
14874            "fixture must retain the split constraint-macro prefix/function geometry"
14875        );
14876    }
14877
14878    #[test]
14879    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
14880        let source = r#"
14881struct Analyzer {
14882    struct Action {
14883        Action() = default;
14884        Action(const Action&) = default;
14885        Action& operator=(const Action& rhs) & = default;
14886
14887        template<class T,
14888                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
14889                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
14890        // NOLINTNEXTLINE(google-explicit-constructor)
14891        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
14892        {}
14893
14894        enum : std::uint16_t { None = 0, Read = (1 << 0) };
14895        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
14896
14897    private:
14898        unsigned int mFlag{};
14899    };
14900
14901    enum class Direction : unsigned char { Forward, Reverse };
14902    virtual Action analyze(Direction d) const = 0;
14903};
14904"#;
14905        let mut parser = tree_sitter::Parser::new();
14906        parser
14907            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14908            .unwrap();
14909        let tree = parser.parse(source, None).unwrap();
14910        assert!(tree.root_node().has_error());
14911        let root = tree.root_node();
14912        let outer = root
14913            .named_children(&mut root.walk())
14914            .find(|child| child.kind() == "ERROR")
14915            .expect("fragmented Analyzer prefix");
14916        let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
14917            .expect("structured Analyzer fragment boundary");
14918        assert_eq!(outer_name, "Analyzer");
14919        let outer_tree = cpp_reparse_fragmented_class_body(
14920            source,
14921            outer_fragment.reparse_start,
14922            outer_fragment.reparse_end,
14923        )
14924        .expect("reparse Analyzer body");
14925        let outer_root = outer_tree.root_node();
14926        let action_prefix = outer_root
14927            .named_children(&mut outer_root.walk())
14928            .find(|child| child.kind() == "ERROR")
14929            .expect("fragmented Action prefix");
14930        let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
14931            .expect("structured Action fragment boundary");
14932        assert_eq!(action_name, "Action");
14933        let action_tree = cpp_reparse_fragmented_class_body(
14934            source,
14935            action_fragment.reparse_start,
14936            action_fragment.reparse_end,
14937        )
14938        .expect("reparse Action body");
14939        let action_root = action_tree.root_node();
14940        let macro_prefix = action_root
14941            .named_children(&mut action_root.walk())
14942            .find(|child| child.kind() == "ERROR")
14943            .expect("constraint macro prefix");
14944        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
14945            .expect("structured template macro prefix");
14946        let macro_companion =
14947            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
14948        assert!(
14949            cpp_reparsed_template_macro_constructor_companion_is_indexable(
14950                macro_companion,
14951                macro_parameter,
14952                source,
14953            ),
14954            "split constrained constructor must be admitted: {}",
14955            macro_companion.to_sexp()
14956        );
14957        assert!(
14958            cpp_reparsed_members_are_indexable(action_root, source),
14959            "complete Action body must pass the recovery gate: {}",
14960            action_tree.root_node().to_sexp()
14961        );
14962        assert!(
14963            cpp_reparsed_members_are_indexable(outer_root, source),
14964            "complete Analyzer body must pass the recovery gate: {}",
14965            outer_tree.root_node().to_sexp()
14966        );
14967        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
14968        let parsed = parse_cpp_file(&file, source, &tree);
14969        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
14970            assert!(
14971                parsed
14972                    .declarations()
14973                    .iter()
14974                    .any(|unit| unit.fq_name() == expected),
14975                "missing recovered declaration {expected}: {:#?}",
14976                parsed.declarations()
14977            );
14978        }
14979        assert!(
14980            parsed
14981                .declarations()
14982                .iter()
14983                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
14984            "nested members must not remain flattened: {:#?}",
14985            parsed.declarations()
14986        );
14987    }
14988
14989    #[test]
14990    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
14991        let source = r#"
14992raw_hash_set& operator=(raw_hash_set&& that) {
14993  return move_assign(
14994      std::move(that),
14995      typename AllocTraits::propagate_on_container_move_assignment());
14996}
14997
14998iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
14999  return {};
15000}
15001
15002void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
15003
15004iterator insert(const_iterator hint, value_type&& value)
15005    ABSL_ATTRIBUTE_LIFETIME_BOUND {
15006  return {};
15007}
15008
15009friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
15010  return left.size() == right.size();
15011}
15012
15013static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
15014  return static_cast<slot_type*>(buffer);
15015}
15016
15017protected:
15018// Included-range recovery can attach this comment to the template prefix.
15019template <class K>
15020void AssertOnFind([[maybe_unused]] const K& key) {
15021  Check(key);
15022}
15023"#;
15024        let mut parser = tree_sitter::Parser::new();
15025        parser
15026            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15027            .unwrap();
15028        let tree = parser.parse(source, None).unwrap();
15029        assert!(
15030            tree.root_node().has_error(),
15031            "the fixture must exercise tree-sitter's errorful member shapes"
15032        );
15033        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
15034
15035        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
15036        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
15037        assert!(!cpp_reparsed_members_are_indexable(
15038            incomplete_tree.root_node(),
15039            incomplete_source
15040        ));
15041
15042        let outside_error_source = "int foo() stray_attribute {}\n";
15043        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
15044        assert!(outside_error_tree.root_node().has_error());
15045        assert!(!cpp_reparsed_members_are_indexable(
15046            outside_error_tree.root_node(),
15047            outside_error_source
15048        ));
15049
15050        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
15051        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
15052        assert!(!cpp_reparsed_members_are_indexable(
15053            variable_initializer_tree.root_node(),
15054            variable_initializer_source
15055        ));
15056    }
15057
15058    #[test]
15059    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
15060        let positive_source = r#"
15061std::pair<iterator, bool> insert(init_type&& value)
15062    ABSL_ATTRIBUTE_LIFETIME_BOUND
15063#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
15064  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
15065#endif
15066{
15067  return emplace(std::move(value));
15068}
15069"#;
15070        let mut parser = tree_sitter::Parser::new();
15071        parser
15072            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15073            .unwrap();
15074        let positive_tree = parser.parse(positive_source, None).unwrap();
15075        assert!(
15076            positive_tree.root_node().has_error(),
15077            "the fixture must exercise the split attribute/requires shape"
15078        );
15079        assert!(cpp_reparsed_members_are_indexable(
15080            positive_tree.root_node(),
15081            positive_source
15082        ));
15083
15084        let template_return_source = r#"
15085pair<int> insert(init_type&& value)
15086    ABSL_ATTRIBUTE_LIFETIME_BOUND
15087#if LANGUAGE_LEVEL >= 202002L
15088  requires(!Predicate<init_type>::value)
15089#endif
15090// Attributes and the function body may be separated by comments.
15091{
15092  return {};
15093}
15094"#;
15095        let template_return_tree = parser.parse(template_return_source, None).unwrap();
15096        assert!(
15097            cpp_reparsed_members_are_indexable(
15098                template_return_tree.root_node(),
15099                template_return_source
15100            ),
15101            "template-return attribute/requires tree: {}",
15102            template_return_tree.root_node().to_sexp()
15103        );
15104
15105        let no_body_source = r#"
15106std::pair<iterator, bool> insert(init_type&& value)
15107    ABSL_ATTRIBUTE_LIFETIME_BOUND
15108#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
15109  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
15110#endif
15111+ 0;
15112"#;
15113        let no_body_tree = parser.parse(no_body_source, None).unwrap();
15114        assert!(!cpp_reparsed_members_are_indexable(
15115            no_body_tree.root_node(),
15116            no_body_source
15117        ));
15118
15119        let extra_payload_source = r#"
15120pair<int> insert(init_type&& value)
15121    ABSL_ATTRIBUTE_LIFETIME_BOUND
15122#if LANGUAGE_LEVEL >= 202002L
15123  int unrelated;
15124  requires(Predicate<init_type>::value)
15125#endif
15126{
15127  return {};
15128}
15129"#;
15130        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
15131        assert!(!cpp_reparsed_members_are_indexable(
15132            extra_payload_tree.root_node(),
15133            extra_payload_source
15134        ));
15135
15136        let variable_initializer_source = r#"
15137int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
15138#if LANGUAGE_LEVEL >= 202002L
15139  requires(true)
15140#endif
15141{
15142  bad;
15143}
15144"#;
15145        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
15146        assert!(!cpp_reparsed_members_are_indexable(
15147            variable_initializer_tree.root_node(),
15148            variable_initializer_source
15149        ));
15150    }
15151
15152    #[test]
15153    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
15154        let source = r#"namespace absl {
15155ABSL_NAMESPACE_BEGIN namespace container_internal {
15156
15157class raw_hash_set : public Base {
15158 public:
15159  using value_type = int;
15160
15161  template <class U,
15162            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
15163  void insert(U value) { (void)value; }
15164
15165  struct InsertSlot {
15166    raw_hash_set& s;
15167  };
15168};
15169
15170}
15171ABSL_NAMESPACE_END
15172}"#;
15173        let mut parser = tree_sitter::Parser::new();
15174        parser
15175            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15176            .unwrap();
15177        let tree = parser.parse(source, None).unwrap();
15178        let root = tree.root_node();
15179        let outer_namespace = root
15180            .named_children(&mut root.walk())
15181            .find(|child| child.kind() == "namespace_definition")
15182            .expect("outer absl namespace");
15183        let declaration_list = outer_namespace
15184            .child_by_field_name("body")
15185            .expect("outer namespace body");
15186        let sentinel_function = declaration_list
15187            .named_children(&mut declaration_list.walk())
15188            .find(|child| child.kind() == "function_definition")
15189            .expect("malformed namespace sentinel function");
15190        let ancestry = ParentIndex::new(root);
15191        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
15192            .expect("structured nested namespace sentinel");
15193        let fragmented =
15194            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
15195                .expect("fragmented raw_hash_set class");
15196        assert_eq!(fragmented.class_node.kind(), "ERROR");
15197        assert_eq!(fragmented.name, "raw_hash_set");
15198        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
15199
15200        let outer_scope =
15201            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
15202        let mut outer_siblings = Vec::new();
15203        push_cpp_sentinel_sibling_classes(
15204            &mut outer_siblings,
15205            declaration_list,
15206            sentinel.function,
15207            &outer_scope,
15208            source,
15209            &ancestry,
15210        );
15211        let [outer_shadow] = outer_siblings.as_slice() else {
15212            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
15213        };
15214        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
15215        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
15216
15217        let field = "    raw_hash_set& s;";
15218        let start = source.find(field).expect("InsertSlot field") + 4;
15219        let node = root
15220            .descendant_for_byte_range(start, start + "raw_hash_set".len())
15221            .expect("raw_hash_set type node");
15222        let recovered = cpp_sentinel_recovered_classes(root, source);
15223        let [deep_class] = recovered.as_slice() else {
15224            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
15225        };
15226        assert_eq!(
15227            deep_class.namespace_scope_components,
15228            vec!["absl", "container_internal"]
15229        );
15230        assert_eq!(
15231            deep_class.scope_components,
15232            vec!["absl", "container_internal", "raw_hash_set"]
15233        );
15234        assert!(
15235            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
15236                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
15237        );
15238
15239        assert_eq!(
15240            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
15241            Some(vec![
15242                "absl".to_string(),
15243                "container_internal".to_string(),
15244                "raw_hash_set".to_string(),
15245                "InsertSlot".to_string(),
15246            ])
15247        );
15248
15249        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
15250        let parsed = parse_cpp_file(&file, source, &tree);
15251        let raw_hash_set = parsed
15252            .declarations()
15253            .iter()
15254            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
15255            .expect("recovered raw_hash_set class");
15256        assert_eq!(
15257            raw_hash_set.fq_name(),
15258            "absl::container_internal.raw_hash_set",
15259            "the recovered declaration must publish under the deeper sentinel namespace"
15260        );
15261        assert_eq!(
15262            parsed.raw_supertypes.get(raw_hash_set),
15263            Some(&vec!["Base".to_string()]),
15264            "the structured base clause on the fragmented ERROR prefix must survive publication"
15265        );
15266        assert!(
15267            parsed.materialization_records.iter().any(|record| matches!(
15268                record,
15269                MaterializationRecord::RecoveredDeclaration { recovery, unit }
15270                    if unit == raw_hash_set && *recovery == deep_class.class_range
15271            )),
15272            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
15273            parsed.materialization_records
15274        );
15275    }
15276
15277    /// Issue #2358: recording an aggregate definition must not walk the whole
15278    /// file.
15279    ///
15280    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
15281    /// class-like shape that has a body, so the removal step runs once per
15282    /// aggregate. It used to `retain` over `top_level_declarations` and over
15283    /// *every* child list in the file on each of those calls, comparing whole
15284    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
15285    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
15286    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
15287    /// tens of thousands of times over and the C forward differential never
15288    /// finished.
15289    ///
15290    /// A definition the file has not already declared removes nothing, so the
15291    /// honest cost is zero regardless of how many other aggregates surround it.
15292    /// Two sizes an order of magnitude apart pin that the count is not merely
15293    /// small but independent of the file.
15294    ///
15295    /// The declaration walk answers every ancestor question from a
15296    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
15297    /// the root for each one (#2361). Substituting the index is only safe
15298    /// because it answers the identical question, so pin that on the shapes
15299    /// this file's recovery paths care about: anonymous and named aggregates,
15300    /// nested namespaces, templates, macro-displaced declarations and the
15301    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
15302    /// too -- `Node::parent` walks the visible tree, not the named one.
15303    #[test]
15304    fn the_parent_index_answers_what_tree_sitter_answers() {
15305        const SHAPES: [&str; 5] = [
15306            "namespace outer { namespace inner { struct Tag { int field; }; } }",
15307            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
15308            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
15309            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
15310            "class API Broken : public First, public Second {\n  void member();\n",
15311        ];
15312        for source in SHAPES {
15313            let mut parser = tree_sitter::Parser::new();
15314            parser
15315                .set_language(&tree_sitter_cpp::LANGUAGE.into())
15316                .unwrap();
15317            let tree = parser.parse(source, None).unwrap();
15318            let root = tree.root_node();
15319            let ancestry = ParentIndex::new(root);
15320            let mut nodes = 0usize;
15321            let mut stack = vec![root];
15322            while let Some(node) = stack.pop() {
15323                nodes += 1;
15324                assert_eq!(
15325                    node.parent().map(|parent| parent.id()),
15326                    ancestry.parent(node).map(|parent| parent.id()),
15327                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
15328                );
15329                let mut cursor = node.walk();
15330                stack.extend(node.children(&mut cursor));
15331            }
15332            assert!(nodes > 1, "{source:?} produced no tree to compare");
15333        }
15334    }
15335
15336    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
15337    /// index for every ancestor edge. Asking tree-sitter directly makes each
15338    /// edge re-descend from the root, turning declaration extraction on a
15339    /// deeply nested generated header from quadratic output work into a cubic
15340    /// tree walk. Exact query counts pin the route without a machine-dependent
15341    /// wall-clock ceiling.
15342    #[test]
15343    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
15344        const DEPTH: usize = 64;
15345        let mut source = String::new();
15346        for level in 0..DEPTH {
15347            writeln!(source, "namespace n{level} {{").unwrap();
15348        }
15349        source.push_str("int deepest(int value);\n");
15350        for _ in 0..DEPTH {
15351            source.push_str("}\n");
15352        }
15353
15354        let mut parser = tree_sitter::Parser::new();
15355        parser
15356            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15357            .unwrap();
15358        let tree = parser.parse(&source, None).unwrap();
15359        let root = tree.root_node();
15360        let ancestry = ParentIndex::new(root);
15361        let mut function_declarator = None;
15362        walk_named_tree_preorder(root, true, |node| {
15363            if node.kind() == "function_declarator" {
15364                function_declarator = Some(node);
15365                WalkControl::Break
15366            } else {
15367                WalkControl::Continue
15368            }
15369        });
15370        let function_declarator = function_declarator.expect("deepest function declarator");
15371        let ancestor_count =
15372            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
15373
15374        ancestry.reset_parent_query_count_for_test();
15375        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
15376        assert_eq!(DEPTH, lexical_scope.len());
15377        assert_eq!(
15378            ancestor_count + 1,
15379            ancestry.parent_query_count_for_test(),
15380            "lexical-scope ancestry bypassed the parent index"
15381        );
15382
15383        ancestry.reset_parent_query_count_for_test();
15384        assert_eq!(
15385            DispatchExtensibility::Closed,
15386            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
15387        );
15388        assert_eq!(
15389            ancestor_count,
15390            ancestry.parent_query_count_for_test(),
15391            "dispatch ancestry bypassed the parent index"
15392        );
15393
15394        ancestry.reset_parent_query_count_for_test();
15395        assert_eq!(
15396            CallableLinkage::External,
15397            cpp_callable_linkage(function_declarator, &source, &ancestry)
15398        );
15399        assert_eq!(
15400            ancestor_count + 1,
15401            ancestry.parent_query_count_for_test(),
15402            "linkage ancestry bypassed the parent index"
15403        );
15404
15405        ancestry.reset_parent_query_count_for_test();
15406        assert!(!cpp_callable_is_structural_constructor(
15407            function_declarator,
15408            &source,
15409            &ancestry
15410        ));
15411        assert_eq!(
15412            ancestor_count + 1,
15413            ancestry.parent_query_count_for_test(),
15414            "constructor ancestry bypassed the parent index"
15415        );
15416    }
15417
15418    /// Forward declarations followed by definitions are compacted as one
15419    /// batch, without rescanning the shared namespace/top-level lists for each
15420    /// tag. Definitions are intentionally visited in reverse order so the
15421    /// assertion also pins eager remove-and-reappend ordering.
15422    #[test]
15423    fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
15424        for aggregates in [64usize, 512] {
15425            let mut source =
15426                String::from("typedef unsigned long long u64;\nnamespace generated {\n");
15427            for index in 0..aggregates {
15428                writeln!(source, "struct tag{index};").unwrap();
15429            }
15430            for index in (0..aggregates).rev() {
15431                writeln!(
15432                    source,
15433                    "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
15434                )
15435                .unwrap();
15436            }
15437            source.push_str("}\n");
15438
15439            start_code_unit_removal_scan_probe();
15440            let parsed = parse_cpp_declarations(&source, "vmlinux.h");
15441            let scanned = finish_code_unit_removal_scan_probe();
15442
15443            let expected_names: Vec<String> = (0..aggregates)
15444                .rev()
15445                .map(|index| format!("tag{index}"))
15446                .collect();
15447            let top_level_names: Vec<String> = parsed
15448                .top_level_declarations
15449                .iter()
15450                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
15451                .map(|unit| unit.short_name().to_string())
15452                .collect();
15453            let namespace = parsed
15454                .declarations()
15455                .iter()
15456                .find(|unit| {
15457                    unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
15458                })
15459                .expect("generated namespace should be declared");
15460            let child_names: Vec<String> = parsed.children[namespace]
15461                .iter()
15462                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
15463                .map(|unit| unit.short_name().to_string())
15464                .collect();
15465            assert_eq!(
15466                aggregates,
15467                parsed
15468                    .declarations()
15469                    .iter()
15470                    .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
15471                    .count(),
15472                "every aggregate must still be declared at {aggregates} aggregates"
15473            );
15474            assert_eq!(expected_names, top_level_names);
15475            assert_eq!(expected_names, child_names);
15476            assert_eq!(
15477                0, scanned,
15478                "replacing {aggregates} forward declarations must compact their shared lists once"
15479            );
15480        }
15481    }
15482
15483    #[test]
15484    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
15485        const DISTINCT_PER_KIND: usize = 64;
15486        let mut source = String::new();
15487        for index in 0..DISTINCT_PER_KIND {
15488            writeln!(source, "typedef int Alias{index};").unwrap();
15489        }
15490        writeln!(source, "typedef long Alias0;").unwrap();
15491        for index in 0..DISTINCT_PER_KIND {
15492            writeln!(source, "#define MACRO_{index} {index}").unwrap();
15493        }
15494        writeln!(source, "#define MACRO_0 duplicate").unwrap();
15495        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
15496
15497        let mut parser = tree_sitter::Parser::new();
15498        parser
15499            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15500            .unwrap();
15501        let tree = parser.parse(&source, None).unwrap();
15502        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
15503
15504        start_declaration_identity_comparison_probe();
15505        let parsed = parse_cpp_file(&file, &source, &tree);
15506        let comparisons = finish_declaration_identity_comparison_probe();
15507
15508        assert_eq!(
15509            DISTINCT_PER_KIND + 1,
15510            parsed
15511                .declarations()
15512                .iter()
15513                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
15514                .count(),
15515            "every physical typedef alias declaration must be retained so \
15516             conditional branch guards stay available to the resolver"
15517        );
15518        assert_eq!(
15519            DISTINCT_PER_KIND + 1,
15520            parsed
15521                .declarations()
15522                .iter()
15523                .filter(|unit| {
15524                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
15525                })
15526                .count(),
15527            "distinct macro redefinitions must remain available to temporal lookup"
15528        );
15529        assert_eq!(
15530            2,
15531            parsed
15532                .declarations()
15533                .iter()
15534                .filter(|unit| {
15535                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
15536                })
15537                .count(),
15538            "function overloads must remain distinct"
15539        );
15540
15541        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
15542        assert!(
15543            comparisons <= dedup_inputs * 4,
15544            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
15545        );
15546    }
15547
15548    #[test]
15549    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
15550        let source = r#"namespace absl {
15551ABSL_NAMESPACE_BEGIN namespace container_internal {
15552template <typename T>
15553class broken {
15554 public:
15555  using value_type = T;
15556  T operator->() const { return &operator*(); }
15557  using alias = value_type;
15558};
15559}
15560}
15561"#;
15562        let mut parser = tree_sitter::Parser::new();
15563        parser
15564            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15565            .unwrap();
15566        let tree = parser.parse(source, None).unwrap();
15567        let broken = find_class_named(tree.root_node(), source, "broken")
15568            .expect("the positive fixture must expose the broken class node");
15569        assert!(
15570            broken.has_error(),
15571            "the positive fixture must retain an internal parser error"
15572        );
15573        assert!(
15574            cpp_complete_class_body_close(broken).is_some(),
15575            "the positive fixture must expose a real class body close"
15576        );
15577        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15578        assert!(
15579            recovered.iter().any(|class| {
15580                class.scope_components == ["absl", "container_internal", "broken"]
15581            }),
15582            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
15583        );
15584    }
15585
15586    #[test]
15587    fn sentinel_recovery_keeps_members_after_nested_body_close() {
15588        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
15589NLOHMANN_BASIC_JSON_TPL_DECLARATION
15590class basic_json {
15591 private:
15592  union storage {
15593    int value;
15594  } data;
15595 public:
15596  using late_alias = int;
15597  late_alias value() const;
15598};
15599NLOHMANN_JSON_NAMESPACE_END
15600"#;
15601        let mut parser = tree_sitter::Parser::new();
15602        parser
15603            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15604            .unwrap();
15605        let tree = parser.parse(source, None).unwrap();
15606        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15607        let basic_json = recovered
15608            .iter()
15609            .find(|class| {
15610                class
15611                    .scope_components
15612                    .last()
15613                    .is_some_and(|name| name == "basic_json")
15614            })
15615            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
15616        let late_alias = source
15617            .find("late_alias value")
15618            .expect("late alias reference");
15619        assert!(
15620            basic_json.class_range.start_byte < late_alias
15621                && late_alias < basic_json.class_range.end_byte,
15622            "the recovered class range must include members after a nested close: {basic_json:#?}"
15623        );
15624    }
15625
15626    #[test]
15627    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
15628        let source = r#"namespace absl {
15629ABSL_NAMESPACE_BEGIN namespace container_internal {
15630template <typename T>
15631class broken {
15632 public:
15633  using value_type = T;
15634  T operator->() const { return &operator*(); }
15635}
15636}
15637"#;
15638        let mut parser = tree_sitter::Parser::new();
15639        parser
15640            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15641            .unwrap();
15642        let tree = parser.parse(source, None).unwrap();
15643        let broken = find_class_named(tree.root_node(), source, "broken")
15644            .expect("the negative fixture must expose the malformed class node");
15645        assert!(
15646            broken.has_error(),
15647            "the negative fixture must retain a parser error"
15648        );
15649        assert!(
15650            cpp_complete_class_body_close(broken).is_none(),
15651            "the malformed class must not expose a real body close"
15652        );
15653        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15654        assert!(
15655            recovered
15656                .iter()
15657                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
15658            "an incomplete class must not borrow the namespace close: {recovered:#?}"
15659        );
15660    }
15661
15662    #[test]
15663    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
15664        let source = r#"namespace absl {
15665ABSL_NAMESPACE_BEGIN namespace container_internal {
15666template <typename T>
15667struct broken {
15668  using value_type = T;
15669};
15670}
15671
15672#ifdef OWNER_DEF
15673template <typename T>
15674typename broken<T>::value_type broken<T>::method() {
15675  value_type value{};
15676  return value;
15677}
15678#endif
15679
15680namespace sibling {
15681template <typename T>
15682typename broken<T>::value_type broken<T>::other() {
15683  value_type value{};
15684  return value;
15685}
15686}
15687
15688ABSL_NAMESPACE_END
15689}
15690"#;
15691        let mut parser = tree_sitter::Parser::new();
15692        parser
15693            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15694            .unwrap();
15695        let tree = parser.parse(source, None).unwrap();
15696        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15697        let broken = recovered
15698            .iter()
15699            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
15700            .expect("the sentinel class must be recovered");
15701        let method_start = source
15702            .find("typename broken<T>::value_type broken<T>::method()")
15703            .expect("guarded sibling owner");
15704        let method_end = source[method_start..]
15705            .find("\n}")
15706            .map(|offset| method_start + offset + 2)
15707            .expect("guarded sibling owner close");
15708        assert!(
15709            broken
15710                .owner_ranges
15711                .iter()
15712                .any(|owner| owner.range.start_byte <= method_start
15713                    && method_end <= owner.range.end_byte),
15714            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
15715        );
15716        let sibling_start = source
15717            .find("typename broken<T>::value_type broken<T>::other()")
15718            .expect("nested namespace sibling owner");
15719        assert!(
15720            broken
15721                .owner_ranges
15722                .iter()
15723                .all(|owner| owner.range.start_byte > sibling_start
15724                    || owner.range.end_byte <= sibling_start),
15725            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
15726        );
15727    }
15728
15729    #[test]
15730    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
15731        let source = r#"#ifdef OUTER
15732namespace absl {
15733ABSL_NAMESPACE_BEGIN namespace container_internal {
15734template <typename T>
15735struct broken {
15736  using value_type = T;
15737};
15738}
15739}
15740
15741#ifdef OWNER_DEF
15742template <typename T>
15743typename broken<T>::value_type broken<T>::method() {
15744  value_type value{};
15745  return value;
15746}
15747#endif
15748#endif
15749"#;
15750        let mut parser = tree_sitter::Parser::new();
15751        parser
15752            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15753            .unwrap();
15754        let tree = parser.parse(source, None).unwrap();
15755        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15756        let broken = recovered
15757            .iter()
15758            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
15759            .expect("the sentinel class must be recovered");
15760        let method_start = source
15761            .find("typename broken<T>::value_type broken<T>::method()")
15762            .expect("outer sibling owner");
15763        assert!(
15764            broken
15765                .owner_ranges
15766                .iter()
15767                .all(|owner| owner.range.start_byte > method_start
15768                    || owner.range.end_byte <= method_start),
15769            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
15770        );
15771    }
15772
15773    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
15774    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
15775        let mut signatures = parsed
15776            .declarations()
15777            .iter()
15778            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
15779            .filter_map(|unit| unit.signature().map(str::to_string))
15780            .collect::<Vec<_>>();
15781        signatures.sort();
15782        signatures.dedup();
15783        signatures
15784    }
15785
15786    #[test]
15787    fn callable_parameter_types_come_from_the_ast_parameter_list() {
15788        let source = r#"
15789template <typename T, ENABLE_BYTES(T)>
15790Vec256<T> DupOdd(Vec256<T> value) { return value; }
15791
15792struct Visitor {
15793  void fail(this auto const& self) {}
15794};
15795"#;
15796        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
15797        let dup_odd = parsed
15798            .declarations()
15799            .iter()
15800            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
15801            .expect("DupOdd declaration");
15802        assert_eq!(
15803            dup_odd.signature(),
15804            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
15805        );
15806        assert_eq!(
15807            parsed
15808                .signature_metadata
15809                .get(dup_odd)
15810                .and_then(|metadata| metadata.first())
15811                .and_then(SignatureMetadata::callable_parameter_types),
15812            Some(["Vec256<T>".to_string()].as_slice())
15813        );
15814
15815        let fail = parsed
15816            .declarations()
15817            .iter()
15818            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
15819            .expect("explicit-object member");
15820        assert_eq!(fail.signature(), Some("(const this auto &)"));
15821        let metadata = parsed
15822            .signature_metadata
15823            .get(fail)
15824            .and_then(|metadata| metadata.first())
15825            .expect("explicit-object signature metadata");
15826        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
15827        assert!(
15828            metadata
15829                .callable_arity()
15830                .is_some_and(|arity| arity.accepts(0))
15831        );
15832    }
15833
15834    #[test]
15835    fn trailing_qualifiers_survive_parameter_list_whitespace() {
15836        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
15837        // declarator's structure, so an out-of-line definition that spells its
15838        // parameter list with different whitespace than the declaration must
15839        // still carry it.
15840        let source = r#"
15841struct Widget {
15842  bool multiline(int settings, int supprs) const;
15843  bool doublespace(int settings, int supprs) const;
15844  bool noexcept_multiline(int settings, int supprs) noexcept;
15845  bool ref_multiline(int settings, int supprs) &&;
15846};
15847bool
15848Widget::multiline (int settings,
15849                   int supprs) const
15850{ return settings + supprs > 0; }
15851bool Widget::doublespace(int settings,  int supprs) const { return true; }
15852bool Widget::noexcept_multiline(int settings,
15853                                int supprs) noexcept { return true; }
15854bool Widget::ref_multiline(int settings,
15855                           int supprs) && { return true; }
15856"#;
15857        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
15858        assert_eq!(
15859            vec!["(int, int) const".to_string()],
15860            identity_signatures(&parsed, "Widget.multiline")
15861        );
15862        assert_eq!(
15863            vec!["(int, int) const".to_string()],
15864            identity_signatures(&parsed, "Widget.doublespace")
15865        );
15866        assert_eq!(
15867            vec!["(int, int) noexcept".to_string()],
15868            identity_signatures(&parsed, "Widget.noexcept_multiline")
15869        );
15870        assert_eq!(
15871            vec!["(int, int) &&".to_string()],
15872            identity_signatures(&parsed, "Widget.ref_multiline")
15873        );
15874    }
15875
15876    #[test]
15877    fn macro_fragmented_plain_class_keeps_following_member_signature() {
15878        let source = r#"
15879struct CString {};
15880class CMessage {
15881public:
15882  CString GetParams(unsigned int index, unsigned int length = -1) const
15883      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
15884    return GetParamsColon(index, length);
15885  }
15886  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
15887};
15888CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
15889  return {};
15890}
15891"#;
15892        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
15893        assert_eq!(
15894            vec!["(unsigned int, unsigned int) const".to_string()],
15895            identity_signatures(&parsed, "CMessage.GetParamsColon")
15896        );
15897    }
15898
15899    #[test]
15900    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
15901        let source = r#"
15902#pragma once
15903#define DEMO_DEPRECATED(message)
15904namespace demo {
15905struct Base {
15906    static int aligned(int value) { return value; }
15907    int legacy(int value) const
15908        DEMO_DEPRECATED("use replacement()") { return value; }
15909    int replacement() const;
15910    void run(int value);
15911};
15912struct OtherBase {
15913    void run(int value);
15914    static int aligned(int value) { return value; }
15915};
15916struct Derived : Base {};
15917struct Override : Base {
15918    void run(int value);
15919    static int aligned(int value) { return value; }
15920};
15921struct RecoveredOverride : Base {
15922    int legacy(int value) const
15923        DEMO_DEPRECATED("use replacement()") { return value; }
15924    void run(int value);
15925};
15926struct Hidden : Base {
15927    void run(int first, int second);
15928    static int aligned(int first, int second) { return first + second; }
15929};
15930struct Ambiguous : Base, OtherBase {};
15931}
15932struct Global {};
15933"#;
15934        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
15935        let declarations = parsed.declarations();
15936        let fq_names = declarations
15937            .iter()
15938            .map(|unit| unit.fq_name())
15939            .collect::<std::collections::BTreeSet<_>>();
15940
15941        for expected in [
15942            "demo.Base",
15943            "demo.Base.aligned",
15944            "demo.Base.legacy",
15945            "demo.Base.replacement",
15946            "demo.Base.run",
15947            "demo.Derived",
15948            "demo.OtherBase",
15949            "demo.Override",
15950            "demo.RecoveredOverride",
15951            "demo.Hidden",
15952            "demo.Ambiguous",
15953            "Global",
15954        ] {
15955            assert!(
15956                fq_names.contains(expected),
15957                "missing {expected} from namespaced macro fragment: {declarations:#?}"
15958            );
15959        }
15960        assert!(
15961            !fq_names.contains("Derived"),
15962            "following class escaped its namespace: {declarations:#?}"
15963        );
15964        assert!(
15965            !fq_names.contains("demo.Global"),
15966            "global class crossed the recovered namespace boundary: {declarations:#?}"
15967        );
15968    }
15969
15970    #[test]
15971    fn trailing_qualifiers_still_separate_genuine_overloads() {
15972        // The qualifier must keep distinguishing the real C++ overload sets it
15973        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
15974        let source = r#"
15975struct Widget {
15976  int* slot(int index);
15977  const int* slot(int index) const;
15978  int log(int severity) &;
15979  int log(int severity) &&;
15980};
15981"#;
15982        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
15983        assert_eq!(
15984            vec!["(int)".to_string(), "(int) const".to_string()],
15985            identity_signatures(&parsed, "Widget.slot")
15986        );
15987        assert_eq!(
15988            vec!["(int) &".to_string(), "(int) &&".to_string()],
15989            identity_signatures(&parsed, "Widget.log")
15990        );
15991    }
15992
15993    #[test]
15994    fn virtual_specifier_is_not_part_of_the_identity_signature() {
15995        // `override` never appears on the out-of-line definition, and C++ does
15996        // not make it part of the signature, so it must not split the identity.
15997        let source = r#"
15998struct Base {
15999  virtual void run(int value) const;
16000};
16001struct Widget : Base {
16002  void run(int value) const override;
16003};
16004void Widget::run(int value) const {}
16005"#;
16006        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
16007        assert_eq!(
16008            vec!["(int) const".to_string()],
16009            identity_signatures(&parsed, "Widget.run")
16010        );
16011    }
16012
16013    #[test]
16014    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
16015        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
16016        // the function type, so a declaration that spells `const int` and a
16017        // definition that spells `int` are one entity.
16018        let source = r#"
16019struct Widget {
16020  bool value_params(const int settings, const int supprs);
16021  void pointee_const(const int* p);
16022  void pointer_const(int* const p);
16023  void both_const(const int* const p);
16024  void reference_const(const int& p);
16025  void array_const(const int values[4]);
16026};
16027bool Widget::value_params(int settings, int supprs) { return true; }
16028void Widget::pointer_const(int* p) {}
16029void Widget::both_const(const int* p) {}
16030"#;
16031        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
16032        assert_eq!(
16033            vec!["(int, int)".to_string()],
16034            identity_signatures(&parsed, "Widget.value_params")
16035        );
16036        assert_eq!(
16037            vec!["(int *)".to_string()],
16038            identity_signatures(&parsed, "Widget.pointer_const")
16039        );
16040        assert_eq!(
16041            vec!["(const int *)".to_string()],
16042            identity_signatures(&parsed, "Widget.both_const")
16043        );
16044        // The const that is not top-level still distinguishes the type.
16045        assert_eq!(
16046            vec!["(const int *)".to_string()],
16047            identity_signatures(&parsed, "Widget.pointee_const")
16048        );
16049        assert_eq!(
16050            vec!["(const int &)".to_string()],
16051            identity_signatures(&parsed, "Widget.reference_const")
16052        );
16053        assert_eq!(
16054            vec!["(const int [4])".to_string()],
16055            identity_signatures(&parsed, "Widget.array_const")
16056        );
16057    }
16058
16059    #[test]
16060    fn top_level_parameter_const_still_separates_pointee_overloads() {
16061        let source = r#"
16062struct Widget {
16063  void take(const int* p);
16064  void take(int* p);
16065};
16066"#;
16067        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
16068        assert_eq!(
16069            vec!["(const int *)".to_string(), "(int *)".to_string()],
16070            identity_signatures(&parsed, "Widget.take")
16071        );
16072    }
16073
16074    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
16075        let mut parser = tree_sitter::Parser::new();
16076        parser
16077            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16078            .unwrap();
16079        let tree = parser.parse(source, None).unwrap();
16080        let start = source.find(callable_name).expect("callable declaration");
16081        let declarator =
16082            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
16083        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
16084    }
16085
16086    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
16087        let mut shapes = comparable_shapes(source, callable_name);
16088        assert_eq!(1, shapes.len(), "{shapes:?}");
16089        match shapes.remove(0) {
16090            CppComparableSlot::Shape(shape) => shape,
16091            other => panic!("expected a comparable shape, got {other:?}"),
16092        }
16093    }
16094
16095    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
16096        let mut current = shape.root();
16097        loop {
16098            match shape.node(current) {
16099                CppComparableNode::Named { .. } => return shape.node(current),
16100                CppComparableNode::Pointer { inner, .. }
16101                | CppComparableNode::Reference { inner }
16102                | CppComparableNode::Array { inner } => current = *inner,
16103                CppComparableNode::Generic { base, .. } => current = *base,
16104            }
16105        }
16106    }
16107
16108    #[test]
16109    fn comparable_shape_keeps_pointee_const() {
16110        assert_ne!(
16111            sole_comparable_shape("void f(const char* p);", "f("),
16112            sole_comparable_shape("void f(char* p);", "f(")
16113        );
16114    }
16115
16116    #[test]
16117    fn comparable_shape_keeps_inner_pointer_const() {
16118        assert_ne!(
16119            sole_comparable_shape("void f(int** p);", "f("),
16120            sole_comparable_shape("void f(int* const* p);", "f(")
16121        );
16122    }
16123
16124    #[test]
16125    fn comparable_shape_drops_top_level_pointer_const() {
16126        assert_eq!(
16127            sole_comparable_shape("void f(int* const p);", "f("),
16128            sole_comparable_shape("void f(int* p);", "f(")
16129        );
16130    }
16131
16132    #[test]
16133    fn comparable_shape_drops_top_level_base_const() {
16134        assert_eq!(
16135            sole_comparable_shape("void f(const int p);", "f("),
16136            sole_comparable_shape("void f(int p);", "f(")
16137        );
16138    }
16139
16140    #[test]
16141    fn comparable_shape_decays_top_level_array_to_pointer() {
16142        assert_eq!(
16143            sole_comparable_shape("void f(int a[3]);", "f("),
16144            sole_comparable_shape("void f(int* a);", "f(")
16145        );
16146        assert_eq!(
16147            sole_comparable_shape("void f(int* a[3]);", "f("),
16148            sole_comparable_shape("void f(int** a);", "f(")
16149        );
16150    }
16151
16152    #[test]
16153    fn comparable_shape_keeps_array_behind_pointer() {
16154        assert_ne!(
16155            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
16156            sole_comparable_shape("struct S { void f(int** a); };", "f(")
16157        );
16158    }
16159
16160    #[test]
16161    fn comparable_shape_records_written_name_and_lexical_scope() {
16162        let declared =
16163            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
16164        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
16165        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
16166            panic!("named leaf");
16167        };
16168        assert_eq!(["Msg".to_string()].as_slice(), name.path());
16169        assert_eq!(
16170            ["ns".to_string(), "S".to_string()].as_slice(),
16171            name.lexical_scope()
16172        );
16173        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
16174            panic!("named leaf");
16175        };
16176        assert_eq!(
16177            ["ns".to_string(), "Msg".to_string()].as_slice(),
16178            name.path()
16179        );
16180        assert!(name.lexical_scope().is_empty());
16181        assert_ne!(declared, defined);
16182    }
16183
16184    #[test]
16185    fn comparable_shape_marks_sized_primitive_leaf() {
16186        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
16187        let CppComparableNode::Named {
16188            name, primitive, ..
16189        } = comparable_named_leaf(&shape)
16190        else {
16191            panic!("named leaf");
16192        };
16193        assert!(primitive);
16194        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
16195        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
16196    }
16197
16198    #[test]
16199    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
16200        assert_eq!(
16201            vec![CppComparableSlot::Unstructured],
16202            comparable_shapes("void f(void (*cb)(int));", "f(")
16203        );
16204    }
16205
16206    #[test]
16207    fn comparable_shape_reports_ellipsis_slot() {
16208        let shapes = comparable_shapes("void f(int a, ...);", "f(");
16209        assert_eq!(2, shapes.len(), "{shapes:?}");
16210        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
16211    }
16212
16213    #[test]
16214    fn comparable_shape_keeps_template_argument_const() {
16215        assert_ne!(
16216            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
16217            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
16218        );
16219    }
16220
16221    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
16222    /// file-scope tag that a later `struct inner *` at file scope may name.
16223    #[test]
16224    fn c_file_mints_aggregate_member_tag_at_file_scope() {
16225        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
16226        let parsed = parse_cpp_declarations(source, "x.c");
16227        let declarations = parsed.declarations();
16228
16229        assert!(
16230            declarations
16231                .iter()
16232                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
16233            "expected a file-scope inner tag, got {declarations:?}"
16234        );
16235        assert!(
16236            declarations
16237                .iter()
16238                .all(|unit| unit.fq_name() != "outer$inner"),
16239            "expected no nested identity, got {declarations:?}"
16240        );
16241        assert!(
16242            declarations
16243                .iter()
16244                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
16245        );
16246        // Members still belong to their own aggregate.
16247        assert!(
16248            declarations
16249                .iter()
16250                .any(|unit| unit.fq_name() == "inner.value")
16251        );
16252        assert!(
16253            declarations
16254                .iter()
16255                .any(|unit| unit.fq_name() == "outer.item")
16256        );
16257
16258        let outer = declarations
16259            .iter()
16260            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
16261            .expect("outer");
16262        assert!(
16263            parsed
16264                .children
16265                .get(outer)
16266                .into_iter()
16267                .flatten()
16268                .all(|child| child.fq_name() != "inner"),
16269            "the tag must not hang off the aggregate it is written inside: {:?}",
16270            parsed.children
16271        );
16272    }
16273
16274    /// A header carries no compilation language of its own, and a `.cpp`
16275    /// translation unit really does declare a nested class. Both keep exactly
16276    /// the C++ extraction they had before the C dialect existed.
16277    #[test]
16278    fn header_and_cpp_files_keep_nested_tag_identity() {
16279        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
16280        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
16281            let parsed = parse_cpp_declarations(source, name);
16282            let declarations = parsed.declarations();
16283            assert!(
16284                declarations
16285                    .iter()
16286                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
16287                "{name} must keep the nested identity, got {declarations:?}"
16288            );
16289            assert!(
16290                declarations.iter().all(|unit| unit.fq_name() != "inner"),
16291                "{name} must not mint a file-scope tag, got {declarations:?}"
16292            );
16293            assert!(
16294                declarations
16295                    .iter()
16296                    .any(|unit| unit.fq_name() == "outer$inner.value")
16297            );
16298        }
16299    }
16300
16301    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
16302    #[test]
16303    fn uppercase_c_extension_keeps_cpp_tag_scope() {
16304        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
16305        let parsed = parse_cpp_declarations(source, "x.C");
16306        assert!(
16307            parsed
16308                .declarations()
16309                .iter()
16310                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
16311        );
16312    }
16313
16314    /// There is no such thing as a partially nested tag in C: every level of a
16315    /// nested aggregate chain lands at the same enclosing scope.
16316    #[test]
16317    fn c_file_mints_every_nesting_level_at_file_scope() {
16318        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
16319        let parsed = parse_cpp_declarations(source, "z.c");
16320        let declarations = parsed.declarations();
16321
16322        for tag in ["a", "b", "c"] {
16323            assert!(
16324                declarations
16325                    .iter()
16326                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
16327                "expected a file-scope {tag}, got {declarations:?}"
16328            );
16329        }
16330        assert!(
16331            declarations
16332                .iter()
16333                .all(|unit| !unit.fq_name().contains('$')),
16334            "no level may keep a nested identity, got {declarations:?}"
16335        );
16336        // Each member still belongs to the aggregate that declares it.
16337        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
16338        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
16339        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
16340    }
16341
16342    /// An enum tag is a tag; its enumerators stay members of the enum, which is
16343    /// what makes them ordinary identifiers at the enum's own (file) scope.
16344    #[test]
16345    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
16346        let source = "struct outer { enum color { RED, GREEN } c; };\n";
16347        let parsed = parse_cpp_declarations(source, "e.c");
16348        let declarations = parsed.declarations();
16349
16350        let color = declarations
16351            .iter()
16352            .find(|unit| unit.is_class() && unit.fq_name() == "color")
16353            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
16354        assert!(
16355            declarations
16356                .iter()
16357                .all(|unit| unit.fq_name() != "outer$color")
16358        );
16359        for enumerator in ["color.RED", "color.GREEN"] {
16360            assert!(
16361                declarations.iter().any(|unit| unit.fq_name() == enumerator),
16362                "expected {enumerator}, got {declarations:?}"
16363            );
16364        }
16365        let children = parsed
16366            .children
16367            .get(color)
16368            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
16369        assert!(
16370            ["color.RED", "color.GREEN"]
16371                .iter()
16372                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
16373            "enumerators must hang off their enum: {children:?}"
16374        );
16375    }
16376
16377    #[test]
16378    fn c_file_mints_member_list_union_at_file_scope() {
16379        let source = "struct outer { union inner { int a; float b; } item; };\n";
16380        let parsed = parse_cpp_declarations(source, "u.c");
16381        let declarations = parsed.declarations();
16382        assert!(
16383            declarations
16384                .iter()
16385                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
16386            "expected a file-scope inner union, got {declarations:?}"
16387        );
16388        assert!(
16389            declarations
16390                .iter()
16391                .all(|unit| unit.fq_name() != "outer$inner")
16392        );
16393        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
16394        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
16395    }
16396
16397    /// A tag declared in a namespace member list is not a file-scope tag: the
16398    /// nearest enclosing non-aggregate scope is the namespace.
16399    #[test]
16400    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
16401        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
16402        let parsed = parse_cpp_declarations(source, "n.c");
16403        let declarations = parsed.declarations();
16404        let inner = declarations
16405            .iter()
16406            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
16407            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
16408        assert_eq!(inner.package_name(), "ns");
16409        assert!(
16410            declarations
16411                .iter()
16412                .all(|unit| unit.fq_name() != "ns.outer$inner")
16413        );
16414    }
16415
16416    /// Pins today's treatment of a tag declared inside a function body: the
16417    /// declaration walk does not descend into statement bodies, so no unit is
16418    /// minted for it in either dialect. C block scope is out of scope for the
16419    /// dialect change, and this test proves the change did not disturb it.
16420    #[test]
16421    fn function_local_tags_are_unchanged_in_both_dialects() {
16422        let source =
16423            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
16424        for name in ["y.c", "y.cpp"] {
16425            let parsed = parse_cpp_declarations(source, name);
16426            let declarations = parsed.declarations();
16427            assert!(
16428                declarations
16429                    .iter()
16430                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
16431                "{name}: {declarations:?}"
16432            );
16433            for tag in ["localtag", "deeper", "localtag$deeper"] {
16434                assert!(
16435                    declarations.iter().all(|unit| unit.fq_name() != tag),
16436                    "{name} must not mint {tag}, got {declarations:?}"
16437                );
16438            }
16439        }
16440    }
16441
16442    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
16443    /// re-scope: the typedef name is identical in both dialects.
16444    #[test]
16445    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
16446        let source = "typedef struct { int v; } T;\n";
16447        for name in ["t.c", "t.cpp"] {
16448            let parsed = parse_cpp_declarations(source, name);
16449            let declarations = parsed.declarations();
16450            assert!(
16451                declarations
16452                    .iter()
16453                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
16454                "{name}: {declarations:?}"
16455            );
16456        }
16457    }
16458
16459    #[test]
16460    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
16461        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
16462        let parsed = parse_cpp_declarations(source, "socket.c");
16463        let declarations = parsed.declarations();
16464        assert_eq!(
16465            declarations
16466                .iter()
16467                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
16468                .count(),
16469            1,
16470            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
16471        );
16472        for expected in [
16473            "PAL_HANDLE",
16474            "PAL_HANDLE.sock",
16475            "PAL_HANDLE$sock",
16476            "PAL_HANDLE$sock.ops",
16477        ] {
16478            assert!(
16479                declarations.iter().any(|unit| unit.fq_name() == expected),
16480                "expected {expected}, got {declarations:?}"
16481            );
16482        }
16483    }
16484
16485    /// `class` is not C. Source that spells one in a `.c` file is not C code,
16486    /// so it keeps the C++ reading rather than acquiring a half-C identity.
16487    #[test]
16488    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
16489        let source = "class outer { class inner { int v; }; };\n";
16490        let c_parsed = parse_cpp_declarations(source, "k.c");
16491        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
16492        let c_declarations = c_parsed.declarations();
16493        let cpp_declarations = cpp_parsed.declarations();
16494        assert!(
16495            c_declarations
16496                .iter()
16497                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
16498            "{c_declarations:?}"
16499        );
16500        assert_eq!(
16501            c_declarations
16502                .iter()
16503                .map(|unit| unit.fq_name())
16504                .collect::<std::collections::BTreeSet<_>>(),
16505            cpp_declarations
16506                .iter()
16507                .map(|unit| unit.fq_name())
16508                .collect::<std::collections::BTreeSet<_>>()
16509        );
16510    }
16511
16512    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
16513    /// every (node, class-like name) pair a tree offers, and require the same
16514    /// answer from both.
16515    ///
16516    /// The release build has no `debug_assertions` agreement check, so this is
16517    /// what pins the two together there.  Both query orders are exercised:
16518    /// document order is what the walk does, and reverse order proves that a
16519    /// question about an earlier byte than one already answered is still
16520    /// filtered back to its own prefix rather than answered from the wider
16521    /// fold.
16522    ///
16523    /// Returns how many questions were answered with a namespace, so a fixture
16524    /// can assert it actually reached the path (#2754).
16525    fn namespace_forward_scan_agreement(source: &str) -> usize {
16526        let mut parser = tree_sitter::Parser::new();
16527        parser
16528            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16529            .unwrap();
16530        let tree = parser.parse(source, None).unwrap();
16531        let root = tree.root_node();
16532        let ancestry = ParentIndex::new(root);
16533
16534        let mut nodes = Vec::new();
16535        let mut names = std::collections::BTreeSet::new();
16536        let mut cursor = root.walk();
16537        let mut stack = vec![root];
16538        while let Some(node) = stack.pop() {
16539            if matches!(
16540                node.kind(),
16541                "class_specifier" | "struct_specifier" | "union_specifier"
16542            ) && let Some(name) = class_like_name(node, source, &ancestry)
16543            {
16544                names.insert(name);
16545            }
16546            nodes.push(node);
16547            stack.extend(node.named_children(&mut cursor));
16548        }
16549        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
16550        assert!(!names.is_empty(), "fixture declares no class-like name");
16551
16552        let mut answered = 0usize;
16553        for reversed in [false, true] {
16554            let mut scan = CppNamespaceForwardScan::default();
16555            let ordered: Vec<_> = if reversed {
16556                nodes.iter().rev().copied().collect()
16557            } else {
16558                nodes.clone()
16559            };
16560            answered = 0;
16561            for node in ordered {
16562                for name in &names {
16563                    scan.advance_to(root, node.start_byte(), source, &ancestry);
16564                    let carried = scan.unique_earlier_forward(name, node);
16565                    assert_eq!(
16566                        carried,
16567                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
16568                        "carried-forward scan and prefix scan disagree about {name} at \
16569                         {} node starting at byte {} (reversed order: {reversed})",
16570                        node.kind(),
16571                        node.start_byte()
16572                    );
16573                    answered += usize::from(carried.is_some());
16574                }
16575            }
16576        }
16577        answered
16578    }
16579
16580    /// A malformed namespace whose forward declarations are the only identity
16581    /// signal left for the class definitions tree-sitter pushed out to file
16582    /// scope.  Both recovered classes open the guard; only the first one is
16583    /// separated from the namespace by nothing but recovery trivia, so only the
16584    /// first one borrows.  The carried-forward scan has to reproduce both
16585    /// answers.
16586    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
16587namespace ns {
16588class Widget;
16589class Gadget;
16590int x = ;
16591}
16592class API Widget {
16593public:
16594    void first();
16595};
16596class API Gadget {
16597public:
16598    void second();
16599};
16600"#;
16601
16602    #[test]
16603    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
16604        assert!(
16605            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
16606            "the fixture must actually reach the namespace-borrow path"
16607        );
16608
16609        // Nothing here may be answered, and the two paths have to agree about
16610        // that too: a clean namespace is not an identity proof, two forwards of
16611        // one name are ambiguous rather than a guess, and a forward inside a
16612        // function body is not at namespace scope.
16613        for source in [
16614            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
16615            r#"#define API
16616namespace ns {
16617class Widget;
16618class Widget;
16619int x = ;
16620}
16621class API Widget {
16622public:
16623    void method();
16624};
16625"#,
16626            r#"#define API
16627namespace ns {
16628void host() {
16629    class Widget;
16630}
16631int x = ;
16632}
16633class API Widget {
16634public:
16635    void method();
16636};
16637"#,
16638        ] {
16639            assert_eq!(
16640                namespace_forward_scan_agreement(source),
16641                0,
16642                "no borrow is justified here: {source}"
16643            );
16644        }
16645    }
16646
16647    /// The fold is incremental, so a walk that asks about steadily later bytes
16648    /// must never re-fold a node an earlier question already folded, and must
16649    /// never skip one that lies between two questions.
16650    #[test]
16651    fn carried_forward_namespace_scan_folds_each_node_once() {
16652        let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
16653        let mut parser = tree_sitter::Parser::new();
16654        parser
16655            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16656            .unwrap();
16657        let tree = parser.parse(source, None).unwrap();
16658        let root = tree.root_node();
16659        let ancestry = ParentIndex::new(root);
16660
16661        let mut incremental = CppNamespaceForwardScan::default();
16662        for cutoff in 0..=source.len() {
16663            incremental.advance_to(root, cutoff, source, &ancestry);
16664        }
16665        let mut whole = CppNamespaceForwardScan::default();
16666        whole.advance_to(root, source.len(), source, &ancestry);
16667
16668        let mut incremental_shape: Vec<_> = incremental
16669            .forwards
16670            .iter()
16671            .map(|(name, forwards)| {
16672                (
16673                    name.clone(),
16674                    forwards
16675                        .iter()
16676                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
16677                        .collect::<Vec<_>>(),
16678                )
16679            })
16680            .collect();
16681        let mut whole_shape: Vec<_> = whole
16682            .forwards
16683            .iter()
16684            .map(|(name, forwards)| {
16685                (
16686                    name.clone(),
16687                    forwards
16688                        .iter()
16689                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
16690                        .collect::<Vec<_>>(),
16691                )
16692            })
16693            .collect();
16694        incremental_shape.sort();
16695        whole_shape.sort();
16696        for (_, forwards) in &mut incremental_shape {
16697            forwards.sort();
16698        }
16699        for (_, forwards) in &mut whole_shape {
16700            forwards.sort();
16701        }
16702
16703        assert!(!whole_shape.is_empty(), "fixture folds no forward");
16704        assert_eq!(
16705            incremental_shape, whole_shape,
16706            "one byte at a time must fold exactly what one whole pass folds"
16707        );
16708    }
16709
16710    /// Drive the region reparse and the whitespace-padded reparse it replaced
16711    /// over the same region and require identical trees.
16712    ///
16713    /// The release build has no `debug_assertions` agreement check, so this is
16714    /// what pins the two together there. Each body is reparsed at its own
16715    /// offset and again after a long prefix, because the prefix is the whole
16716    /// difference between the two techniques: the padded parse lexes it as
16717    /// whitespace, the included-range parse never sees it, and the tree has to
16718    /// come out the same either way (#2788).
16719    fn fragmented_class_reparse_agreement(body: &str) {
16720        for prefix in [
16721            String::new(),
16722            "// leading comment\n".to_string(),
16723            // A body starts just after its class head's `{`, which is normally
16724            // mid-line: the padded parse then has spaces before the region on
16725            // the region's own line, and the included-range parse has nothing
16726            // at all before it.
16727            "class Widget : public Base { ".to_string(),
16728            "namespace filler {\n".to_string()
16729                + &"struct Filler { int member; };\n".repeat(200)
16730                + "}\n",
16731            "namespace filler {\n".to_string()
16732                + &"struct Filler { int member; };\n".repeat(200)
16733                + "}\nclass Widget : public Base { ",
16734        ] {
16735            let source = format!("{prefix}{body}");
16736            let start = prefix.len();
16737            let end = source.len();
16738            let region = cpp_reparse_fragmented_class_body(&source, start, end)
16739                .expect("the region reparse must produce a tree");
16740            let padded = cpp_reparse_padded_class_body(&source, start, end)
16741                .expect("the padded reparse must produce a tree");
16742            assert_eq!(
16743                cpp_tree_shape(&region),
16744                cpp_tree_shape(&padded),
16745                "region and padded reparse disagree at offset {start} of {end} bytes"
16746            );
16747            assert_eq!(
16748                region.root_node().start_byte(),
16749                start,
16750                "the reparsed region keeps its original offsets"
16751            );
16752        }
16753    }
16754
16755    #[test]
16756    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
16757        // A conditional immediately after an access label: the shape the padded
16758        // technique was kept for, because the directive and its macro name
16759        // become an ERROR plus the following declaration's apparent type.
16760        fragmented_class_reparse_agreement(
16761            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
16762        );
16763        fragmented_class_reparse_agreement(
16764            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
16765        );
16766        // The merged inline constructor and the nested fragmented bodies the
16767        // #938 recovery reads out of a reparse.
16768        fragmented_class_reparse_agreement(
16769            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
16770        );
16771        fragmented_class_reparse_agreement(
16772            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
16773        );
16774        // A body that is not member-shaped at all still has to reparse the same
16775        // way, because the admission gate reads the tree to reject it.
16776        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
16777    }
16778
16779    /// A header shaped like the generated ones this walk is slow on: many
16780    /// enums, classes whose members share the enums' names, nested enums, an
16781    /// ownerless enumerator, and namespaced repeats of all of it.
16782    fn many_enums_and_mixed_declarations() -> String {
16783        let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
16784        for index in 0..40 {
16785            let _ = write!(
16786                source,
16787                "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
16788                 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
16789                 class Color{index}Like {{ public: int member{index}; }};\n"
16790            );
16791        }
16792        source.push_str("namespace outer {\n");
16793        for index in 0..20 {
16794            let _ = write!(
16795                source,
16796                "enum Shade{index} {{ DARK{index} }};\n\
16797                 struct Shade{index}Holder {{ int field{index}; }};\n"
16798            );
16799        }
16800        source.push_str("}\n");
16801        source
16802    }
16803
16804    /// Drive [`CppFieldOwnerIndex`] and the declaration scan it replaced over
16805    /// every question a fixture's declarations can ask, and require the same
16806    /// answer from both.
16807    ///
16808    /// The release build has no `debug_assertions` agreement check, so this is
16809    /// what pins the two together there. The index is fed one declaration at a
16810    /// time and every question is re-asked after each one, which is what proves
16811    /// the incremental record agrees -- a whole-set rebuild would pass a weaker
16812    /// test. Each of the fixture's own fields is also fed in restated as a
16813    /// declaration of another file: the scan ignores those because it asks
16814    /// about the asking unit's own source, and the index has to ignore them for
16815    /// the same reason.
16816    ///
16817    /// Returns how many questions the fixture answered `true`, so a caller can
16818    /// assert that it actually reached the path (#2786).
16819    fn field_owner_index_agreement(source: &str, name: &str) -> usize {
16820        let parsed = parse_cpp_declarations(source, name);
16821        let file = ProjectFile::new(std::env::temp_dir(), name);
16822        let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
16823
16824        let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
16825        declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
16826
16827        let foreign: Vec<CodeUnit> = declarations
16828            .iter()
16829            .filter(|unit| unit.kind() == CodeUnitType::Field)
16830            .map(|unit| {
16831                CodeUnit::new_fq(
16832                    elsewhere.clone(),
16833                    unit.kind(),
16834                    unit.package_name().to_string(),
16835                    unit.short_name().to_string(),
16836                    unit.fq().clone(),
16837                )
16838            })
16839            .collect();
16840
16841        // One owner chain deeper than any C++ short name reaches today
16842        // (`cpp_member_fq`: at most one `.`, separating the owner chain from
16843        // the member). The scan asks `starts_with("owner.")`, so a field like
16844        // this answers for every owner in its chain, and the index has to
16845        // record every one of them rather than only the innermost.
16846        let mut packages: Vec<String> = declarations
16847            .iter()
16848            .map(|unit| unit.package_name().to_string())
16849            .collect();
16850        packages.push(String::new());
16851        packages.sort();
16852        packages.dedup();
16853        let deeper: Vec<CodeUnit> = packages
16854            .iter()
16855            .map(|package_name| {
16856                CodeUnit::new_fq(
16857                    file.clone(),
16858                    CodeUnitType::Field,
16859                    package_name.clone(),
16860                    "SynthOwner.middle.leaf".to_string(),
16861                    cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
16862                )
16863            })
16864            .collect();
16865
16866        // Every (package, owner) pair anything could ask about: each unit's own
16867        // short name, each dotted prefix of it, and the empty owner an
16868        // anonymous enum asks with (#2140).
16869        let mut questions: Vec<(String, String)> = Vec::new();
16870        for unit in declarations.iter().chain(deeper.iter()) {
16871            let package_name = unit.package_name().to_string();
16872            let short_name = unit.short_name();
16873            questions.push((package_name.clone(), short_name.to_string()));
16874            questions.push((package_name.clone(), String::new()));
16875            for (offset, _) in short_name.match_indices('.') {
16876                questions.push((package_name.clone(), short_name[..offset].to_string()));
16877            }
16878        }
16879        questions.sort();
16880        questions.dedup();
16881
16882        let mut index = CppFieldOwnerIndex::default();
16883        let mut recorded: Vec<&CodeUnit> = Vec::new();
16884        let mut answered = 0usize;
16885        for unit in foreign
16886            .iter()
16887            .chain(declarations.iter())
16888            .chain(deeper.iter())
16889        {
16890            index.record(unit, &file);
16891            recorded.push(unit);
16892            for (package_name, owner_short_name) in &questions {
16893                let carried = index.owns_fields(package_name, owner_short_name);
16894                assert_eq!(
16895                    carried,
16896                    cpp_declarations_hold_owned_fields(
16897                        recorded.iter().copied(),
16898                        &file,
16899                        package_name,
16900                        owner_short_name
16901                    ),
16902                    "the carried field index and the declaration scan disagree about \
16903                     {package_name:?}/{owner_short_name:?} after recording {}",
16904                    unit.fq_name()
16905                );
16906                answered += usize::from(carried);
16907            }
16908        }
16909
16910        // The whole-set build the first question performs must land on the same
16911        // index the incremental record built.
16912        let rebuilt = CppFieldOwnerIndex::of(
16913            foreign
16914                .iter()
16915                .chain(declarations.iter())
16916                .chain(deeper.iter()),
16917            &file,
16918        );
16919        for (package_name, owner_short_name) in &questions {
16920            assert_eq!(
16921                rebuilt.owns_fields(package_name, owner_short_name),
16922                index.owns_fields(package_name, owner_short_name),
16923                "a rebuilt index must answer what the incremental one answers for \
16924                 {package_name:?}/{owner_short_name:?}"
16925            );
16926        }
16927        answered
16928    }
16929
16930    /// The one thing the field index cannot absorb by addition: a deferred
16931    /// replacement of a declaration that owns children removes those children.
16932    ///
16933    /// The first enum builds the index, the struct records `Color.RED` into it,
16934    /// the body-less second `Color` replaces the first and takes `Color.RED`
16935    /// with it, and the last enum then asks about owner `Color`. An index that
16936    /// survived that removal answers `true` where the declarations say `false`,
16937    /// which is exactly what the in-walk agreement assertion catches (#2786).
16938    #[test]
16939    fn a_replacement_that_removes_children_drops_the_field_index() {
16940        let source =
16941            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
16942        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
16943        let mut names: Vec<_> = parsed
16944            .declarations()
16945            .iter()
16946            .map(|unit| unit.fq_name())
16947            .collect();
16948        names.sort();
16949        assert_eq!(
16950            names,
16951            vec![
16952                "Color".to_string(),
16953                "First".to_string(),
16954                "First.A".to_string()
16955            ],
16956            "the replaced Color owns no field any more"
16957        );
16958    }
16959
16960    /// A recovery that re-declares what the file already declared mints
16961    /// nothing.
16962    ///
16963    /// The reparse walk replaces the outer `Widget`, which removes its method,
16964    /// and then re-creates that method from the region. Creation alone would
16965    /// call the method recovered; it was there before the recovery opened, so
16966    /// the recovered set is empty and only the region's own reparse window is
16967    /// recorded (#2787).
16968    #[test]
16969    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
16970        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
16971                      BEGIN_NS\n\
16972                      namespace demo { struct Widget { void doWork(); }; }\n\
16973                      END_NS\n";
16974        let parsed = parse_cpp_declarations(source, "restored.cpp");
16975        let recovered: Vec<String> = parsed
16976            .materialization_records
16977            .iter()
16978            .filter_map(|record| match record {
16979                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
16980                _ => None,
16981            })
16982            .collect();
16983        assert!(
16984            recovered.is_empty(),
16985            "the region declares nothing the file did not already declare: {recovered:?}"
16986        );
16987        let mut names: Vec<String> = parsed
16988            .declarations()
16989            .iter()
16990            .map(|unit| unit.fq_name())
16991            .collect();
16992        names.sort();
16993        assert_eq!(
16994            names,
16995            vec![
16996                "demo".to_string(),
16997                "demo.Widget".to_string(),
16998                "demo.Widget.doWork".to_string(),
16999            ]
17000        );
17001    }
17002
17003    /// Four macro-sentinel recoveries in one file (#941). Each one must record
17004    /// exactly the declarations it minted -- not the ones an earlier recovery
17005    /// minted, and not the file's other declarations -- in start-byte order,
17006    /// and each record must carry its own reparse window (#2787).
17007    #[test]
17008    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
17009        let mut source = String::new();
17010        for index in 0..4 {
17011            let _ = write!(
17012                source,
17013                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
17014            );
17015        }
17016        source.push_str("void outside() {}\n");
17017        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
17018
17019        let recovered: Vec<(String, (usize, usize))> = parsed
17020            .materialization_records
17021            .iter()
17022            .filter_map(|record| match record {
17023                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
17024                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
17025                }
17026                _ => None,
17027            })
17028            .collect();
17029
17030        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
17031        for index in 0..4 {
17032            // The window the reparse covers: everything after the opening
17033            // sentinel token up to the newline before the closing one.
17034            let region = format!("namespace demo{index}");
17035            let region_start = source.find(&region).expect("each region is in the source");
17036            let start = source[..region_start]
17037                .rfind("BEGIN_NS")
17038                .expect("each region opens with a sentinel")
17039                + "BEGIN_NS".len();
17040            let end = start
17041                + source[start..]
17042                    .find("END_NS")
17043                    .expect("each region closes with a sentinel")
17044                - 1;
17045            let window = (start, end);
17046            for name in [
17047                format!("demo{index}"),
17048                format!("demo{index}.Widget{index}"),
17049                format!("demo{index}.Widget{index}.doWork{index}"),
17050            ] {
17051                expected.push((name, window));
17052            }
17053        }
17054        assert_eq!(
17055            recovered, expected,
17056            "each recovery records its own minted declarations, in order"
17057        );
17058        assert!(
17059            parsed
17060                .declarations()
17061                .iter()
17062                .any(|unit| unit.fq_name() == "outside"),
17063            "the declaration outside every region stays parsed and unrecovered"
17064        );
17065    }
17066
17067    #[test]
17068    fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
17069        assert!(
17070            field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
17071            "the fixture must actually own fields"
17072        );
17073
17074        // The shapes that make the two implementations diverge if the index
17075        // records the wrong keys: a nested enum's owner is its whole dotted
17076        // chain, a class named like an enum owns fields under that same name, a
17077        // `$` in a short name is an owner separator the dotted prefix rule must
17078        // not split on, and the same enum name in two namespaces is two owners.
17079        for (source, name) in [
17080            ("struct S { enum E { V }; };\n", "nested.hpp"),
17081            (
17082                "enum Color { RED };\nstruct Color { int RED; };\n",
17083                "class-like.c",
17084            ),
17085            ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
17086            (
17087                "enum E { V };\nnamespace ns { enum E { V }; }\n",
17088                "repeated.hpp",
17089            ),
17090            ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
17091        ] {
17092            field_owner_index_agreement(source, name);
17093        }
17094    }
17095}