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
1419/// One declaration an attribute-like macro invocation swallowed into a
1420/// declaration-scope `ERROR`, with the byte range that spells it.
1421/// What [`stranded_declaration_run`] read out of one node.
1422struct StrandedRun<'tree> {
1423    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1424    /// Whether every part of the node read as part of a declaration. False when
1425    /// a part the reader does not understand ended it early, or when the last
1426    /// parts were types with no declarator after them. Callers that index what
1427    /// was found keep the declarations either way; a caller deciding whether a
1428    /// whole region is safe to index requires this.
1429    complete: bool,
1430}
1431
1432struct MacroWrappedDeclaration<'tree> {
1433    declarator: Node<'tree>,
1434    range: Range,
1435    /// Whether the recovered declaration spells `static`. The envelope hides
1436    /// that keyword from the ordinary linkage reader, which looks for it among
1437    /// a declaration node's own children, and internal linkage is what decides
1438    /// whether a header declaration and a body in another file are one symbol.
1439    is_static: bool,
1440}
1441
1442/// Whether `node` stands where declarations live: directly in the translation
1443/// unit, or in a `namespace` or `extern "C"` body.
1444fn is_declaration_scope_position(node: Node<'_>) -> bool {
1445    let Some(parent) = node.parent() else {
1446        return false;
1447    };
1448    match parent.kind() {
1449        "translation_unit" => true,
1450        "declaration_list" => parent.parent().is_some_and(|grandparent| {
1451            matches!(
1452                grandparent.kind(),
1453                "namespace_definition" | "linkage_specification"
1454            )
1455        }),
1456        _ => false,
1457    }
1458}
1459
1460/// Whether `node` is an `ERROR` the parser produced where declarations live.
1461fn is_declaration_scope_error(node: Node<'_>) -> bool {
1462    node.kind() == "ERROR" && is_declaration_scope_position(node)
1463}
1464
1465/// Whether `node` can only be part of what precedes a declarator -- a type, a
1466/// specifier, or a word of the attribute macro's own text that the lexer left
1467/// as a bare identifier -- so a run of these followed by a declarator is one
1468/// declaration the parser failed to group.
1469fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1470    matches!(
1471        node.kind(),
1472        "identifier"
1473            | "type_identifier"
1474            | "primitive_type"
1475            | "sized_type_specifier"
1476            | "struct_specifier"
1477            | "union_specifier"
1478            | "enum_specifier"
1479            | "type_qualifier"
1480            | "storage_class_specifier"
1481            | "explicit_function_specifier"
1482            | "virtual_function_specifier"
1483            | "qualified_identifier"
1484            | "template_type"
1485            | "dependent_type"
1486            | "placeholder_type_specifier"
1487    )
1488}
1489
1490/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
1491/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
1492/// words the lexer reports as bare identifiers. Anything else in such an
1493/// `ERROR` stops the recovery, so a macro argument that carries structure this
1494/// recovery does not understand is never guessed at.
1495fn is_macro_argument_error(node: Node<'_>) -> bool {
1496    if node.kind() != "ERROR" {
1497        return false;
1498    }
1499    let mut cursor = node.walk();
1500    node.named_children(&mut cursor).all(|child| {
1501        matches!(
1502            child.kind(),
1503            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1504        )
1505    })
1506}
1507
1508/// The end of a recovered declaration: the declarator's end, extended across
1509/// the `;` the grammar left beside it inside the envelope.
1510///
1511/// The envelope's own end is the hard boundary. The parser hands the last
1512/// declaration's `;` to the sibling statement it recovered with, outside the
1513/// envelope, and a range that reached it would no longer lie inside one node --
1514/// which is how every reader (including the resolver's climb from a range to
1515/// the node that declares it) finds a recovered declaration again.
1516fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1517    declarator
1518        .next_sibling()
1519        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1520        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1521}
1522
1523/// The declarations `node` holds after an attribute-like macro cost the parser
1524/// their grouping, in source order.
1525///
1526/// The parser packs the parts into whichever slots it has left. In whisper's
1527/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
1528/// field takes the export macro, the real return type and the *next*
1529/// declaration's declarator both end up inside one sibling `ERROR`, and the
1530/// `declarator` field takes whatever declaration the recovery reached last. In
1531/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
1532/// arrive as bare identifiers and the attributed member and the member after it
1533/// share one declarator node.
1534///
1535/// Both are the same failure, so both get the same reading: flatten the node's
1536/// parts -- splicing each nested `ERROR`'s own children in place, since an
1537/// `ERROR` here is only a grouping failure -- and read the flat run as what it
1538/// spells, a run of type-and-specifier tokens followed by a declarator, over
1539/// and over.
1540///
1541/// Fails closed. A part that is neither a declarator nor something that can
1542/// only precede one ends the recovery there, and the declarations before it are
1543/// kept.
1544fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1545    let mut parts = Vec::new();
1546    let mut cursor = node.walk();
1547    for child in node.named_children(&mut cursor) {
1548        if child.kind() == "ERROR" {
1549            let mut error_cursor = child.walk();
1550            parts.extend(child.named_children(&mut error_cursor));
1551        } else {
1552            parts.push(child);
1553        }
1554    }
1555
1556    let mut declarations = Vec::new();
1557    let mut start = None;
1558    let mut is_static = false;
1559    let mut complete = true;
1560    for part in parts {
1561        if part.kind() == "comment" {
1562            continue;
1563        }
1564        if let Some(declarator) = extract_function_declarator(part) {
1565            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1566            declarations.push(MacroWrappedDeclaration {
1567                declarator,
1568                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1569                is_static,
1570            });
1571            is_static = false;
1572            continue;
1573        }
1574        if !is_recovered_declaration_type_part(part) {
1575            complete = false;
1576            break;
1577        }
1578        is_static |= part.kind() == "storage_class_specifier"
1579            && normalize_cpp_whitespace(node_text(part, source)) == "static";
1580        start.get_or_insert(part.start_byte());
1581    }
1582    StrandedRun {
1583        declarations,
1584        complete: complete && start.is_none(),
1585    }
1586}
1587
1588/// The declarations an attribute-like macro invocation swallowed into a
1589/// declaration-scope `ERROR`, in source order.
1590///
1591/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
1592/// declaration in a macro call:
1593///
1594/// ```text
1595/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
1596///                  struct llama_model * model,
1597///           struct llama_context_params   params),
1598///         "use llama_init_from_model instead");
1599/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
1600/// ```
1601///
1602/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
1603/// holding the macro name, the wrapped declaration as a
1604/// `parameter_declaration`, the hint string as another `ERROR`, and then every
1605/// following declaration as a further `parameter_declaration` until it
1606/// recovers. That is what removed `llama_tokenize` from the index and left the
1607/// seven-argument call in `talk-llama.cpp` with only that file's own
1608/// three-parameter `static` overload to choose from (#2552, and the
1609/// `LLAMA_API` half of #2551).
1610///
1611/// Every part of every swallowed declaration is still a real node; only their
1612/// grouping is lost. This rebuilds the grouping and reads the nodes.
1613fn macro_wrapped_declarations<'tree>(
1614    envelope: Node<'tree>,
1615    source: &str,
1616) -> Vec<MacroWrappedDeclaration<'tree>> {
1617    let mut declarations = Vec::new();
1618    if !is_declaration_scope_error(envelope) {
1619        return declarations;
1620    }
1621    let mut cursor = envelope.walk();
1622    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1623    let [macro_name, arguments @ ..] = children.as_slice() else {
1624        return declarations;
1625    };
1626    if macro_name.kind() != "identifier" {
1627        return declarations;
1628    }
1629    let mut wrapped_declaration_seen = false;
1630    for argument in arguments {
1631        match argument.kind() {
1632            "comment" => {}
1633            "parameter_declaration" => {
1634                let recovered = stranded_declaration_run(*argument, source).declarations;
1635                if recovered.is_empty() {
1636                    break;
1637                }
1638                wrapped_declaration_seen = true;
1639                declarations.extend(recovered);
1640            }
1641            // The hint string, and only that: an argument the recovery cannot
1642            // read as a declaration is admitted before the wrapped declaration
1643            // is found, so a macro whose first argument is not a declaration
1644            // recovers nothing.
1645            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1646            _ => break,
1647        }
1648    }
1649    declarations
1650}
1651
1652/// What one macro invocation swallowed when it collapsed a whole run of
1653/// declarations into a single node.
1654struct CollapsedMacroDeclarationRun {
1655    /// The byte just past the `;` that closes the invocation, which is where
1656    /// the declarations it swallowed begin.
1657    invocation_end: usize,
1658}
1659
1660/// The macro invocation at the head of `node` when it swallowed the
1661/// declarations written after it, or `None` when `node` is not that shape.
1662///
1663/// whisper.cpp's bundled `llama.h` writes
1664///
1665/// ```text
1666/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
1667///                          const char * path_model,
1668///           struct llama_model_params   params),
1669///         "use llama_model_load_from_file instead");
1670/// ```
1671///
1672/// The wrapped declaration's own parameter list spans lines, and that alone is
1673/// enough -- no stack of such items, no `extern "C"` block -- for the parser to
1674/// read `DEPRECATED(` as a function declarator whose close it never finds. It
1675/// then consumes every declaration written after it: in the real header, 57 KB
1676/// from line 481 to line 1535, `llama_tokenize` included, which is the
1677/// `LLAMA_API` half of #2551. A `MACRO(decl, "hint");` whose wrapped
1678/// declaration fits on its line collapses nothing; the parser leaves the
1679/// declaration-scope `ERROR` that [`macro_wrapped_declarations`] reads.
1680///
1681/// The envelope is a `function_definition` when a brace block falls in the
1682/// swallowed tail -- the parser borrows it for the body the bogus definition
1683/// needs -- and an `ERROR` when none does. That says nothing about the
1684/// construct, so both are accepted. Neither is the shape
1685/// [`macro_wrapped_declarations`] reads, whose first named child is the bare
1686/// macro-name `identifier` with no declarator around it.
1687///
1688/// Fails closed. The first argument must be a declaration and the argument
1689/// after it must be the one the parser handed the invocation's own `)` and `;`,
1690/// so a macro call whose end this cannot name is left to the ordinary readers.
1691fn collapsed_macro_declaration_run(
1692    node: Node<'_>,
1693    source: &str,
1694) -> Option<CollapsedMacroDeclarationRun> {
1695    if !matches!(node.kind(), "function_definition" | "ERROR")
1696        || !is_declaration_scope_position(node)
1697    {
1698        return None;
1699    }
1700    let head = if node.kind() == "function_definition" {
1701        // A real definition names its return type here. The envelope has none:
1702        // the macro name took the declarator slot and nothing precedes it.
1703        if node.child_by_field_name("type").is_some() {
1704            return None;
1705        }
1706        node.child_by_field_name("declarator")?
1707    } else {
1708        node.named_child(0)?
1709    };
1710    let mut invocation = extract_function_declarator(head)?;
1711    if invocation.start_byte() != node.start_byte() {
1712        return None;
1713    }
1714    // Each declaration the invocation swallowed wraps another
1715    // `function_declarator` around the one before it, so the macro's own
1716    // invocation is the innermost.
1717    while let Some(inner) = invocation
1718        .child_by_field_name("declarator")
1719        .filter(|inner| inner.kind() == "function_declarator")
1720    {
1721        invocation = inner;
1722    }
1723    let name = invocation.child_by_field_name("declarator")?;
1724    if name.kind() != "identifier"
1725        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
1726    {
1727        return None;
1728    }
1729    let arguments = invocation.child_by_field_name("parameters")?;
1730    let mut cursor = arguments.walk();
1731    let mut children = arguments
1732        .children(&mut cursor)
1733        .filter(|child| child.kind() != "comment");
1734    if children.next()?.kind() != "(" || children.next()?.kind() != "parameter_declaration" {
1735        return None;
1736    }
1737    // The argument after the wrapped declaration is where the parser put the
1738    // invocation's own `)` and `;`. Their adjacency inside that argument is
1739    // where the invocation ends; whatever follows them there, or after the
1740    // argument, is what the invocation swallowed. A `)` the lexer left inside
1741    // the hint text is not followed by a `;`, so the pair names the end and
1742    // nothing else does.
1743    let hint = children.find(|child| child.kind() != ",")?;
1744    if hint.kind() != "ERROR" {
1745        return None;
1746    }
1747    let mut hint_cursor = hint.walk();
1748    let parts = hint.children(&mut hint_cursor).collect::<Vec<_>>();
1749    let invocation_end = parts.windows(2).find_map(|pair| {
1750        let [close, semicolon] = pair else {
1751            return None;
1752        };
1753        (close.kind() == ")"
1754            && !close.is_missing()
1755            && semicolon.kind() == ";"
1756            && !semicolon.is_missing())
1757        .then(|| semicolon.end_byte())
1758    })?;
1759    // An invocation that ends where the node does swallowed nothing after it.
1760    (invocation_end < node.end_byte()).then_some(CollapsedMacroDeclarationRun { invocation_end })
1761}
1762
1763/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
1764/// ordinary class body, and the members it swallowed.
1765///
1766/// Botan deprecates members that way:
1767///
1768/// ```text
1769/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
1770/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
1771/// ```
1772///
1773/// The parser reads the macro name as the member's type and its argument list
1774/// as a parenthesized declarator, which then swallows the attributed member
1775/// *and* the member written after it: a `field_declaration` whose `type` is a
1776/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
1777/// opening with an `ERROR` whose first part is a bare identifier -- the first
1778/// word of the string, which the lexer could not keep together.
1779///
1780/// The macro's spelling is not the criterion; that structural shape is. The
1781/// returned declarations are in source order, the first being the attributed
1782/// member and the rest the members the declarator swallowed after it.
1783fn string_attribute_macro_member_declarators<'tree>(
1784    field: Node<'tree>,
1785    source: &str,
1786) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
1787    if field.kind() != "field_declaration"
1788        || field
1789            .child_by_field_name("type")
1790            .is_none_or(|type_node| type_node.kind() != "type_identifier")
1791    {
1792        return None;
1793    }
1794    let declarator = field.child_by_field_name("declarator")?;
1795    if declarator.kind() != "parenthesized_declarator" {
1796        return None;
1797    }
1798    let opening = declarator.named_child(0)?;
1799    if opening.kind() != "ERROR"
1800        || opening
1801            .named_child(0)
1802            .is_none_or(|word| word.kind() != "identifier")
1803    {
1804        return None;
1805    }
1806    let declarations = stranded_declaration_run(declarator, source).declarations;
1807    (!declarations.is_empty()).then_some(declarations)
1808}
1809
1810/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
1811/// export-macro class body leaves in front of the members that macro decorated.
1812///
1813/// The reparse reads the class body as statements, so the attribute becomes a
1814/// call statement of its own -- with the `;` the grammar had to invent -- and
1815/// the members after it are stranded in the `ERROR` that follows.
1816fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
1817    let Some(call) = (node.kind() == "expression_statement")
1818        .then(|| node.named_child(0))
1819        .flatten()
1820        .filter(|child| child.kind() == "call_expression")
1821    else {
1822        return false;
1823    };
1824    call.child_by_field_name("function")
1825        .is_some_and(|function| function.kind() == "identifier")
1826        && call
1827            .child_by_field_name("arguments")
1828            .is_some_and(|arguments| {
1829                let mut cursor = arguments.walk();
1830                arguments.named_child_count() > 0
1831                    && arguments
1832                        .named_children(&mut cursor)
1833                        .all(|argument| argument.kind() == "string_literal")
1834            })
1835}
1836
1837/// The start byte of the call the region reparse left where an access-labeled
1838/// constructor was written.
1839///
1840/// A constructor is a member only inside a class body. The export-macro class
1841/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
1842/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
1843/// comma-expression under `private:`, with no declarator left in the tree. It
1844/// is still spelled in the source, though, starting at the one call under the
1845/// label whose callee is the class's own name. Botan's private six-parameter
1846/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
1847/// call is an ambiguity this declines, and so is a reparse from that byte that
1848/// does not yield exactly one constructor declarator there.
1849fn cpp_access_label_constructor_call_start(
1850    node: Node<'_>,
1851    class_name: &str,
1852    source: &str,
1853) -> Option<usize> {
1854    if node.kind() != "labeled_statement" {
1855        return None;
1856    }
1857    let label = node.named_child(0)?;
1858    if label.kind() != "statement_identifier"
1859        || !matches!(
1860            node_text(label, source).trim(),
1861            "public" | "private" | "protected"
1862        )
1863    {
1864        return None;
1865    }
1866    let mut starts = Vec::new();
1867    let mut stack = vec![node];
1868    while let Some(current) = stack.pop() {
1869        if current.kind() == "call_expression"
1870            && current
1871                .child_by_field_name("function")
1872                .is_some_and(|function| {
1873                    function.kind() == "identifier"
1874                        && node_text(function, source).trim() == class_name
1875                })
1876        {
1877            starts.push(current.start_byte());
1878        }
1879        let mut cursor = current.walk();
1880        stack.extend(current.named_children(&mut cursor));
1881    }
1882    let [start] = starts.as_slice() else {
1883        return None;
1884    };
1885    Some(*start)
1886}
1887
1888/// The `function_definition` that gives `declarator` a body, following only the
1889/// declarator chain, so a recovered callable knows whether it is a declaration
1890/// or a definition without anyone having to say.
1891fn cpp_declarator_function_definition<'tree>(
1892    declarator: Node<'tree>,
1893    ancestry: &ParentIndex<'tree>,
1894) -> Option<Node<'tree>> {
1895    let mut current = declarator;
1896    while let Some(parent) = ancestry.parent(current) {
1897        match parent.kind() {
1898            "function_definition" if parent.child_by_field_name("body").is_some() => {
1899                return Some(parent);
1900            }
1901            "pointer_declarator"
1902            | "reference_declarator"
1903            | "parenthesized_declarator"
1904            | "array_declarator" => current = parent,
1905            _ => return None,
1906        }
1907    }
1908    None
1909}
1910
1911/// Whether `node` lies in the body of a `namespace_definition` the ordinary
1912/// declaration walk still reaches, so a recovery that runs ahead of that walk
1913/// would name what it finds without the namespace.
1914fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1915    let mut current = node;
1916    while let Some(parent) = ancestry.parent(current) {
1917        if parent.kind() == "namespace_definition"
1918            && parent.child_by_field_name("body") == Some(current)
1919        {
1920            return true;
1921        }
1922        current = parent;
1923    }
1924    false
1925}
1926
1927/// Whether the source a recovered callable's byte range spells is a definition
1928/// (a declarator with a body) rather than a declaration.
1929///
1930/// A callable recovered from a mangled region owns no node of its own in the
1931/// file's tree, so the occurrence-role scan that climbs from the range to the
1932/// node containing it lands on whatever container the parser left and answers
1933/// for that instead -- which called Botan's inline `XMSS_Parameters` private
1934/// constructor a declaration and left its call site with nothing to navigate to
1935/// (#2552). Reparse the range on its own, the same offset-preserving reparse
1936/// extraction used, and read the answer from the one declaration it spells.
1937///
1938/// `None` when the range is not one recovered declaration on its own, which is
1939/// the case for every ordinary declaration, so callers keep their own answer.
1940pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
1941    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
1942    let root = tree.root_node();
1943    let mut cursor = root.walk();
1944    let items = root
1945        .named_children(&mut cursor)
1946        .filter(|child| child.kind() != "comment")
1947        .collect::<Vec<_>>();
1948    let [item] = items.as_slice() else {
1949        return None;
1950    };
1951    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
1952        return None;
1953    }
1954    match item.kind() {
1955        "function_definition" => Some(item.child_by_field_name("body").is_some()),
1956        "declaration" | "field_declaration" => Some(false),
1957        _ => None,
1958    }
1959}
1960
1961/// Whether `node` is an envelope an attribute-like macro invocation left where
1962/// declarations were written: the declaration-scope `ERROR`
1963/// [`macro_wrapped_declarations`] reads, or the collapsed run
1964/// [`collapsed_macro_declaration_run`] reads.
1965///
1966/// Extraction and resolution must read one definition of this shape. The
1967/// resolver climbs from a declaration's recorded byte range to the node that
1968/// declares it, and a recovered declaration's range lies inside one of these
1969/// envelopes rather than inside a `declaration` node, so the climb stops here
1970/// (the same role `is_recovered_exported_class_container` plays for a recovered
1971/// class).
1972pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
1973    !macro_wrapped_declarations(node, source).is_empty()
1974        || collapsed_macro_declaration_run(node, source).is_some()
1975}
1976
1977fn recover_exported_class_function_definition<'tree>(
1978    node: Node<'tree>,
1979    source: &str,
1980) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1981    if node.kind() != "function_definition" {
1982        return None;
1983    }
1984    if let Some(prefix) = node.prev_named_sibling()
1985        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
1986        && recovered.range.end_byte == node.end_byte()
1987    {
1988        return Some((node, recovered.name, recovered.raw_supertypes));
1989    }
1990    let type_node = node.child_by_field_name("type")?;
1991    let declarator = node.child_by_field_name("declarator")?;
1992
1993    if matches!(
1994        type_node.kind(),
1995        "class_specifier" | "struct_specifier" | "union_specifier"
1996    ) {
1997        let type_name = type_node
1998            .child_by_field_name("name")
1999            .and_then(|name| direct_identifier_name(name, source));
2000        let exported_macro_type = type_name
2001            .as_ref()
2002            .is_some_and(|name| cpp_export_macro_token(name));
2003        if exported_macro_type {
2004            let mut cursor = node.walk();
2005            let errors_before_declarator = node
2006                .named_children(&mut cursor)
2007                .filter(|child| {
2008                    child.kind() == "ERROR"
2009                        && child.start_byte() >= type_node.end_byte()
2010                        && child.end_byte() <= declarator.start_byte()
2011                })
2012                .collect::<Vec<_>>();
2013            if let Some(name) = errors_before_declarator
2014                .iter()
2015                .find_map(|error| displaced_exported_class_name(*error, source))
2016            {
2017                let raw_supertypes = errors_before_declarator
2018                    .iter()
2019                    .any(|error| malformed_inheritance_syntax(*error))
2020                    .then(|| recovered_malformed_base_name(declarator, source))
2021                    .flatten()
2022                    .map(|base| vec![base]);
2023                return Some((node, name, raw_supertypes));
2024            }
2025            if errors_before_declarator
2026                .iter()
2027                .any(|error| malformed_inheritance_syntax(*error))
2028            {
2029                return None;
2030            }
2031        }
2032        if !exported_macro_type
2033            && let Some(name) = type_name
2034            && !cpp_export_macro_token(&name)
2035            && let Some(base) =
2036                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2037        {
2038            return Some((node, name, Some(vec![base])));
2039        }
2040        if let Some(name) = direct_identifier_name(declarator, source)
2041            && exported_macro_type
2042            && !cpp_export_macro_token(&name)
2043        {
2044            let raw_supertypes = exported_macro_type
2045                .then(|| recovered_single_base_after_declarator(node, declarator, source))
2046                .flatten()
2047                .map(|base| vec![base]);
2048            return Some((node, name, raw_supertypes));
2049        }
2050        if declarator.kind() == "parenthesized_declarator"
2051            && type_node
2052                .child_by_field_name("name")
2053                .and_then(|name| direct_identifier_name(name, source))
2054                .is_some_and(|name| cpp_export_macro_token(&name))
2055        {
2056            if let Some((name, base)) =
2057                recovered_function_like_export_class_owner(declarator, source)
2058            {
2059                return Some((node, name, Some(vec![base])));
2060            }
2061            let body_start = node
2062                .child_by_field_name("body")
2063                .map(|body| body.start_byte())
2064                .unwrap_or(node.end_byte());
2065            let mut cursor = node.walk();
2066            if let Some(name) = node
2067                .named_children(&mut cursor)
2068                .filter(|child| {
2069                    child.kind() == "ERROR"
2070                        && child.start_byte() >= declarator.end_byte()
2071                        && child.end_byte() <= body_start
2072                })
2073                .find_map(|error| declarator_name_from_node(error, source))
2074            {
2075                return Some((node, name, None));
2076            }
2077        }
2078    }
2079
2080    let declarator_text = direct_identifier_name(declarator, source)?;
2081    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2082        return None;
2083    }
2084    class_identifier_before_body(node, source).map(|name| (node, name, None))
2085}
2086
2087fn recovered_function_like_export_class_owner(
2088    declarator: Node<'_>,
2089    source: &str,
2090) -> Option<(String, String)> {
2091    if declarator.kind() != "parenthesized_declarator" {
2092        return None;
2093    }
2094    let mut cursor = declarator.walk();
2095    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2096    let [prefix, base] = children.as_slice() else {
2097        return None;
2098    };
2099    if prefix.kind() != "ERROR"
2100        || !matches!(
2101            base.kind(),
2102            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2103        )
2104    {
2105        return None;
2106    }
2107    let mut identifiers = Vec::new();
2108    let mut prefix_cursor = prefix.walk();
2109    for child in prefix.named_children(&mut prefix_cursor) {
2110        match child.kind() {
2111            "number_literal" | "string_literal" | "char_literal" => {}
2112            "identifier" | "type_identifier" => {
2113                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2114            }
2115            _ => return None,
2116        }
2117    }
2118    let name = match identifiers.as_slice() {
2119        [name] => name.clone(),
2120        [name, final_token] if final_token == "final" => name.clone(),
2121        _ => return None,
2122    };
2123    if name.is_empty() || cpp_export_macro_token(&name) {
2124        return None;
2125    }
2126    let base = recovered_malformed_base_name(*base, source)?;
2127    Some((name, base))
2128}
2129
2130/// Collect the base names tree-sitter scattered across the recovered head of a
2131/// function-like export-macro class. `skip` names the structural children that
2132/// are not bases, such as the class name and the body. A base arrives either as
2133/// a direct sibling identifier or inside the `ERROR` node the grammar produced
2134/// for a `: public Base` fragment. The grammar leaves the `final` specifier and
2135/// the access specifiers in the same position as the bases, so drop them.
2136fn recovered_export_head_bases(node: Node<'_>, skip: &[Node<'_>], source: &str) -> Vec<String> {
2137    let mut bases = Vec::new();
2138    let mut cursor = node.walk();
2139    for child in node.named_children(&mut cursor) {
2140        if skip.iter().any(|skipped| same_node(child, *skipped)) {
2141            continue;
2142        }
2143        if child.kind() == "ERROR" {
2144            let mut error_cursor = child.walk();
2145            bases.extend(
2146                child
2147                    .named_children(&mut error_cursor)
2148                    .filter_map(|part| recovered_malformed_base_name(part, source)),
2149            );
2150        } else if let Some(base) = recovered_malformed_base_name(child, source) {
2151            bases.push(base);
2152        }
2153    }
2154    bases.retain(|base| !matches!(base.as_str(), "final" | "public" | "protected" | "private"));
2155    bases
2156}
2157
2158/// Read the bases and the initializer-list body of a `declaration`-shaped tail
2159/// of a function-like export-macro class head. The grammar splits a base list
2160/// across bare declarator fields, `ERROR` fragments, and one trailing
2161/// `init_declarator` whose `value` is the class body. `head` is the child that
2162/// carries the class identity rather than a base: the access specifier when the
2163/// class name went to a statement label, and the class name itself otherwise.
2164fn recovered_export_declaration_tail<'tree>(
2165    declaration: Node<'tree>,
2166    head: Node<'tree>,
2167    source: &str,
2168) -> Option<(Vec<String>, Node<'tree>)> {
2169    let mut cursor = declaration.walk();
2170    let init = declaration
2171        .named_children(&mut cursor)
2172        .find(|child| child.kind() == "init_declarator")?;
2173    let body = init.child_by_field_name("value")?;
2174    if body.kind() != "initializer_list" {
2175        return None;
2176    }
2177    let mut bases = recovered_export_head_bases(declaration, &[head, init], source);
2178    bases.extend(recovered_export_head_bases(init, &[body], source));
2179    Some((bases, body))
2180}
2181
2182fn recover_function_like_export_class_pair(
2183    node: Node<'_>,
2184    source: &str,
2185) -> Option<RecoveredFunctionLikeExportClassPair> {
2186    if node.kind() != "ERROR" {
2187        return None;
2188    }
2189    let class_node = first_class_like_child(node)?;
2190    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2191        return None;
2192    }
2193    let macro_name = class_node
2194        .child_by_field_name("name")
2195        .and_then(|name| direct_identifier_name(name, source))?;
2196    if !cpp_export_macro_token(&macro_name) {
2197        return None;
2198    }
2199    let sibling = node.next_named_sibling()?;
2200    let (name, raw_supertypes, body) = match sibling.kind() {
2201        "expression_statement" => {
2202            let compound = sibling.named_child(0)?;
2203            if compound.kind() != "compound_literal_expression" {
2204                return None;
2205            }
2206            let body = compound.child_by_field_name("value")?;
2207            if body.kind() != "initializer_list" {
2208                return None;
2209            }
2210            (
2211                compound
2212                    .child_by_field_name("type")
2213                    .and_then(|name| direct_identifier_name(name, source))?,
2214                None,
2215                body,
2216            )
2217        }
2218        "labeled_statement" => {
2219            let label = sibling.child_by_field_name("label")?;
2220            if label.kind() != "statement_identifier" {
2221                return None;
2222            }
2223            let name = normalize_cpp_whitespace(node_text(label, source));
2224            let declaration = sibling
2225                .named_children(&mut sibling.walk())
2226                .find(|child| child.kind() == "declaration")?;
2227            let access = declaration.child_by_field_name("type")?;
2228            if !matches!(
2229                node_text(access, source),
2230                "public" | "protected" | "private"
2231            ) {
2232                return None;
2233            }
2234            let (bases, body) = recovered_export_declaration_tail(declaration, access, source)?;
2235            (name, (!bases.is_empty()).then_some(bases), body)
2236        }
2237        // `class MACRO(2, 0) Name final { ... };` and
2238        // `class MACRO(2, 0) Name final : public Base { ... };`. The class name
2239        // lands in the `type` field, `final` in the declarator field, and every
2240        // base in an `ERROR` fragment beside them.
2241        "function_definition" => {
2242            let type_node = sibling.child_by_field_name("type")?;
2243            let body = sibling.child_by_field_name("body")?;
2244            if body.kind() != "compound_statement" {
2245                return None;
2246            }
2247            let name = direct_identifier_name(type_node, source)?;
2248            let bases = recovered_export_head_bases(sibling, &[type_node, body], source);
2249            (name, (!bases.is_empty()).then_some(bases), body)
2250        }
2251        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
2252        // comma-separated base list makes the grammar keep the whole tail as one
2253        // declaration whose `type` field is the class name.
2254        "declaration" => {
2255            let type_node = sibling.child_by_field_name("type")?;
2256            let name = direct_identifier_name(type_node, source)?;
2257            let (bases, body) = recovered_export_declaration_tail(sibling, type_node, source)?;
2258            (name, (!bases.is_empty()).then_some(bases), body)
2259        }
2260        _ => return None,
2261    };
2262    if name.is_empty() || cpp_export_macro_token(&name) {
2263        return None;
2264    }
2265    let range = Range {
2266        start_byte: node.start_byte(),
2267        end_byte: sibling.end_byte(),
2268        start_line: node.start_position().row + 1,
2269        end_line: sibling.end_position().row + 1,
2270    };
2271    Some(RecoveredFunctionLikeExportClassPair {
2272        name,
2273        raw_supertypes,
2274        range,
2275        fragmented_body: recovered_fragmented_export_body(body, range)?,
2276    })
2277}
2278
2279/// Recover a function-like export-macro class that tree-sitter embedded in a
2280/// larger error after an earlier malformed class body. The grammar still
2281/// preserves every part of the class head: the `class` token, export macro
2282/// identifier and argument list, displaced class identifier, access specifier,
2283/// base field, and initializer-list-shaped body. Match only that complete
2284/// structured sequence and keep each recovered class's exact byte envelope.
2285fn recover_embedded_function_like_export_classes(
2286    node: Node<'_>,
2287    source: &str,
2288) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2289    if node.kind() != "ERROR" {
2290        return Vec::new();
2291    }
2292
2293    let mut nodes = Vec::new();
2294    let mut stack = vec![node];
2295    while let Some(current) = stack.pop() {
2296        nodes.push(current);
2297        for index in (0..current.child_count()).rev() {
2298            stack.push(
2299                current
2300                    .child(index)
2301                    .expect("index below the node's own child count"),
2302            );
2303        }
2304    }
2305    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2306
2307    let mut recovered = Vec::new();
2308    for class_token in nodes
2309        .iter()
2310        .copied()
2311        .filter(|child| !child.is_named() && child.kind() == "class")
2312    {
2313        let row = class_token.start_position().row;
2314        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2315            candidate.start_byte() >= class_token.end_byte()
2316                && candidate.start_position().row == row
2317                && matches!(
2318                    candidate.kind(),
2319                    "identifier" | "type_identifier" | "field_identifier"
2320                )
2321                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*candidate, source)))
2322        }) else {
2323            continue;
2324        };
2325        let Some(arguments) = nodes.iter().copied().find(|candidate| {
2326            candidate.kind() == "argument_list"
2327                && candidate.start_byte() >= macro_name.end_byte()
2328                && candidate.start_position().row == row
2329        }) else {
2330            continue;
2331        };
2332        let Some(name_node) = nodes.iter().copied().find(|candidate| {
2333            candidate.kind() == "identifier"
2334                && candidate.start_byte() >= arguments.end_byte()
2335                && candidate.start_position().row == row
2336        }) else {
2337            continue;
2338        };
2339        let name = normalize_cpp_whitespace(node_text(name_node, source));
2340        if name.is_empty() || cpp_export_macro_token(&name) {
2341            continue;
2342        }
2343        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2344            candidate.kind() == "field_initializer"
2345                && candidate.start_byte() >= name_node.end_byte()
2346                && candidate
2347                    .child_by_field_name("field")
2348                    .or_else(|| candidate.named_child(0))
2349                    .is_some()
2350                && candidate
2351                    .child_by_field_name("value")
2352                    .or_else(|| {
2353                        let mut cursor = candidate.walk();
2354                        candidate
2355                            .named_children(&mut cursor)
2356                            .find(|child| child.kind() == "initializer_list")
2357                    })
2358                    .is_some_and(|value| value.kind() == "initializer_list")
2359        }) else {
2360            continue;
2361        };
2362        let has_access = nodes.iter().copied().any(|candidate| {
2363            candidate.start_byte() >= name_node.end_byte()
2364                && candidate.end_byte() <= base_initializer.start_byte()
2365                && matches!(
2366                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2367                    "public" | "protected" | "private"
2368                )
2369        });
2370        if !has_access {
2371            continue;
2372        }
2373        let Some(base_node) = base_initializer
2374            .child_by_field_name("field")
2375            .or_else(|| base_initializer.named_child(0))
2376        else {
2377            continue;
2378        };
2379        let Some(base) = recovered_malformed_base_name(base_node, source) else {
2380            continue;
2381        };
2382        let body = base_initializer
2383            .child_by_field_name("value")
2384            .or_else(|| {
2385                let mut cursor = base_initializer.walk();
2386                base_initializer
2387                    .named_children(&mut cursor)
2388                    .find(|child| child.kind() == "initializer_list")
2389            })
2390            .expect("initializer-list value checked above");
2391        let range = Range {
2392            start_byte: class_token.start_byte(),
2393            end_byte: body.end_byte(),
2394            start_line: class_token.start_position().row + 1,
2395            end_line: body.end_position().row + 1,
2396        };
2397        if recovered
2398            .iter()
2399            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
2400                existing.name == name && existing.range == range
2401            })
2402        {
2403            continue;
2404        }
2405        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
2406            name,
2407            range,
2408            raw_supertypes: vec![base],
2409            fragmented_body: match recovered_fragmented_export_body(body, range) {
2410                Some(fragmented) => fragmented,
2411                None => continue,
2412            },
2413        });
2414    }
2415    recovered
2416}
2417
2418fn lifted_function_like_export_class_namespace<'tree>(
2419    node: Node<'tree>,
2420    source: &str,
2421    ancestry: &ParentIndex<'tree>,
2422) -> Option<String> {
2423    // A long malformed body can embed the next exported class several levels
2424    // below a bogus top-level function_definition. Compare namespace evidence
2425    // against that top-level envelope, not only the recovered ERROR's direct
2426    // parent. The source tree still proves the same boundary: one earlier
2427    // malformed namespace and one later standalone closing brace.
2428    let mut anchor = node;
2429    let parent = loop {
2430        let parent = ancestry.parent(anchor)?;
2431        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
2432            break parent;
2433        }
2434        anchor = parent;
2435    };
2436    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
2437        sibling.start_byte() > anchor.end_byte()
2438            && sibling.kind() == "ERROR"
2439            && sibling.named_child_count() == 0
2440            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
2441    });
2442    if !has_later_close {
2443        return None;
2444    }
2445    let candidates = parent
2446        .named_children(&mut parent.walk())
2447        .filter(|sibling| {
2448            sibling.kind() == "namespace_definition"
2449                && sibling.has_error()
2450                && sibling.end_byte() < anchor.start_byte()
2451        })
2452        .filter_map(|namespace| {
2453            namespace
2454                .child_by_field_name("name")
2455                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
2456                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
2457        })
2458        .collect::<Vec<_>>();
2459    let [namespace] = candidates.as_slice() else {
2460        return None;
2461    };
2462    Some(namespace.clone())
2463}
2464
2465pub(crate) fn recovered_function_like_export_class_pair_has_body(
2466    node: Node<'_>,
2467    source: &str,
2468    identifier: &str,
2469    range: &Range,
2470) -> bool {
2471    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
2472        recovered.name == identifier
2473            && recovered.range.start_byte == range.start_byte
2474            && recovered.range.end_byte == range.end_byte
2475    })
2476}
2477
2478/// One file's embedded export-macro class recovery, resolved once and keyed by
2479/// the `ERROR` node that mints each set.
2480///
2481/// [`recover_embedded_function_like_export_classes`] collects and sorts an
2482/// `ERROR` node's whole subtree, and the declaration-strength question asks it
2483/// once per class-like unit in the file. On a translation unit the parser could
2484/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
2485/// node spans most of the file -- that is one full subtree pass per class,
2486/// quadratic in the file's size, and it was 78% of that file's inverse scan
2487/// (#1496).
2488///
2489/// Keyed by byte span rather than node identity, so one analyzer generation's
2490/// re-parses of the same content share the index. A nested `ERROR` that shares
2491/// its parent's span recovers the same classes: the only node the parent adds
2492/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
2493#[derive(Default)]
2494pub struct CppRecoveredExportClassIndex {
2495    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
2496}
2497
2498impl CppRecoveredExportClassIndex {
2499    pub fn build(root: Node<'_>, source: &str) -> Self {
2500        let mut by_error_node: HashMap<
2501            (usize, usize),
2502            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
2503        > = HashMap::default();
2504        let mut stack = vec![root];
2505        while let Some(node) = stack.pop() {
2506            if node.kind() == "ERROR" {
2507                let recovered = recover_embedded_function_like_export_classes(node, source);
2508                if !recovered.is_empty() {
2509                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
2510                }
2511            }
2512            let mut cursor = node.walk();
2513            stack.extend(node.named_children(&mut cursor));
2514        }
2515        Self { by_error_node }
2516    }
2517
2518    /// The bytes this index holds, for the analyzer cache's weight.
2519    pub fn approximate_size(&self) -> usize {
2520        self.by_error_node
2521            .values()
2522            .fold(0usize, |total, recovered| {
2523                recovered.iter().fold(
2524                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
2525                    |acc, class| {
2526                        acc.saturating_add(std::mem::size_of::<
2527                            RecoveredEmbeddedFunctionLikeExportClass,
2528                        >())
2529                        .saturating_add(class.name.len())
2530                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
2531                    },
2532                )
2533            })
2534    }
2535
2536    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
2537        self.by_error_node
2538            .get(&(node.start_byte(), node.end_byte()))
2539            .is_some_and(|recovered| {
2540                recovered.iter().any(|class| {
2541                    class.name == identifier
2542                        && class.range.start_byte == range.start_byte
2543                        && class.range.end_byte == range.end_byte
2544                })
2545            })
2546    }
2547}
2548
2549// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
2550// `recovered_class_body_at` pops while deciding whether a recovered class shape
2551// claims one declaration range. The count is deterministic for a given source,
2552// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
2553// pins it directly instead of timing the walk, the way #2358 pinned the
2554// `remove_code_unit` scan.
2555#[cfg(any(test, feature = "test-support"))]
2556thread_local! {
2557    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
2558        const { std::cell::Cell::new(0) };
2559}
2560
2561/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
2562/// the calling thread since the last
2563/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
2564#[cfg(any(test, feature = "test-support"))]
2565#[doc(hidden)]
2566pub fn recovered_class_body_node_visits_for_test() -> usize {
2567    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
2568}
2569
2570/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
2571#[cfg(any(test, feature = "test-support"))]
2572#[doc(hidden)]
2573pub fn reset_recovered_class_body_node_visits_for_test() {
2574    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
2575}
2576
2577#[cfg(any(test, feature = "test-support"))]
2578fn record_recovered_class_body_visit() {
2579    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
2580}
2581
2582#[cfg(not(any(test, feature = "test-support")))]
2583fn record_recovered_class_body_visit() {}
2584
2585/// Whether a recovered class shape named `identifier` owns `range`, and if so
2586/// whether that shape has a body.
2587///
2588/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
2589/// forward declaration, and `None` means no recovered shape claims the range,
2590/// so the caller reads the plain `class_specifier` family instead. This is the
2591/// single definition of "does a recovered class have a body": the resolver's
2592/// declaration-strength answer and the navigation occurrence role both read it,
2593/// so an export-macro class is a definition on both paths.
2594///
2595/// The walk follows only the nodes whose span covers `range.start_byte`, which
2596/// is every node that can answer. Each recovered shape reports a range that
2597/// starts at the node's own start byte (the function-like export pair, whose
2598/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
2599/// class, which the caller gates on an equal start) or at a token inside the
2600/// node (the embedded export class, keyed on its `class` token, and the
2601/// exported class wrapper, gated on containment here), and every one of them is
2602/// accepted only on an exact match with `range`. Descending everywhere instead
2603/// made one declaration-strength question cost a full pass over the file, so
2604/// asking it once per reference was quadratic in file size: 80% of the 385 s
2605/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
2606/// (#1496).
2607pub(crate) fn recovered_class_body_at(
2608    recovered_export_classes: &CppRecoveredExportClassIndex,
2609    root: Node<'_>,
2610    source: &str,
2611    identifier: &str,
2612    range: &Range,
2613) -> Option<bool> {
2614    let covers_range_start = |node: &Node<'_>| {
2615        node.start_byte() <= range.start_byte
2616            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
2617    };
2618    let mut stack = vec![root];
2619    let mut saw_forward = false;
2620    while let Some(node) = stack.pop() {
2621        record_recovered_class_body_visit();
2622        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
2623        // so an unequal start settles it before the recovery reads the node's
2624        // children at all.
2625        if (node.start_byte() == range.start_byte
2626            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
2627            || recovered_export_classes.claims(node, identifier, range)
2628            || (node.start_byte() == range.start_byte
2629                && recovered_fragmented_plain_class_has_body(node, source, identifier, range))
2630        {
2631            return Some(true);
2632        }
2633        // Macro-decorated exported classes are recovered from a malformed
2634        // function_definition/declaration wrapper. Their indexed class range starts at
2635        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
2636        // may also extend the indexed range beyond the wrapper through trailing class
2637        // fragments. Match the structured container that owns the range start by its
2638        // recovered name instead of requiring identical boundaries.
2639        if node.start_byte() <= range.start_byte
2640            && range.start_byte < node.end_byte()
2641            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
2642        {
2643            if has_body {
2644                return Some(true);
2645            }
2646            saw_forward = true;
2647            continue;
2648        }
2649        let mut cursor = node.walk();
2650        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
2651    }
2652    saw_forward.then_some(false)
2653}
2654
2655/// Whether `node` is the base type displaced into the declarator field of an
2656/// export-macro class that tree-sitter represented as a declaration or
2657/// function definition.
2658///
2659/// Declaration extraction already recovers this exact malformed envelope as a
2660/// class and records the declarator as its base. Reference extraction must use
2661/// the same structural fact instead of treating the node as a function name.
2662pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
2663    if !matches!(
2664        node.kind(),
2665        "qualified_identifier" | "scoped_type_identifier" | "template_type"
2666    ) {
2667        return false;
2668    }
2669    if let Some(function) = node.parent().filter(|parent| {
2670        parent.kind() == "function_definition"
2671            && parent
2672                .child_by_field_name("declarator")
2673                .is_some_and(|declarator| same_node(declarator, node))
2674    }) {
2675        return recover_exported_class_function_definition(function, source)
2676            .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
2677    }
2678    let Some(initializer) = node.parent().filter(|parent| {
2679        parent.kind() == "init_declarator"
2680            && parent
2681                .child_by_field_name("declarator")
2682                .is_some_and(|declarator| same_node(declarator, node))
2683    }) else {
2684        return false;
2685    };
2686    initializer
2687        .parent()
2688        .filter(|parent| parent.kind() == "declaration")
2689        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
2690        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
2691}
2692
2693/// Recover the class item from a region reparse that still carries the
2694/// sentinel's synthetic function envelope.  An unknown class attribute can
2695/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
2696/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
2697/// then nested below that function, so direct class-child lookup is not enough.
2698struct CppSentinelReparsedClass<'tree> {
2699    declaration_node: Node<'tree>,
2700    name: String,
2701    body: Node<'tree>,
2702    raw_supertypes: Option<Vec<String>>,
2703}
2704
2705fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
2706    let mut cursor = root.walk();
2707    root.named_children(&mut cursor)
2708        .find(|child| child.kind() != "comment")
2709        .filter(|child| child.kind() == "template_declaration")
2710}
2711
2712fn cpp_sentinel_reparsed_class<'tree>(
2713    root: Node<'tree>,
2714    template_node: Option<Node<'tree>>,
2715    source: &str,
2716    ancestry: &ParentIndex<'tree>,
2717) -> Option<CppSentinelReparsedClass<'tree>> {
2718    let container = template_node.unwrap_or(root);
2719    let mut cursor = container.walk();
2720    for child in container.named_children(&mut cursor) {
2721        if matches!(
2722            child.kind(),
2723            "class_specifier" | "struct_specifier" | "union_specifier"
2724        ) {
2725            let name = class_like_name(child, source, ancestry)?;
2726            let body = cpp_body_node(child)?;
2727            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
2728                .then(|| extract_cpp_supertypes(child, source));
2729            return Some(CppSentinelReparsedClass {
2730                declaration_node: child,
2731                name,
2732                body,
2733                raw_supertypes,
2734            });
2735        }
2736        if child.kind() == "declaration"
2737            && let Some(class_node) = first_class_like_child(child)
2738        {
2739            let name = class_like_name(class_node, source, ancestry)?;
2740            let body = cpp_body_node(class_node)?;
2741            let raw_supertypes =
2742                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
2743                    .then(|| extract_cpp_supertypes(class_node, source));
2744            return Some(CppSentinelReparsedClass {
2745                declaration_node: class_node,
2746                name,
2747                body,
2748                raw_supertypes,
2749            });
2750        }
2751        // Only when the nested class item carries its own body. A bodyless
2752        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
2753        // a function definition -- is the export-macro shape recovered by the
2754        // next arm, and must fall through to it rather than abort the search.
2755        if child.kind() == "function_definition"
2756            && let Some(class_node) = first_class_like_child(child)
2757            && let Some(body) = cpp_body_node(class_node)
2758            && let Some(name) = class_like_name(class_node, source, ancestry)
2759        {
2760            let raw_supertypes =
2761                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
2762                    .then(|| extract_cpp_supertypes(class_node, source));
2763            return Some(CppSentinelReparsedClass {
2764                declaration_node: class_node,
2765                name,
2766                body,
2767                raw_supertypes,
2768            });
2769        }
2770        if child.kind() == "function_definition"
2771            && let Some((_, name, raw_supertypes)) =
2772                recover_exported_class_function_definition(child, source)
2773        {
2774            let body = cpp_body_node(child)?;
2775            return Some(CppSentinelReparsedClass {
2776                declaration_node: child,
2777                name,
2778                body,
2779                raw_supertypes,
2780            });
2781        }
2782    }
2783    None
2784}
2785
2786fn recovered_postfix_export_macro_base(
2787    node: Node<'_>,
2788    type_node: Node<'_>,
2789    declarator: Node<'_>,
2790    source: &str,
2791) -> Option<String> {
2792    let mut cursor = node.walk();
2793    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
2794        child.kind() == "ERROR"
2795            && child.start_byte() >= type_node.end_byte()
2796            && child.end_byte() <= declarator.start_byte()
2797            && postfix_export_macro_inheritance(*child, source)
2798    });
2799    malformed_clauses.next()?;
2800    if malformed_clauses.next().is_some() {
2801        return None;
2802    }
2803    recovered_malformed_base_name(declarator, source)
2804}
2805
2806fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
2807    let mut macro_count = 0;
2808    let mut colon_count = 0;
2809    let mut access_count = 0;
2810    for index in 0..node.child_count() {
2811        let Some(child) = node.child(index) else {
2812            return false;
2813        };
2814        match child.kind() {
2815            "identifier" | "type_identifier" if child.is_named() => {
2816                let candidate = normalize_cpp_whitespace(node_text(child, source));
2817                if !cpp_export_macro_token(&candidate) {
2818                    return false;
2819                }
2820                macro_count += 1;
2821            }
2822            ":" if !child.is_named() => colon_count += 1,
2823            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
2824            _ => return false,
2825        }
2826    }
2827    macro_count == 1 && colon_count == 1 && access_count == 1
2828}
2829
2830fn recovered_single_base_after_declarator(
2831    node: Node<'_>,
2832    declarator: Node<'_>,
2833    source: &str,
2834) -> Option<String> {
2835    let body_start = node
2836        .child_by_field_name("body")
2837        .map(|body| body.start_byte())
2838        .unwrap_or(node.end_byte());
2839    let mut cursor = node.walk();
2840    let mut bases = node
2841        .named_children(&mut cursor)
2842        .filter(|child| {
2843            child.kind() == "ERROR"
2844                && child.start_byte() >= declarator.end_byte()
2845                && child.end_byte() <= body_start
2846        })
2847        .filter_map(|error| displaced_exported_class_name(error, source));
2848    let base = bases.next()?;
2849    bases.next().is_none().then_some(base)
2850}
2851
2852fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
2853    (0..node.child_count()).any(|index| {
2854        node.child(index)
2855            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
2856    })
2857}
2858
2859pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
2860    recover_exported_class_function_definition(node, source).is_some()
2861}
2862
2863fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
2864    matches!(
2865        kind,
2866        "ERROR"
2867            | "preproc_if"
2868            | "preproc_ifdef"
2869            | "preproc_ifndef"
2870            | "preproc_else"
2871            | "preproc_elif"
2872    ) || (kind == "labeled_statement" && in_class_scope)
2873}
2874
2875pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
2876    if node.kind() != "declaration" {
2877        return false;
2878    }
2879    let mut ancestor = node.parent();
2880    while let Some(container) = ancestor {
2881        match container.kind() {
2882            "compound_statement" => {
2883                return container.parent().is_some_and(|class_container| {
2884                    is_recovered_exported_class_container(class_container, source)
2885                });
2886            }
2887            // These containers preserve ScopeInfo in visit_node. declaration_list is
2888            // the body container selected for a linkage specification.
2889            "template_declaration" | "linkage_specification" | "declaration_list" => {}
2890            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
2891            _ => return false,
2892        }
2893        ancestor = container.parent();
2894    }
2895    false
2896}
2897
2898pub fn recovered_exported_class_has_body(
2899    node: Node<'_>,
2900    source: &str,
2901    expected_name: &str,
2902) -> Option<bool> {
2903    match node.kind() {
2904        "function_definition" => {
2905            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
2906            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
2907        }
2908        "declaration" | "field_declaration" => {
2909            let recovered = recover_exported_class_declaration(node, source)?;
2910            (recovered.name == expected_name).then(|| recovered.body.is_some())
2911        }
2912        _ => None,
2913    }
2914}
2915
2916fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
2917    let body_start = node
2918        .child_by_field_name("body")
2919        .map(|body| body.start_byte())
2920        .unwrap_or(node.end_byte());
2921    let mut stack = Vec::new();
2922    for index in (0..node.named_child_count()).rev() {
2923        let Some(child) = node.named_child(index) else {
2924            continue;
2925        };
2926        if child.start_byte() >= body_start {
2927            continue;
2928        }
2929        stack.push(child);
2930    }
2931
2932    let mut best = None;
2933    while let Some(current) = stack.pop() {
2934        if matches!(current.kind(), "identifier" | "type_identifier") {
2935            let name = normalize_cpp_whitespace(node_text(current, source));
2936            if !name.is_empty()
2937                && !cpp_export_macro_token(&name)
2938                && !matches!(name.as_str(), "class" | "struct" | "union")
2939            {
2940                best = Some(name);
2941            }
2942            continue;
2943        }
2944
2945        for index in (0..current.named_child_count()).rev() {
2946            if let Some(child) = current.named_child(index)
2947                && child.start_byte() < body_start
2948            {
2949                stack.push(child);
2950            }
2951        }
2952    }
2953    best
2954}
2955
2956fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
2957    if node.kind() == "declaration"
2958        && node
2959            .child_by_field_name("type")
2960            .or_else(|| first_class_like_child(node))
2961            .is_some_and(|type_node| {
2962                matches!(
2963                    type_node.kind(),
2964                    "class_specifier" | "struct_specifier" | "union_specifier"
2965                )
2966            })
2967        && let Some(name) = node
2968            .child_by_field_name("declarator")
2969            .and_then(|declarator| declarator_name_from_node(declarator, source))
2970        && !cpp_export_macro_token(&name)
2971    {
2972        return Some(name);
2973    }
2974
2975    if node.kind() == "function_definition"
2976        && node.child_by_field_name("type").is_some_and(|type_node| {
2977            matches!(
2978                type_node.kind(),
2979                "class_specifier" | "struct_specifier" | "union_specifier"
2980            )
2981        })
2982        && let Some(name) = node
2983            .child_by_field_name("declarator")
2984            .and_then(|declarator| direct_identifier_name(declarator, source))
2985        && !cpp_export_macro_token(&name)
2986    {
2987        return Some(name);
2988    }
2989
2990    let class_node = if matches!(
2991        node.kind(),
2992        "class_specifier" | "struct_specifier" | "union_specifier"
2993    ) {
2994        node
2995    } else {
2996        first_class_like_child(node)?
2997    };
2998    class_like_name_from_children(class_node, source)
2999}
3000
3001fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3002    if !matches!(
3003        node.kind(),
3004        "identifier" | "field_identifier" | "type_identifier"
3005    ) {
3006        return None;
3007    }
3008    let name = normalize_cpp_whitespace(node_text(node, source));
3009    (!name.is_empty()).then_some(name)
3010}
3011
3012fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3013    match node.kind() {
3014        "identifier" | "field_identifier" | "type_identifier" => {
3015            let name = normalize_cpp_whitespace(node_text(node, source));
3016            (!name.is_empty()).then_some(name)
3017        }
3018        _ => {
3019            let mut cursor = node.walk();
3020            node.named_children(&mut cursor)
3021                .find_map(|child| declarator_name_from_node(child, source))
3022        }
3023    }
3024}
3025
3026fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3027    let mut cursor = node.walk();
3028    node.named_children(&mut cursor).find(|child| {
3029        matches!(
3030            child.kind(),
3031            "class_specifier" | "struct_specifier" | "union_specifier"
3032        )
3033    })
3034}
3035
3036/// Push a container's children as a `Siblings` cursor rather than snapshotting
3037/// them all with one shared scope: children are visited one at a time so a
3038/// `using namespace X;` sibling can affect the scope threaded to the siblings
3039/// that textually follow it (issue #1093).
3040fn push_cpp_container_work<'tree>(
3041    node: Node<'tree>,
3042    scope: ScopeInfo,
3043    stack: &mut Vec<CppWork<'tree>>,
3044) {
3045    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3046}
3047
3048/// Materialize one selected named-child range with a tree-sitter cursor. The
3049/// cursor advances linearly across the parent's concrete children; repeatedly
3050/// asking for `named_child(index)` is quadratic on very wide generated nodes.
3051fn push_cpp_sibling_range<'tree>(
3052    parent: Node<'tree>,
3053    start_index: usize,
3054    end_index: usize,
3055    scope: ScopeInfo,
3056    stack: &mut Vec<CppWork<'tree>>,
3057) {
3058    let mut cursor = parent.walk();
3059    let children = parent
3060        .named_children(&mut cursor)
3061        .skip(start_index)
3062        .take(end_index.saturating_sub(start_index))
3063        .collect::<Vec<_>>()
3064        .into_iter();
3065    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3066}
3067
3068/// Advance a `Siblings` cursor by one child: dispatch the current child under
3069/// the scope accumulated from its *earlier* siblings, then push a
3070/// continuation for the remaining siblings carrying the scope updated for
3071/// *this* child (only `using namespace X;` directives change it). Pushing the
3072/// continuation before the current child's own node work means the current
3073/// child's subtree fully drains (LIFO) before the next sibling is visited,
3074/// preserving left-to-right order.
3075fn advance_cpp_siblings<'tree>(
3076    mut siblings: CppSiblingsWork<'tree>,
3077    source: &str,
3078    stack: &mut Vec<CppWork<'tree>>,
3079) {
3080    let Some(child) = siblings.children.next() else {
3081        return;
3082    };
3083    let current_scope = siblings.scope.clone();
3084    if let Some(namespace) = cpp_using_namespace_target(child, source) {
3085        siblings.scope.visible_using_namespaces.push(namespace);
3086    }
3087    if !siblings.children.as_slice().is_empty() {
3088        stack.push(CppWork::Siblings(siblings));
3089    }
3090    stack.push(CppWork::Node(CppNodeWork {
3091        node: child,
3092        scope: current_scope,
3093    }));
3094}
3095
3096/// The namespace target of a `using namespace X;` directive, or `None` for
3097/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
3098/// kind. Distinguished structurally by the presence of the grammar's literal
3099/// `namespace` keyword token among the node's children -- not by inspecting
3100/// source text -- so it never misreads a member-importing using-declaration
3101/// as a namespace directive.
3102fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3103    if node.kind() != "using_declaration" {
3104        return None;
3105    }
3106    let mut cursor = node.walk();
3107    let is_namespace_directive = node
3108        .children(&mut cursor)
3109        .any(|child| child.kind() == "namespace");
3110    if !is_namespace_directive {
3111        return None;
3112    }
3113    let target = node.named_child(0)?;
3114    // A leading `::` is the explicit-global marker, not part of the namespace
3115    // path (`using namespace ::std::chrono;`). Drop that AST token before
3116    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
3117    // keeps: storing the marker verbatim desynced the legacy package string from
3118    // the FqName bridge, which splits on `::` and drops the empty leading
3119    // component, tripping the package/short boundary assert when a bare-owner
3120    // out-of-line definition borrowed the directive's namespace (#1093 path).
3121    let start = target
3122        .child(0)
3123        .filter(|child| !child.is_named() && child.kind() == "::")
3124        .map_or(target.start_byte(), |marker| marker.end_byte());
3125    let text = normalize_cpp_whitespace(
3126        source
3127            .get(start..target.end_byte())
3128            .expect("using-directive target covers one source range"),
3129    );
3130    (!text.is_empty()).then_some(text)
3131}
3132
3133/// Every `using namespace X;` directive target in a file, in source order, for
3134/// resolution-time consumers that need the file's using-directives without the
3135/// per-position scope threading extraction does. Parses `source` fresh and
3136/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
3137/// keys on the grammar's `namespace` keyword token, not source text), so it
3138/// never misreads a member-importing `using X::Y;` as a namespace directive.
3139///
3140/// This is a whole-file over-approximation of what is in scope at any one point
3141/// (a directive nested inside a `namespace {}` block or a function body is still
3142/// reported), which is exactly what the #1134 identity reconciler wants: extra
3143/// candidate namespaces that no visible class confirms are harmless, and two
3144/// that both confirm are treated as a genuine ambiguity by the reconciler.
3145pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3146    let mut parser = Parser::new();
3147    if parser
3148        .set_language(&tree_sitter_cpp::LANGUAGE.into())
3149        .is_err()
3150    {
3151        return Vec::new();
3152    }
3153    let Some(tree) = parser.parse(source, None) else {
3154        return Vec::new();
3155    };
3156    let mut namespaces = Vec::new();
3157    let mut seen = std::collections::HashSet::new();
3158    let mut stack = vec![tree.root_node()];
3159    while let Some(node) = stack.pop() {
3160        if let Some(namespace) = cpp_using_namespace_target(node, source)
3161            && seen.insert(namespace.clone())
3162        {
3163            namespaces.push(namespace);
3164        }
3165        let mut cursor = node.walk();
3166        stack.extend(node.named_children(&mut cursor));
3167    }
3168    namespaces
3169}
3170
3171pub struct CppVisitor<'a> {
3172    pub file: &'a ProjectFile,
3173    pub source: &'a str,
3174    pub parsed: &'a mut ParsedFile,
3175    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
3176    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
3177    ///
3178    /// C has no nested tag scope: a struct/union/enum tag declared inside
3179    /// another aggregate's member list has the scope of the outer declaration
3180    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
3181    /// therefore declares a file-scope `inner` that a later file-scope
3182    /// `struct inner *p;` legitimately references, where C++ would make the
3183    /// same shape a nested class `outer::inner`. Headers carry no compilation
3184    /// language of their own and keep the conservative C++ interpretation.
3185    pub c_tag_semantics: bool,
3186    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3187    /// Byte regions whose contents were re-owned by a fragmented export-class
3188    /// recovery (#938): the scattered members between the fragmented
3189    /// declaration and its displaced closing brace are indexed as members of
3190    /// the recovered class by the region reparse, so the ordinary sibling walk
3191    /// must not ALSO index them as top-level declarations (that double-indexing
3192    /// made a scattered nested class ambiguous between `Inner` and
3193    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
3194    /// linear scan at visit time is fine.
3195    pub consumed_fragment_regions: Vec<(usize, usize)>,
3196    /// The namespace forward declarations already folded out of each tree this
3197    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
3198    /// Empty until the first question, which the overwhelming majority of files
3199    /// never ask.
3200    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3201    /// Which owners already have field declarations in the parse product, as
3202    /// [`CppVisitor::has_enum_enumerator_units`] needs to know. `None` until
3203    /// the first enum asks, which most files never do (#2786).
3204    pub field_owners: Option<CppFieldOwnerIndex>,
3205    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
3206    /// happen to the declaration set, innermost last. Empty outside a recovery
3207    /// reparse, which is almost always (#2787).
3208    pub recovery_captures: Vec<CppRecoveryCapture>,
3209}
3210
3211impl<'a> CppVisitor<'a> {
3212    /// Records `code_unit` with the answers the walk carries forward, then adds
3213    /// it to the parse product.
3214    ///
3215    /// Every declaration this walk publishes goes through this family, so the
3216    /// carried-forward answers see each one exactly once: the field ownership
3217    /// index behind [`Self::has_enum_enumerator_units`] (#2786) and the minted
3218    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
3219    fn add_declaration(
3220        &mut self,
3221        code_unit: CodeUnit,
3222        node: Node<'_>,
3223        parent: Option<CodeUnit>,
3224        top_level: Option<CodeUnit>,
3225    ) {
3226        self.note_declaration(&code_unit);
3227        let source = self.source;
3228        self.parsed
3229            .add_code_unit(code_unit, node, source, parent, top_level);
3230    }
3231
3232    /// Range-based form of [`Self::add_declaration`].
3233    fn add_declaration_with_range(
3234        &mut self,
3235        code_unit: CodeUnit,
3236        range: Range,
3237        parent: Option<CodeUnit>,
3238        top_level: Option<CodeUnit>,
3239    ) {
3240        self.note_declaration(&code_unit);
3241        self.parsed
3242            .add_code_unit_with_range(code_unit, range, parent, top_level);
3243    }
3244
3245    /// Deferred-replacement form of [`Self::add_declaration`].
3246    fn replace_declaration_deferred(
3247        &mut self,
3248        code_unit: CodeUnit,
3249        node: Node<'_>,
3250        parent: Option<CodeUnit>,
3251        top_level: Option<CodeUnit>,
3252    ) {
3253        self.note_replaced_declaration(&code_unit);
3254        let source = self.source;
3255        self.parsed
3256            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
3257    }
3258
3259    /// Range-based form of [`Self::replace_declaration_deferred`].
3260    fn replace_declaration_with_range_deferred(
3261        &mut self,
3262        code_unit: CodeUnit,
3263        range: Range,
3264        parent: Option<CodeUnit>,
3265        top_level: Option<CodeUnit>,
3266    ) {
3267        self.note_replaced_declaration(&code_unit);
3268        self.parsed
3269            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
3270    }
3271
3272    /// Notes one declaration about to enter the parse product.
3273    ///
3274    /// A declaration the product already holds is not a creation, so an open
3275    /// recovery capture ignores it -- which is the membership test the set
3276    /// difference it replaces performed. A creation inside a nested recovery
3277    /// belongs to the recoveries around it too, so every open capture takes it.
3278    fn note_declaration(&mut self, code_unit: &CodeUnit) {
3279        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
3280            for capture in &mut self.recovery_captures {
3281                if capture.removed_pre_existing.contains(code_unit) {
3282                    continue;
3283                }
3284                if capture.created_units.insert(code_unit.clone()) {
3285                    capture.created.push(code_unit.clone());
3286                }
3287            }
3288        }
3289        if let Some(field_owners) = self.field_owners.as_mut() {
3290            field_owners.record(code_unit, self.file);
3291        }
3292    }
3293
3294    /// Notes one declaration about to replace an existing one.
3295    ///
3296    /// A deferred replacement of a declaration that already owns children
3297    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
3298    /// a removal is the one thing the field index cannot absorb by addition.
3299    /// Drop it; the next question rebuilds it from the declarations that
3300    /// survive. A replacement of a unit with no children, and a "replacement"
3301    /// of a unit that is not there at all, remove nothing.
3302    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
3303        let removes_children = self.parsed.contains_declaration(code_unit)
3304            && self
3305                .parsed
3306                .children
3307                .get(code_unit)
3308                .is_some_and(|children| !children.is_empty());
3309        if removes_children {
3310            if !self.recovery_captures.is_empty() {
3311                let removed = self.declarations_a_replacement_removes(code_unit);
3312                for capture in &mut self.recovery_captures {
3313                    for unit in &removed {
3314                        // A declaration this capture watched being created is
3315                        // its own; one it did not is a declaration that was
3316                        // already there when the capture opened, so creating it
3317                        // again is a restoration and not a mint.
3318                        if !capture.created_units.contains(unit) {
3319                            capture.removed_pre_existing.insert(unit.clone());
3320                        }
3321                    }
3322                }
3323            }
3324            self.field_owners = None;
3325        }
3326        self.note_declaration(code_unit);
3327    }
3328
3329    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
3330    /// when `code_unit` is replaced: its children, transitively.
3331    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
3332        let mut removed = Vec::new();
3333        let mut seen = HashSet::default();
3334        let mut pending: Vec<CodeUnit> = self
3335            .parsed
3336            .children
3337            .get(code_unit)
3338            .cloned()
3339            .unwrap_or_default();
3340        while let Some(unit) = pending.pop() {
3341            if !seen.insert(unit.clone()) {
3342                continue;
3343            }
3344            if let Some(children) = self.parsed.children.get(&unit) {
3345                pending.extend(children.iter().cloned());
3346            }
3347            removed.push(unit);
3348        }
3349        removed
3350    }
3351
3352    fn visit_function_like_export_class_pair<'tree>(
3353        &mut self,
3354        node: Node<'tree>,
3355        scope: &ScopeInfo,
3356        stack: &mut Vec<CppWork<'tree>>,
3357        ancestry: &ParentIndex<'tree>,
3358    ) -> bool {
3359        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
3360            return false;
3361        };
3362        let member_outcome = self
3363            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
3364        // A malformed class body can escape into several following siblings
3365        // before the next export-macro class head appears. Inspect siblings in
3366        // order and stop at the first envelope that contains such a head. One
3367        // envelope can contain several following classes, all recovered in a
3368        // single bounded traversal.
3369        let mut displaced = node.next_named_sibling();
3370        while let Some(candidate) = displaced {
3371            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
3372                break;
3373            }
3374            displaced = candidate.next_named_sibling();
3375        }
3376        let class_unit = self.visit_named_class_like_shape(
3377            node,
3378            recovered.name,
3379            // The adjacent initializer_list proves the class body envelope,
3380            // but its children are expression-shaped rather than declaration-
3381            // preserving. Index the class identity here; callable definitions
3382            // remain available from their ordinary out-of-line declarations.
3383            None,
3384            true,
3385            Some(recovered.range),
3386            recovered.raw_supertypes,
3387            scope,
3388            stack,
3389            ancestry,
3390        );
3391        self.parsed
3392            .record_materialization(MaterializationRecord::RecoveredDeclaration {
3393                recovery: recovered.range,
3394                unit: class_unit.clone(),
3395            });
3396        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
3397            && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
3398                tree.root_node(),
3399                class_unit.identifier(),
3400                self.source,
3401            )
3402        {
3403            self.visit_recovered_fragment_constructor(
3404                range,
3405                body,
3406                node,
3407                &class_unit,
3408                scope,
3409                ancestry,
3410            );
3411        }
3412        if let Some(outcome) = member_outcome {
3413            self.visit_fragmented_export_class_members(outcome, class_unit, scope);
3414        }
3415        self.consumed_fragment_regions
3416            .push((node.start_byte(), recovered.range.end_byte));
3417        true
3418    }
3419
3420    fn visit_embedded_function_like_export_classes<'tree>(
3421        &mut self,
3422        node: Node<'tree>,
3423        scope: &ScopeInfo,
3424        stack: &mut Vec<CppWork<'tree>>,
3425        ancestry: &ParentIndex<'tree>,
3426    ) -> bool {
3427        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
3428        let found = !recovered_classes.is_empty();
3429        for recovered in recovered_classes {
3430            let member_outcome = self.reparse_fragmented_export_class_members(
3431                &recovered.fragmented_body,
3432                &recovered.name,
3433            );
3434            let class_unit = self.visit_named_class_like_shape(
3435                node,
3436                recovered.name,
3437                None,
3438                true,
3439                Some(recovered.range),
3440                Some(recovered.raw_supertypes),
3441                scope,
3442                stack,
3443                ancestry,
3444            );
3445            self.parsed
3446                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3447                    recovery: recovered.range,
3448                    unit: class_unit.clone(),
3449                });
3450            if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
3451                && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
3452                    tree.root_node(),
3453                    class_unit.identifier(),
3454                    self.source,
3455                )
3456            {
3457                self.visit_recovered_fragment_constructor(
3458                    range,
3459                    body,
3460                    node,
3461                    &class_unit,
3462                    scope,
3463                    ancestry,
3464                );
3465            }
3466            if let Some(outcome) = member_outcome {
3467                self.visit_fragmented_export_class_members(outcome, class_unit, scope);
3468            }
3469        }
3470        found
3471    }
3472
3473    /// Walk `node`'s container, answering every ancestor question from
3474    /// `ancestry`.
3475    ///
3476    /// `ancestry` must index the tree `node` belongs to. The caller owns it
3477    /// because one tree can be walked more than once -- a header's C and C++
3478    /// readings are the same tree under different tag semantics -- and the
3479    /// parent relation is a property of the tree, not of the reading.
3480    #[allow(clippy::too_many_arguments)]
3481    pub fn visit_container<'tree>(
3482        &mut self,
3483        node: Node<'tree>,
3484        ancestry: &ParentIndex<'tree>,
3485        package_name: &str,
3486        module: Option<CodeUnit>,
3487        class_unit: Option<CodeUnit>,
3488        template_signature: Option<String>,
3489        visible_using_namespaces: Vec<String>,
3490    ) {
3491        let scope = ScopeInfo {
3492            package_name: package_name.to_string(),
3493            module,
3494            class_unit,
3495            template_signature,
3496            template_metadata: None,
3497            declarations_are_fields: false,
3498            recovered_specialization_member_scope: false,
3499            visible_using_namespaces,
3500        };
3501        self.run_container_work(node, scope, ancestry);
3502    }
3503
3504    /// Whether a work node lies entirely inside a byte region consumed by a
3505    /// fragmented export-class recovery (#938); such nodes were already indexed
3506    /// as members of the recovered class by the region reparse.
3507    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
3508        self.consumed_fragment_regions
3509            .iter()
3510            .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
3511    }
3512
3513    /// Drive the container work loop from an explicit seed scope to completion. The
3514    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
3515    /// stays alive for the whole traversal.
3516    ///
3517    /// Every ancestor question this walk asks is answered from `ancestry`, which
3518    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
3519    /// node's position in the tree, which made a generated header with thousands
3520    /// of top-level declarations quadratic (#2361). The index is the caller's
3521    /// because it outlives any one walk: the file's tree is walked twice when a
3522    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
3523    /// builds its own index for its own tree.
3524    fn run_container_work<'tree>(
3525        &mut self,
3526        node: Node<'tree>,
3527        scope: ScopeInfo,
3528        ancestry: &ParentIndex<'tree>,
3529    ) {
3530        self.drain_cpp_work(
3531            vec![CppWork::Container(CppContainer { node, scope })],
3532            ancestry,
3533        );
3534    }
3535
3536    /// The work loop itself, from whatever seed the caller built.
3537    ///
3538    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
3539    /// that must walk only part of a reparsed tree seeds it with the sibling
3540    /// range it may walk instead, which keeps the `using namespace X;` scope
3541    /// accumulation `advance_cpp_siblings` performs.
3542    fn drain_cpp_work<'tree>(
3543        &mut self,
3544        mut stack: Vec<CppWork<'tree>>,
3545        ancestry: &ParentIndex<'tree>,
3546    ) {
3547        while let Some(work) = stack.pop() {
3548            match work {
3549                CppWork::Container(container) => {
3550                    push_cpp_container_work(container.node, container.scope, &mut stack);
3551                }
3552                CppWork::Siblings(siblings) => {
3553                    advance_cpp_siblings(siblings, self.source, &mut stack);
3554                }
3555                CppWork::Node(work) => {
3556                    if self.node_is_inside_consumed_fragment(work.node) {
3557                        continue;
3558                    }
3559                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
3560                }
3561            }
3562        }
3563    }
3564
3565    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
3566    /// it only when the entire region is member-shaped. This validation must happen
3567    /// before registering the recovered class because a rejected speculative range
3568    /// must not leak into the ordinary recovery path.
3569    fn reparse_fragmented_export_class_members(
3570        &self,
3571        fragmented: &FragmentedExportBody,
3572        class_name: &str,
3573    ) -> Option<FragmentedExportMembers> {
3574        if fragmented.reparse_start >= fragmented.reparse_end {
3575            return None;
3576        }
3577        let tree = cpp_reparse_fragmented_class_body(
3578            self.source,
3579            fragmented.reparse_start,
3580            fragmented.reparse_end,
3581        )?;
3582        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
3583            return Some(FragmentedExportMembers::Complete(tree));
3584        }
3585        let has_conditional_constructor = {
3586            let root = tree.root_node();
3587            let mut cursor = root.walk();
3588            root.named_children(&mut cursor).any(|child| {
3589                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
3590            })
3591        };
3592        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
3593    }
3594
3595    /// Index an already validated fragmented body as members of `class_unit`. The
3596    /// region reparse keeps each member's exact original byte and line positions.
3597    fn visit_fragmented_export_class_members(
3598        &mut self,
3599        outcome: FragmentedExportMembers,
3600        class_unit: CodeUnit,
3601        scope: &ScopeInfo,
3602    ) -> bool {
3603        let (tree, complete) = match outcome {
3604            FragmentedExportMembers::Complete(tree) => (tree, true),
3605            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
3606        };
3607        let root = tree.root_node();
3608        let class_name = class_unit.identifier().to_string();
3609        let member_scope = ScopeInfo {
3610            // A recovered export-macro class may borrow its namespace from an
3611            // earlier forward declaration even when the malformed node itself
3612            // sits at file scope. Use the recovered class identity as the
3613            // authoritative package for reparsed members as well.
3614            package_name: class_unit.package_name().to_string(),
3615            module: scope.module.clone(),
3616            class_unit: Some(class_unit),
3617            template_signature: scope.template_signature.clone(),
3618            template_metadata: None,
3619            declarations_are_fields: true,
3620            recovered_specialization_member_scope: false,
3621            visible_using_namespaces: scope.visible_using_namespaces.clone(),
3622        };
3623        if !complete {
3624            // A conditional beginning immediately after an access label can
3625            // fragment one constructor declaration while leaving the rest of
3626            // the class body as unsafe statement soup. Recover only that
3627            // structurally proven constructor and leave the outer-tree
3628            // siblings unconsumed for their ordinary walk.
3629            let mut cursor = root.walk();
3630            let constructors = root
3631                .named_children(&mut cursor)
3632                .filter_map(|child| {
3633                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
3634                })
3635                .collect::<Vec<_>>();
3636            // The reparsed region is its own tree, so this drain walks it with
3637            // its own parent index.
3638            let reparsed_ancestry = ParentIndex::new(root);
3639            for constructor in constructors {
3640                let mut stack = Vec::new();
3641                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
3642                while let Some(work) = stack.pop() {
3643                    match work {
3644                        CppWork::Container(container) => {
3645                            push_cpp_container_work(container.node, container.scope, &mut stack);
3646                        }
3647                        CppWork::Siblings(siblings) => {
3648                            advance_cpp_siblings(siblings, self.source, &mut stack);
3649                        }
3650                        CppWork::Node(work) => {
3651                            self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
3652                        }
3653                    }
3654                }
3655            }
3656            return false;
3657        }
3658        // The reparsed region is its own tree, so this walk indexes it itself.
3659        self.run_container_work(root, member_scope, &ParentIndex::new(root));
3660        true
3661    }
3662
3663    fn visit_recovered_fragment_constructor<'tree>(
3664        &mut self,
3665        range: std::ops::Range<usize>,
3666        constructor_body: Node<'tree>,
3667        class_declaration: Node<'tree>,
3668        class_unit: &CodeUnit,
3669        scope: &ScopeInfo,
3670        ancestry: &ParentIndex<'tree>,
3671    ) {
3672        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
3673            return;
3674        };
3675        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
3676            tree.root_node(),
3677            range.start,
3678            class_unit.identifier(),
3679            self.source,
3680        ) else {
3681            return;
3682        };
3683        let member_scope = ScopeInfo {
3684            package_name: class_unit.package_name().to_string(),
3685            module: scope.module.clone(),
3686            class_unit: Some(class_unit.clone()),
3687            template_signature: scope.template_signature.clone(),
3688            template_metadata: None,
3689            declarations_are_fields: true,
3690            recovered_specialization_member_scope: false,
3691            visible_using_namespaces: scope.visible_using_namespaces.clone(),
3692        };
3693        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
3694        else {
3695            return;
3696        };
3697        debug_assert_eq!(function.name, class_unit.identifier());
3698        let code_unit = function.code_unit(self.file.clone());
3699        self.add_declaration_with_range(
3700            code_unit.clone(),
3701            Range {
3702                start_byte: function_declarator.start_byte(),
3703                end_byte: constructor_body.end_byte(),
3704                start_line: function_declarator.start_position().row + 1,
3705                end_line: constructor_body.end_position().row + 1,
3706            },
3707            None,
3708            None,
3709        );
3710        self.parsed.add_signature_with_metadata(
3711            code_unit.clone(),
3712            cpp_signature_metadata(
3713                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
3714                function_declarator,
3715                self.source,
3716                ancestry,
3717            )
3718            .with_declaration_only(false)
3719            .with_callable_linkage(cpp_callable_linkage(
3720                class_declaration,
3721                self.source,
3722                ancestry,
3723            )),
3724        );
3725        self.parsed.add_child(class_unit.clone(), code_unit);
3726    }
3727
3728    fn visit_recovered_fragment_prefix_members<'tree>(
3729        &mut self,
3730        root: Node<'tree>,
3731        constructor_start: usize,
3732        class_unit: &CodeUnit,
3733        scope: &ScopeInfo,
3734        ancestry: &ParentIndex<'tree>,
3735    ) {
3736        let member_scope = ScopeInfo {
3737            package_name: class_unit.package_name().to_string(),
3738            module: scope.module.clone(),
3739            class_unit: Some(class_unit.clone()),
3740            template_signature: scope.template_signature.clone(),
3741            template_metadata: None,
3742            declarations_are_fields: true,
3743            recovered_specialization_member_scope: false,
3744            visible_using_namespaces: scope.visible_using_namespaces.clone(),
3745        };
3746        let mut stack = vec![root];
3747        while let Some(current) = stack.pop() {
3748            if current.kind() == "comment" || current.start_byte() >= constructor_start {
3749                continue;
3750            }
3751            if current.end_byte() <= constructor_start
3752                && current.kind() != "translation_unit"
3753                && current.kind() != "labeled_statement"
3754                && current.kind() != "ERROR"
3755            {
3756                let mut work_stack = Vec::new();
3757                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
3758                while let Some(work) = work_stack.pop() {
3759                    match work {
3760                        CppWork::Container(container) => {
3761                            push_cpp_container_work(
3762                                container.node,
3763                                container.scope,
3764                                &mut work_stack,
3765                            );
3766                        }
3767                        CppWork::Siblings(siblings) => {
3768                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
3769                        }
3770                        CppWork::Node(work) => {
3771                            self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
3772                        }
3773                    }
3774                }
3775                continue;
3776            }
3777            if matches!(
3778                current.kind(),
3779                "translation_unit" | "labeled_statement" | "ERROR"
3780            ) {
3781                let mut cursor = current.walk();
3782                stack.extend(current.named_children(&mut cursor));
3783            }
3784        }
3785    }
3786
3787    fn visit_node<'tree>(
3788        &mut self,
3789        node: Node<'tree>,
3790        scope: &ScopeInfo,
3791        stack: &mut Vec<CppWork<'tree>>,
3792        ancestry: &ParentIndex<'tree>,
3793    ) {
3794        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
3795            self.visit_node(node, &recovered_scope, stack, ancestry);
3796            return;
3797        }
3798        // Fragmented-class recovery below may consume a malformed function
3799        // envelope before the ordinary kind dispatch runs. Recover any
3800        // export-macro class embedded in that envelope first; the strict class
3801        // head/base/body predicate is independent of which later recovery owns
3802        // the surrounding parser fragment.
3803        if node.kind() == "function_definition" && node.has_error() {
3804            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
3805        }
3806        if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
3807        {
3808            let displaced_namespace_items =
3809                displaced_fragment_namespace_geometry(node, self.source)
3810                    .map(|boundary| boundary.namespace_items)
3811                    .unwrap_or_default();
3812            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
3813            let mut class_stack = Vec::new();
3814            // When the full body cannot be safely reparsed, the original class
3815            // node still proves ownership for its parser-visible prefix.
3816            let parser_visible_body =
3817                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
3818                    .then(|| cpp_body_node(class_node))
3819                    .flatten();
3820            let class_unit = self.visit_named_class_like_shape(
3821                class_node,
3822                name,
3823                parser_visible_body,
3824                true,
3825                Some(fragmented.class_range),
3826                Some(extract_cpp_supertypes(class_node, self.source)),
3827                scope,
3828                &mut class_stack,
3829                ancestry,
3830            );
3831            let member_scope = ScopeInfo {
3832                package_name: class_unit.package_name().to_string(),
3833                module: scope.module.clone(),
3834                class_unit: Some(class_unit.clone()),
3835                template_signature: scope.template_signature.clone(),
3836                template_metadata: None,
3837                declarations_are_fields: true,
3838                recovered_specialization_member_scope: false,
3839                visible_using_namespaces: scope.visible_using_namespaces.clone(),
3840            };
3841            let complete = outcome.is_some_and(|outcome| {
3842                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
3843            });
3844            if complete {
3845                self.consumed_fragment_regions
3846                    .push((node.start_byte(), fragmented.class_range.end_byte));
3847            } else {
3848                // A macro-constrained member can make the full body reparse
3849                // unsafe while tree-sitter still exposes later class members
3850                // as bounded siblings up to the displaced `}`/`;`. Keep the
3851                // structurally proven class/base declaration and re-own those
3852                // sibling nodes under it. They retain their original parser
3853                // nodes and exact ranges; the close boundary comes solely from
3854                // `fragmented_plain_class_body`.
3855                // Template wrappers put the escaped members beside the
3856                // template rather than beside its malformed declaration.
3857                for candidate in cpp_following_named_siblings(node, self.source) {
3858                    if candidate.start_byte() >= fragmented.reparse_end {
3859                        break;
3860                    }
3861                    if cpp_fragment_sibling_is_class_member(
3862                        candidate,
3863                        fragmented.reparse_end,
3864                        self.source,
3865                    ) {
3866                        self.recovered_class_sibling_scopes
3867                            .insert(candidate.id(), member_scope.clone());
3868                    }
3869                }
3870            }
3871            for item in displaced_namespace_items {
3872                self.recovered_class_sibling_scopes
3873                    .insert(item.id(), scope.clone());
3874            }
3875            stack.extend(class_stack);
3876            return;
3877        }
3878        match node.kind() {
3879            "template_declaration" => {
3880                if let Some(recovered) =
3881                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
3882                {
3883                    let mut template_scope = scope.clone();
3884                    template_scope.template_signature =
3885                        cpp_template_signature(node, recovered.declaration_node, self.source);
3886                    template_scope.template_metadata =
3887                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
3888                    let raw_supertypes =
3889                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
3890                    let mut class_stack = Vec::new();
3891                    let class_unit = self.visit_named_class_like_shape(
3892                        recovered.class_node,
3893                        recovered.name,
3894                        Some(recovered.body),
3895                        true,
3896                        Some(recovered.range),
3897                        raw_supertypes,
3898                        &template_scope,
3899                        &mut class_stack,
3900                        ancestry,
3901                    );
3902                    self.parsed.record_materialization(
3903                        MaterializationRecord::RecoveredDeclaration {
3904                            recovery: recovered.range,
3905                            unit: class_unit.clone(),
3906                        },
3907                    );
3908                    let member_scope = ScopeInfo {
3909                        package_name: template_scope.package_name.clone(),
3910                        module: template_scope.module.clone(),
3911                        class_unit: Some(class_unit.clone()),
3912                        template_signature: template_scope.template_signature.clone(),
3913                        template_metadata: None,
3914                        declarations_are_fields: true,
3915                        recovered_specialization_member_scope: recovered
3916                            .class_node
3917                            .child_by_field_name("name")
3918                            .is_some_and(|name| name.kind() == "template_type"),
3919                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
3920                    };
3921                    for tail_member in recovered.tail_members.into_iter().rev() {
3922                        stack.push(CppWork::Node(CppNodeWork {
3923                            node: tail_member,
3924                            scope: member_scope.clone(),
3925                        }));
3926                    }
3927                    stack.extend(class_stack);
3928                    for sibling in recovered.member_siblings {
3929                        self.recovered_class_sibling_scopes
3930                            .insert(sibling.id(), member_scope.clone());
3931                    }
3932                    return;
3933                }
3934                for index in (0..node.named_child_count()).rev() {
3935                    let Some(child) = node.named_child(index) else {
3936                        continue;
3937                    };
3938                    if matches!(
3939                        child.kind(),
3940                        "class_specifier"
3941                            | "struct_specifier"
3942                            | "union_specifier"
3943                            | "enum_specifier"
3944                            | "function_definition"
3945                            | "declaration"
3946                            | "field_declaration"
3947                            | "alias_declaration"
3948                            | "namespace_definition"
3949                    ) {
3950                        let mut template_scope = scope.clone();
3951                        template_scope.template_signature =
3952                            cpp_template_signature(node, child, self.source);
3953                        template_scope.template_metadata =
3954                            cpp_template_metadata(node, child, self.source, ancestry);
3955                        if let Some(recovered) = recover_fragmented_partial_specialization(
3956                            node,
3957                            child,
3958                            self.source,
3959                            ancestry,
3960                        ) {
3961                            let code_unit = self.visit_named_class_like_shape(
3962                                recovered.declaration_node,
3963                                recovered.name,
3964                                None,
3965                                true,
3966                                Some(recovered.range),
3967                                None,
3968                                &template_scope,
3969                                stack,
3970                                ancestry,
3971                            );
3972                            self.parsed.record_materialization(
3973                                MaterializationRecord::RecoveredDeclaration {
3974                                    recovery: recovered.range,
3975                                    unit: code_unit.clone(),
3976                                },
3977                            );
3978                            let mut member_scope = template_scope.clone();
3979                            member_scope.class_unit = Some(code_unit);
3980                            member_scope.declarations_are_fields = true;
3981                            member_scope.recovered_specialization_member_scope = true;
3982                            for prefix_member in recovered.prefix_members.into_iter().rev() {
3983                                stack.push(CppWork::Node(CppNodeWork {
3984                                    node: prefix_member,
3985                                    scope: member_scope.clone(),
3986                                }));
3987                            }
3988                            for sibling in recovered.member_siblings {
3989                                self.recovered_class_sibling_scopes
3990                                    .insert(sibling.id(), member_scope.clone());
3991                            }
3992                            for following in recovered.following_declarations.into_iter().rev() {
3993                                stack.push(CppWork::Node(CppNodeWork {
3994                                    node: following,
3995                                    scope: scope.clone(),
3996                                }));
3997                            }
3998                            return;
3999                        }
4000                        stack.push(CppWork::Node(CppNodeWork {
4001                            node: child,
4002                            scope: template_scope,
4003                        }));
4004                    }
4005                }
4006            }
4007            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4008            "linkage_specification" => {
4009                if let Some(body) = cpp_body_node(node) {
4010                    stack.push(CppWork::Container(CppContainer {
4011                        node: body,
4012                        scope: scope.clone(),
4013                    }));
4014                } else {
4015                    stack.push(CppWork::Container(CppContainer {
4016                        node,
4017                        scope: scope.clone(),
4018                    }));
4019                }
4020            }
4021            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4022                self.visit_class_like(node, scope, stack, ancestry)
4023            }
4024            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4025            // A bare namespace-begin sentinel can make tree-sitter promote the
4026            // wrapped declaration to an ERROR node instead of the usual bogus
4027            // function_definition envelope. Keep the recovery entry point on
4028            // the same structured path for both shapes; ordinary ERROR nodes
4029            // retain their declaration-preserving wrapper traversal when the
4030            // sentinel predicate does not match.
4031            "ERROR" => {
4032                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4033                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4034                    if self.visit_collapsed_macro_declaration_run(node, scope) {
4035                        return;
4036                    }
4037                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4038                        return;
4039                    }
4040                    self.visit_macro_swallowed_function_declarations(node, scope);
4041                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
4042                    self.visit_stranded_class_members(node, scope, ancestry);
4043                    stack.push(CppWork::Container(CppContainer {
4044                        node,
4045                        scope: scope.clone(),
4046                    }));
4047                }
4048            }
4049            "declaration" => {
4050                if scope.class_unit.is_some()
4051                    && scope.declarations_are_fields
4052                    && scope.recovered_specialization_member_scope
4053                    && let Some(alias_name) =
4054                        recovered_using_declaration_alias_name(node, self.source)
4055                {
4056                    self.add_type_aliases(node, scope, vec![alias_name]);
4057                } else {
4058                    self.visit_declaration(
4059                        node,
4060                        scope,
4061                        scope.declarations_are_fields,
4062                        stack,
4063                        ancestry,
4064                    )
4065                }
4066            }
4067            "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4068            "type_definition" | "alias_declaration" => {
4069                self.visit_type_declaration(node, scope, stack, ancestry)
4070            }
4071            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4072            // `#include` is collected by `collect_cpp_includes` before the
4073            // walk, so a directive the container walk never reaches -- inside
4074            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
4075            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
4076            // include claim.
4077            "preproc_include" => {}
4078            kind if preserves_declaration_scope_through_wrapper(
4079                kind,
4080                scope.class_unit.is_some(),
4081            ) =>
4082            {
4083                // A preprocessor conditional gates every declaration inside it
4084                // on a configuration this analyzer never evaluates; record the
4085                // interval so declaration state can say so (issue #1476). The
4086                // else/elif branches are children of the `preproc_if` node, so
4087                // recording the openers covers every branch.
4088                if kind == "labeled_statement" {
4089                    self.visit_access_label_constructor(node, scope);
4090                }
4091                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4092                    let mut range = cpp_declaration_range(node);
4093                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4094                        range.end_byte = boundary.end_byte;
4095                        range.end_line = boundary.end_line;
4096                    }
4097                    self.parsed.record_materialization(
4098                        MaterializationRecord::ConfigurationConditional { range },
4099                    );
4100                    if node.has_error() {
4101                        // A malformed export-macro class can close the namespace
4102                        // node early while the enclosing include guard still owns
4103                        // the remaining class-head/body pairs. The ordinary walk
4104                        // cannot carry the lost namespace through those promoted
4105                        // siblings. Scan only structured ERROR nodes in this
4106                        // already-malformed conditional; the pair recovery's
4107                        // exact class/macro/body predicate remains the admission
4108                        // gate, and its namespace lifting restores the owner.
4109                        let mut candidates = vec![node];
4110                        while let Some(candidate) = candidates.pop() {
4111                            // An `ERROR` still inside a namespace body is one
4112                            // the ordinary walk reaches with that namespace in
4113                            // scope. Claiming it here registers the recovered
4114                            // class at file scope and the consumed region then
4115                            // suppresses the walk that would have named it
4116                            // correctly -- which is why Botan's `DL_Group` was
4117                            // `DL_Group` and not `Botan.DL_Group` in the real
4118                            // header, where an include guard wraps the
4119                            // namespace, and was right in a fixture without one
4120                            // (#2552).
4121                            if candidate.kind() == "ERROR"
4122                                && !cpp_is_inside_namespace_body(candidate, ancestry)
4123                                && self.visit_function_like_export_class_pair(
4124                                    candidate, scope, stack, ancestry,
4125                                )
4126                            {
4127                                continue;
4128                            }
4129                            for index in (0..candidate.named_child_count()).rev() {
4130                                candidates.push(
4131                                    candidate
4132                                        .named_child(index)
4133                                        .expect("index below the node's own named child count"),
4134                                );
4135                            }
4136                        }
4137                    }
4138                }
4139                stack.push(CppWork::Container(CppContainer {
4140                    node,
4141                    scope: scope.clone(),
4142                }))
4143            }
4144            _ => {}
4145        }
4146    }
4147
4148    fn visit_macro_swallowed_function_declarations<'tree>(
4149        &mut self,
4150        envelope: Node<'tree>,
4151        scope: &ScopeInfo,
4152    ) {
4153        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
4154            || envelope.kind() == "ERROR"
4155                && envelope
4156                    .parent()
4157                    .is_some_and(|parent| parent.kind() == "ERROR")
4158        {
4159            return;
4160        }
4161        let mut stack = (0..envelope.named_child_count())
4162            .filter_map(|index| envelope.named_child(index))
4163            .collect::<Vec<_>>();
4164        while let Some(node) = stack.pop() {
4165            if node.kind() == "function_declarator" {
4166                self.visit_error_swallowed_function_declaration(node, scope);
4167            }
4168            for index in 0..node.named_child_count() {
4169                if let Some(child) = node.named_child(index) {
4170                    stack.push(child);
4171                }
4172            }
4173        }
4174    }
4175
4176    /// Index the declarations an attribute-like macro invocation swallowed into
4177    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
4178    /// the shape and why the parser produces it.
4179    fn visit_macro_wrapped_declarations<'tree>(
4180        &mut self,
4181        envelope: Node<'tree>,
4182        scope: &ScopeInfo,
4183        ancestry: &ParentIndex<'tree>,
4184    ) {
4185        let recovered = macro_wrapped_declarations(envelope, self.source);
4186        if recovered.is_empty() {
4187            return;
4188        }
4189        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
4190        self.record_recovered_declarations(recovery, |visitor| {
4191            for declaration in recovered {
4192                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
4193            }
4194        });
4195    }
4196
4197    /// Index the declarations a macro invocation collapsed into one envelope.
4198    /// See [`collapsed_macro_declaration_run`] for the shape and why the parser
4199    /// produces it. Returns whether it claimed `envelope`.
4200    ///
4201    /// The envelope's own nodes cannot be read the way the swallowed tail of
4202    /// the one-line shape can. Of the 214 declarations whisper's `llama.h`
4203    /// hides in it, 57 are shredded to bare identifier and punctuation tokens
4204    /// with no declarator left at all, and the declarators that do survive on
4205    /// the envelope's declarator spine pair one declaration's name with the
4206    /// *next* declaration's parameter list. Reading those would be a guess.
4207    ///
4208    /// The bytes are still ordinary declarations, though, and the collapse is
4209    /// the parser carrying the failure forward from one macro invocation. So
4210    /// reparse the envelope's region, walk the items the parser makes of it,
4211    /// and when one item is itself a collapsed run, recover that invocation
4212    /// from its own bytes and resume the scan just past it. Each pass starts
4213    /// later than the last, so the scan is a loop over the invocations that
4214    /// collapse, not over the declarations: the real header needs three passes
4215    /// for 214 declarations.
4216    fn visit_collapsed_macro_declaration_run(
4217        &mut self,
4218        envelope: Node<'_>,
4219        scope: &ScopeInfo,
4220    ) -> bool {
4221        if collapsed_macro_declaration_run(envelope, self.source).is_none() {
4222            return false;
4223        }
4224        let start = envelope.start_byte();
4225        let end = envelope.end_byte();
4226        let recovery = cpp_recovery_window(self.source, start, end);
4227        self.record_recovered_declarations(recovery, |visitor| {
4228            let mut position = start;
4229            while position < end {
4230                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
4231                    return;
4232                };
4233                let root = tree.root_node();
4234                // A region reparse is its own tree and needs its own parent
4235                // index; the caller's answers nothing about these nodes.
4236                let ancestry = ParentIndex::new(root);
4237                let mut cursor = root.walk();
4238                let collapsed =
4239                    root.named_children(&mut cursor)
4240                        .enumerate()
4241                        .find_map(|(index, item)| {
4242                            collapsed_macro_declaration_run(item, visitor.source)
4243                                .map(|run| (index, item, run))
4244                        });
4245                // Walk only the items before the collapsed one. It swallowed
4246                // the rest of the region, so handing it to the ordinary walk
4247                // would re-enter this recovery on a region that starts where
4248                // this one did.
4249                let mut stack = Vec::new();
4250                push_cpp_sibling_range(
4251                    root,
4252                    0,
4253                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
4254                    scope.clone(),
4255                    &mut stack,
4256                );
4257                visitor.drain_cpp_work(stack, &ancestry);
4258                let Some((_, item, run)) = collapsed else {
4259                    return;
4260                };
4261                // On its own bytes the invocation is the declaration-scope
4262                // shape `macro_wrapped_declarations` reads, so its wrapped
4263                // declaration needs no reader of its own here.
4264                if let Some(head) =
4265                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
4266                {
4267                    let head_root = head.root_node();
4268                    visitor.run_container_work(
4269                        head_root,
4270                        scope.clone(),
4271                        &ParentIndex::new(head_root),
4272                    );
4273                }
4274                assert!(
4275                    run.invocation_end > position,
4276                    "a collapsed run at {position} must end after the byte the scan resumed \
4277                     from, but ended at {}",
4278                    run.invocation_end
4279                );
4280                position = run.invocation_end;
4281            }
4282        });
4283        true
4284    }
4285
4286    /// Index the members a string-argument attribute macro stranded in an
4287    /// `ERROR` inside a class body.
4288    ///
4289    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
4290    /// grouping of the attributed member and of every member written after it
4291    /// until it recovers. The members are still whole `function_declarator`
4292    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
4293    /// export-macro class such as Botan's `DL_Group` was indexed with no
4294    /// members at all (#2552).
4295    fn visit_stranded_class_members<'tree>(
4296        &mut self,
4297        node: Node<'tree>,
4298        scope: &ScopeInfo,
4299        ancestry: &ParentIndex<'tree>,
4300    ) {
4301        if scope.class_unit.is_none() || !scope.declarations_are_fields {
4302            return;
4303        }
4304        for member in stranded_declaration_run(node, self.source).declarations {
4305            self.add_macro_wrapped_declaration(member, scope, ancestry);
4306        }
4307    }
4308
4309    /// Index the constructor an access label swallowed in a reparsed class
4310    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
4311    ///
4312    /// The declarator is recovered by reparsing from that call to the end of
4313    /// the label's own statement, which is the same offset-preserving region
4314    /// reparse every other recovery here uses, so the recovered nodes carry
4315    /// their true source positions.
4316    fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4317        let Some(class_unit) = scope.class_unit.clone() else {
4318            return;
4319        };
4320        if !scope.declarations_are_fields {
4321            return;
4322        }
4323        let class_name = class_unit.identifier().to_string();
4324        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
4325        else {
4326            return;
4327        };
4328        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
4329            return;
4330        };
4331        let root = tree.root_node();
4332        let Some(declarator) =
4333            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
4334        else {
4335            return;
4336        };
4337        let reparsed_ancestry = ParentIndex::new(root);
4338        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
4339        let range = cpp_declaration_range(definition.unwrap_or(declarator));
4340        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
4341        self.record_recovered_declarations(recovery, |visitor| {
4342            visitor.add_macro_wrapped_declaration(
4343                MacroWrappedDeclaration {
4344                    declarator,
4345                    range,
4346                    is_static: false,
4347                },
4348                scope,
4349                &reparsed_ancestry,
4350            );
4351        });
4352    }
4353
4354    fn add_macro_wrapped_declaration<'tree>(
4355        &mut self,
4356        declaration: MacroWrappedDeclaration<'tree>,
4357        scope: &ScopeInfo,
4358        ancestry: &ParentIndex<'tree>,
4359    ) {
4360        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
4361        else {
4362            return;
4363        };
4364        let code_unit =
4365            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
4366        if self.parsed.contains_declaration(&code_unit) {
4367            self.parsed
4368                .record_navigation_range(code_unit, declaration.range);
4369            return;
4370        }
4371        self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
4372        let signature = normalize_cpp_whitespace(
4373            self.source
4374                .get(declaration.range.start_byte..declaration.range.end_byte)
4375                .expect("a recovered declaration range covers one source range"),
4376        );
4377        let linkage = if declaration.is_static {
4378            CallableLinkage::Internal
4379        } else {
4380            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
4381        };
4382        // Whether this is a definition is a property of the recovered node, not
4383        // of the caller: a declarator that a `function_definition` gives a body
4384        // is a definition wherever the recovery found it.
4385        let declaration_only =
4386            cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
4387        self.parsed.add_signature_with_metadata(
4388            code_unit.clone(),
4389            cpp_signature_metadata(signature, declaration.declarator, self.source, ancestry)
4390                .with_declaration_only(declaration_only)
4391                .with_callable_linkage(linkage),
4392        );
4393        if let Some(parent) = &scope.class_unit {
4394            self.parsed.add_child(parent.clone(), code_unit);
4395        } else if let Some(module) = &scope.module {
4396            self.parsed.add_child(module.clone(), code_unit);
4397        }
4398    }
4399
4400    fn visit_error_swallowed_function_declaration<'tree>(
4401        &mut self,
4402        node: Node<'tree>,
4403        scope: &ScopeInfo,
4404    ) -> bool {
4405        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
4406            return false;
4407        };
4408        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
4409            return false;
4410        };
4411        let root = tree.root_node();
4412        let mut cursor = root.walk();
4413        let declarations = root
4414            .named_children(&mut cursor)
4415            .filter(|child| child.kind() != "comment")
4416            .collect::<Vec<_>>();
4417        let [declaration] = declarations.as_slice() else {
4418            return false;
4419        };
4420        if declaration.kind() != "declaration"
4421            || declaration.has_error()
4422            || declaration.start_byte() != start
4423            || declaration.end_byte() != end
4424        {
4425            return false;
4426        }
4427        let recovery = cpp_recovery_window(self.source, start, end);
4428        // The reparsed region is its own tree, so this walk indexes it itself.
4429        let reparsed_ancestry = ParentIndex::new(root);
4430        self.record_recovered_declarations(recovery, |visitor| {
4431            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
4432        });
4433        true
4434    }
4435
4436    fn visit_namespace<'tree>(
4437        &mut self,
4438        node: Node<'tree>,
4439        scope: &ScopeInfo,
4440        stack: &mut Vec<CppWork<'tree>>,
4441        ancestry: &ParentIndex<'tree>,
4442    ) {
4443        let name_node = node.child_by_field_name("name");
4444        let Some(name_node) = name_node else {
4445            if let Some(body) = cpp_body_node(node) {
4446                stack.push(CppWork::Container(CppContainer {
4447                    node: body,
4448                    scope: scope.clone(),
4449                }));
4450            }
4451            return;
4452        };
4453        // Diagnostic corpora contain deliberately ill-formed global namespace
4454        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
4455        // the leading global `::` as the first anonymous child. Honor that AST
4456        // boundary instead of appending the name to the lexical namespace;
4457        // appending produced legacy names such as `outer::::outer::inner`, which
4458        // could not round-trip through the structured FqName boundary.
4459        let explicitly_global = name_node
4460            .child(0)
4461            .is_some_and(|child| !child.is_named() && child.kind() == "::");
4462        let components = cpp_namespace_name_components(name_node, self.source);
4463        if components.is_empty() {
4464            return;
4465        }
4466        // One Module per namespace level. C++17's `namespace a::b { ... }` is
4467        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
4468        // shorthand must declare `a` as well as `a::b` -- extracting only the
4469        // innermost level left the enclosing namespace undeclared and made the
4470        // two spellings of one construct disagree (issue #1878).
4471        let mut package_name = if explicitly_global {
4472            String::new()
4473        } else {
4474            scope.package_name.clone()
4475        };
4476        let mut module = None;
4477        for component in components {
4478            let full_name = if package_name.is_empty() {
4479                component
4480            } else {
4481                format!("{package_name}::{component}")
4482            };
4483            let level = CodeUnit::new_fq(
4484                self.file.clone(),
4485                CodeUnitType::Module,
4486                "",
4487                full_name.clone(),
4488                cpp_namespace_fq(&full_name),
4489            );
4490            if !self.parsed.contains_declaration(&level) {
4491                self.add_declaration(level.clone(), node, None, None);
4492            }
4493            package_name = full_name;
4494            module = Some(level);
4495        }
4496
4497        let namespace_scope = ScopeInfo {
4498            package_name,
4499            module,
4500            // C++ never nests a namespace inside a class, so a surviving
4501            // class_unit here is always recovery bleed: a malformed-region
4502            // boundary upstream mis-scoped this namespace block. Keeping the
4503            // owner would mint the namespace's declarations as class members
4504            // under a re-appended package, desyncing the fq boundary assert
4505            // (#2306). Dropping it is identity-neutral for valid code, where
4506            // class_unit is always empty at a namespace definition.
4507            class_unit: None,
4508            template_signature: scope.template_signature.clone(),
4509            template_metadata: scope.template_metadata.clone(),
4510            declarations_are_fields: false,
4511            recovered_specialization_member_scope: false,
4512            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4513        };
4514        let container = cpp_body_node(node).unwrap_or(node);
4515        // A malformed export-macro class body may turn the following class
4516        // into a descendant of a bogus function/labeled/error envelope. Those
4517        // descendants are not declaration containers and the ordinary walk
4518        // intentionally does not descend into them. Scan the namespace tree
4519        // once for the strict embedded class geometry before scheduling its
4520        // normal declarations. When one envelope matches, its helper recovers
4521        // every embedded class and the walk need not inspect its descendants.
4522        let mut candidates = vec![container];
4523        while let Some(candidate) = candidates.pop() {
4524            if matches!(
4525                candidate.kind(),
4526                "ERROR" | "function_definition" | "labeled_statement"
4527            ) && self.visit_embedded_function_like_export_classes(
4528                candidate,
4529                &namespace_scope,
4530                stack,
4531                ancestry,
4532            ) {
4533                continue;
4534            }
4535            for index in (0..candidate.named_child_count()).rev() {
4536                candidates.push(
4537                    candidate
4538                        .named_child(index)
4539                        .expect("index below the node's own named child count"),
4540                );
4541            }
4542        }
4543        stack.push(CppWork::Container(CppContainer {
4544            node: container,
4545            scope: namespace_scope,
4546        }));
4547    }
4548
4549    fn visit_class_like<'tree>(
4550        &mut self,
4551        node: Node<'tree>,
4552        scope: &ScopeInfo,
4553        stack: &mut Vec<CppWork<'tree>>,
4554        ancestry: &ParentIndex<'tree>,
4555    ) {
4556        let Some(name) = class_like_name(node, self.source, ancestry) else {
4557            return;
4558        };
4559        let name = qualified_class_name_chain(node, self.source, scope)
4560            .map(|chain| chain.join("$"))
4561            .unwrap_or(name);
4562        self.visit_named_class_like(node, name, scope, stack, ancestry);
4563    }
4564
4565    fn visit_named_class_like<'tree>(
4566        &mut self,
4567        node: Node<'tree>,
4568        name: String,
4569        scope: &ScopeInfo,
4570        stack: &mut Vec<CppWork<'tree>>,
4571        ancestry: &ParentIndex<'tree>,
4572    ) {
4573        let body = cpp_body_node(node);
4574        let definition_body_present = body.is_some();
4575        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
4576            .then(|| extract_cpp_supertypes(node, self.source));
4577        self.visit_named_class_like_shape(
4578            node,
4579            name,
4580            body,
4581            definition_body_present,
4582            None,
4583            raw_supertypes,
4584            scope,
4585            stack,
4586            ancestry,
4587        );
4588    }
4589
4590    /// Whether this class-like declaration is a C tag that belongs to the
4591    /// enclosing non-aggregate scope rather than to the aggregate it is
4592    /// lexically written inside.
4593    ///
4594    /// `class_specifier` is deliberately excluded: `class` is not C, so text
4595    /// that spells one in a `.c` file is not C code and keeps the C++ reading
4596    /// rather than getting a half-C identity.
4597    fn mints_tag_at_enclosing_c_scope(
4598        &self,
4599        declaration_node: Node<'_>,
4600        scope: &ScopeInfo,
4601        ancestry: &ParentIndex<'_>,
4602    ) -> bool {
4603        self.c_tag_semantics
4604            && scope.class_unit.is_some()
4605            && class_like_name(declaration_node, self.source, ancestry).is_some()
4606            && matches!(
4607                declaration_node.kind(),
4608                "struct_specifier" | "union_specifier" | "enum_specifier"
4609            )
4610    }
4611
4612    #[allow(clippy::too_many_arguments)]
4613    fn visit_named_class_like_shape<'tree>(
4614        &mut self,
4615        declaration_node: Node<'tree>,
4616        name: String,
4617        body: Option<Node<'tree>>,
4618        definition_body_present: bool,
4619        explicit_range: Option<Range>,
4620        raw_supertypes: Option<Vec<String>>,
4621        scope: &ScopeInfo,
4622        stack: &mut Vec<CppWork<'tree>>,
4623        ancestry: &ParentIndex<'tree>,
4624    ) -> CodeUnit {
4625        let displaced_macro_tail = if explicit_range.is_none() {
4626            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
4627        } else {
4628            None
4629        };
4630        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
4631        let recovered_scope = self.scope_for_recovered_exported_class(
4632            declaration_node,
4633            &name,
4634            definition_body_present,
4635            scope,
4636            ancestry,
4637        );
4638        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
4639        // aggregate's member list is declared at the enclosing non-aggregate
4640        // scope, not nested inside the aggregate. `scope.class_unit` is the
4641        // only aggregate carrier in this walk, so dropping it puts the tag at
4642        // the nearest enclosing non-aggregate scope -- the module at file or
4643        // namespace scope, and the same block-scope representation a
4644        // function-local aggregate already gets. The tag's own body scope
4645        // below still owns its members, so fields and enumerators are
4646        // unaffected.
4647        let c_tag_scope;
4648        let scope =
4649            if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
4650                c_tag_scope = ScopeInfo {
4651                    class_unit: None,
4652                    ..recovered_scope.clone()
4653                };
4654                &c_tag_scope
4655            } else {
4656                &recovered_scope
4657            };
4658        let short_name = if let Some(parent) = &scope.class_unit {
4659            cpp_join_nested_short(parent.short_name(), &name)
4660        } else {
4661            name.clone()
4662        };
4663        // A top-level out-of-line qualified class definition (`struct
4664        // Outer::Inner { ... }` inside its namespace, #2246) carries its
4665        // nesting chain as the `$`-joined display name; push one Type/Nested
4666        // segment per class so segment-pop owner navigation keeps working.
4667        // Every other leaf name stays opaque so a literal `$` in a source
4668        // identifier never crosses the split/join boundary (#2140).
4669        let qualified_chain = if scope.class_unit.is_none() {
4670            qualified_class_name_chain(declaration_node, self.source, scope)
4671                .filter(|chain| chain.join("$") == name)
4672        } else {
4673            None
4674        };
4675        let fq = if let Some(chain) = qualified_chain {
4676            let mut fq = FqName::new();
4677            cpp_push_package(&mut fq, &scope.package_name);
4678            let mut first = true;
4679            for component in chain {
4680                let kind = if first {
4681                    SegmentKind::Type
4682                } else {
4683                    SegmentKind::Nested
4684                };
4685                fq.push(cpp_segment(&component, kind));
4686                first = false;
4687            }
4688            fq
4689        } else {
4690            cpp_leaf_fq(
4691                &scope.package_name,
4692                scope.class_unit.as_ref(),
4693                &name,
4694                SegmentKind::Nested,
4695                SegmentKind::Type,
4696            )
4697        };
4698        let code_unit = CodeUnit::with_signature_and_fq(
4699            self.file.clone(),
4700            CodeUnitType::Class,
4701            scope.package_name.clone(),
4702            short_name,
4703            scope.template_signature.clone(),
4704            false,
4705            fq,
4706        );
4707        let has_body = definition_body_present;
4708        if !has_body && self.parsed.contains_declaration(&code_unit) {
4709            self.parsed.record_navigation_range(
4710                code_unit.clone(),
4711                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
4712            );
4713            return code_unit;
4714        }
4715        if has_body {
4716            if let Some(range) = explicit_range {
4717                self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
4718            } else {
4719                self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
4720            }
4721        } else {
4722            self.add_declaration(code_unit.clone(), declaration_node, None, None);
4723        }
4724        if let Some(raw_supertypes) = raw_supertypes {
4725            self.parsed
4726                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
4727        }
4728        self.parsed.add_signature(
4729            code_unit.clone(),
4730            render_cpp_type_signature(
4731                declaration_node,
4732                self.source,
4733                scope.template_signature.as_deref(),
4734            ),
4735        );
4736        if let Some(metadata) = &scope.template_metadata {
4737            let primary_short_name = if let Some(parent) = &scope.class_unit {
4738                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
4739            } else {
4740                metadata.primary_name.clone()
4741            };
4742            let primary_fq_name = CodeUnit::new(
4743                self.file.clone(),
4744                CodeUnitType::Class,
4745                scope.package_name.clone(),
4746                primary_short_name,
4747            )
4748            .fq_name();
4749            let mut metadata = metadata.clone();
4750            metadata.primary_fq_name = primary_fq_name;
4751            self.parsed
4752                .set_cpp_template_metadata(code_unit.clone(), metadata);
4753        }
4754        if let Some(parent) = &scope.class_unit {
4755            self.parsed.add_child(parent.clone(), code_unit.clone());
4756        } else if let Some(module) = &scope.module {
4757            self.parsed.add_child(module.clone(), code_unit.clone());
4758        }
4759
4760        if let Some(body) = body {
4761            let mut nested_scope = scope.clone();
4762            nested_scope.class_unit = Some(code_unit.clone());
4763            nested_scope.template_signature = scope.template_signature.clone();
4764            // Template metadata describes the class just created. It must not
4765            // leak into ordinary nested declarations in that class's body.
4766            // Recovered export-macro specializations carry a separate scope bit
4767            // for their declaration-shaped body members.
4768            nested_scope.template_metadata = None;
4769            // Export-macro class bodies recovered from a function_definition use
4770            // compound_statement children, whose direct fields are declarations.
4771            nested_scope.recovered_specialization_member_scope =
4772                scope.template_metadata.as_ref().is_some_and(|metadata| {
4773                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
4774                });
4775            nested_scope.declarations_are_fields =
4776                is_recovered_exported_class_container(declaration_node, self.source)
4777                    || nested_scope.recovered_specialization_member_scope;
4778            if let Some(displaced) = displaced_macro_tail {
4779                // A macro-shaped field without a source semicolon can make
4780                // tree-sitter consume the real class terminator as an ERROR
4781                // inside that field, then retain following namespace items as
4782                // later field-list children. Drain the proven class prefix
4783                // first and re-own only the structured tail with the outer
4784                // scope. The tail is pushed first because the work stack is
4785                // LIFO.
4786                push_cpp_sibling_range(
4787                    body,
4788                    displaced.split_index,
4789                    usize::MAX,
4790                    scope.clone(),
4791                    stack,
4792                );
4793                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
4794            } else {
4795                stack.push(CppWork::Container(CppContainer {
4796                    node: body,
4797                    scope: nested_scope,
4798                }));
4799            }
4800        }
4801        if declaration_node.kind() == "enum_specifier" {
4802            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
4803            if !self.has_enum_enumerator_units(&code_unit) {
4804                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
4805            }
4806        }
4807        code_unit
4808    }
4809
4810    /// Whether the parse product already holds enumerator fields for `parent`,
4811    /// answered from the walk's carried-forward field ownership index.
4812    ///
4813    /// Built on the first enum's question and advanced by every declaration
4814    /// recorded after it, so a file that declares no enum -- most files -- pays
4815    /// nothing, and one that declares thousands pays a single pass instead of
4816    /// one per enum (#2786).
4817    fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
4818        if self.field_owners.is_none() {
4819            self.field_owners = Some(CppFieldOwnerIndex::of(
4820                self.parsed.declarations().iter(),
4821                self.file,
4822            ));
4823        }
4824        debug_assert_eq!(
4825            parent.source(),
4826            self.file,
4827            "the walk's declarations are declarations of the file it is walking"
4828        );
4829        let carried = self
4830            .field_owners
4831            .as_ref()
4832            .expect("the index was just ensured")
4833            .owns_fields(parent.package_name(), parent.short_name());
4834
4835        #[cfg(debug_assertions)]
4836        assert_eq!(
4837            carried,
4838            cpp_declarations_hold_owned_fields(
4839                self.parsed.declarations(),
4840                self.file,
4841                parent.package_name(),
4842                parent.short_name()
4843            ),
4844            "the carried-forward field index must answer what a fresh declaration scan \
4845             answers for {}",
4846            parent.fq_name()
4847        );
4848
4849        carried
4850    }
4851
4852    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
4853        walk_named_tree_preorder(node, false, |child| {
4854            if child.kind() != "enumerator" {
4855                return WalkControl::Continue;
4856            }
4857            let Some(name_node) = child.child_by_field_name("name") else {
4858                return WalkControl::Continue;
4859            };
4860            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
4861            if name.is_empty() {
4862                return WalkControl::Continue;
4863            }
4864            let code_unit = CodeUnit::new_fq(
4865                self.file.clone(),
4866                CodeUnitType::Field,
4867                scope.package_name.clone(),
4868                cpp_join_member_short(parent.short_name(), &name),
4869                parent
4870                    .fq()
4871                    .clone()
4872                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
4873            );
4874            if self.parsed.contains_declaration(&code_unit) {
4875                return WalkControl::Continue;
4876            }
4877            self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
4878            self.parsed.add_signature(
4879                code_unit,
4880                normalize_cpp_whitespace(node_text(child, self.source)),
4881            );
4882            WalkControl::Continue
4883        });
4884    }
4885
4886    fn visit_enum_enumerators_from_text(
4887        &mut self,
4888        node: Node<'_>,
4889        scope: &ScopeInfo,
4890        parent: &CodeUnit,
4891    ) {
4892        let text = node_text(node, self.source);
4893        let Some((_, body)) = text.split_once('{') else {
4894            return;
4895        };
4896        let Some((body, _)) = body.rsplit_once('}') else {
4897            return;
4898        };
4899        for entry in body.split(',') {
4900            let trimmed = entry.trim();
4901            let name = trimmed
4902                .split('=')
4903                .next()
4904                .unwrap_or("")
4905                .split_whitespace()
4906                .next()
4907                .unwrap_or("");
4908            if name.is_empty() {
4909                continue;
4910            }
4911            let code_unit = CodeUnit::new_fq(
4912                self.file.clone(),
4913                CodeUnitType::Field,
4914                scope.package_name.clone(),
4915                cpp_join_member_short(parent.short_name(), name),
4916                parent
4917                    .fq()
4918                    .clone()
4919                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
4920            );
4921            if self.parsed.contains_declaration(&code_unit) {
4922                continue;
4923            }
4924            self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
4925            self.parsed.add_signature(code_unit, trimmed.to_string());
4926        }
4927    }
4928
4929    fn visit_function_definition<'tree>(
4930        &mut self,
4931        node: Node<'tree>,
4932        scope: &ScopeInfo,
4933        stack: &mut Vec<CppWork<'tree>>,
4934        ancestry: &ParentIndex<'tree>,
4935    ) {
4936        // An attribute-like macro invocation whose argument is a declaration can
4937        // swallow every declaration written after it into one bogus
4938        // `function_definition` (#2551). This owns the whole region, so it runs
4939        // ahead of `visit_macro_swallowed_function_declarations` below, which
4940        // admits the same envelope by its head macro token and reads single
4941        // declarators out of nodes this recovery reparses properly.
4942        if self.visit_collapsed_macro_declaration_run(node, scope) {
4943            return;
4944        }
4945        // A file-scope object-like macro sentinel the parser cannot see (issue
4946        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
4947        // prefixes as a bogus `function_definition` that swallows real namespaces,
4948        // classes, and members. Reparse the swallowed interior as C++ items so the
4949        // ordinary declaration visitors index it with byte/line-exact ownership.
4950        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4951            return;
4952        }
4953        if node.has_error() {
4954            self.visit_macro_swallowed_function_declarations(node, scope);
4955        }
4956        if let Some((class_node, name, raw_supertypes)) =
4957            recover_exported_class_function_definition(node, self.source)
4958        {
4959            let body = cpp_body_node(class_node);
4960            let displaced_namespace = cpp_body_node(node)
4961                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
4962            let fragmented = cpp_body_node(node).and_then(|body| {
4963                fragmented_export_function_body_region(
4964                    node,
4965                    body,
4966                    self.source,
4967                    displaced_namespace.as_ref(),
4968                )
4969            });
4970            // The recovery tuple's first node is the class-like type when the
4971            // parser exposes one, but the synthetic wrapper owns the compound
4972            // statement that contains the truncated class body. Use the
4973            // wrapper body for fragmented-member detection; retain the
4974            // class-node body for the ordinary (non-fragmented) path below.
4975            if let Some(fragmented) = fragmented {
4976                // The lifted sibling no longer sits below the parser-visible
4977                // namespace node. Restore the current parent scope when the
4978                // ordinary work walk reaches that class.
4979                if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
4980                    .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
4981                {
4982                    let mut boundary_scope = scope.clone();
4983                    for sibling in cpp_following_named_siblings(node, self.source) {
4984                        if sibling.start_byte() >= boundary.start_byte() {
4985                            break;
4986                        }
4987                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
4988                            boundary_scope.visible_using_namespaces.push(namespace);
4989                        }
4990                    }
4991                    self.recovered_class_sibling_scopes
4992                        .insert(boundary.id(), boundary_scope);
4993                }
4994                let mut recovered_constructor = None;
4995                let mut recovered_prefix_tree = None;
4996                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
4997                {
4998                    Some(FragmentedExportMembers::Complete(tree)) => {
4999                        if let Some(body) = body
5000                            && let Some(range) =
5001                                cpp_reparsed_synthetic_initializer_constructor_range(
5002                                    tree.root_node(),
5003                                    &name,
5004                                    self.source,
5005                                    body.end_byte(),
5006                                )
5007                        {
5008                            recovered_constructor = Some(range);
5009                            recovered_prefix_tree = Some(tree);
5010                            None
5011                        } else {
5012                            Some(FragmentedExportMembers::Complete(tree))
5013                        }
5014                    }
5015                    outcome => outcome,
5016                };
5017                let mut class_stack = Vec::new();
5018                let class_unit = self.visit_named_class_like_shape(
5019                    class_node,
5020                    name,
5021                    None,
5022                    true,
5023                    Some(fragmented.class_range),
5024                    raw_supertypes,
5025                    scope,
5026                    &mut class_stack,
5027                    ancestry,
5028                );
5029                self.parsed
5030                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
5031                        recovery: fragmented.class_range,
5032                        unit: class_unit.clone(),
5033                    });
5034                let complete = outcome.is_some_and(|outcome| {
5035                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
5036                });
5037                if complete {
5038                    self.consumed_fragment_regions
5039                        .push((node.start_byte(), fragmented.class_range.end_byte));
5040                } else {
5041                    // The reparse can fail when the first constructor or a
5042                    // method body is split into statement-shaped siblings.
5043                    // Keep the recovered class envelope, but do not visit the
5044                    // synthetic wrapper body: its initializer expressions can
5045                    // look like same-named member functions (for example
5046                    // `Token.location(loc)`). Re-own only the original sibling
5047                    // nodes that fall inside the proven class range. Their CST
5048                    // shapes retain the real field/function kinds and ranges.
5049                    let member_scope = ScopeInfo {
5050                        package_name: class_unit.package_name().to_string(),
5051                        module: scope.module.clone(),
5052                        class_unit: Some(class_unit.clone()),
5053                        template_signature: scope.template_signature.clone(),
5054                        template_metadata: None,
5055                        declarations_are_fields: true,
5056                        recovered_specialization_member_scope: false,
5057                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
5058                    };
5059                    for candidate in cpp_following_named_siblings(node, self.source) {
5060                        if candidate.start_byte() >= fragmented.reparse_end {
5061                            break;
5062                        }
5063                        if cpp_fragment_sibling_is_class_member(
5064                            candidate,
5065                            fragmented.reparse_end,
5066                            self.source,
5067                        ) {
5068                            self.recovered_class_sibling_scopes
5069                                .insert(candidate.id(), member_scope.clone());
5070                        }
5071                    }
5072                    if let Some(range) = recovered_constructor
5073                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
5074                    {
5075                        self.visit_recovered_fragment_prefix_members(
5076                            prefix_tree.root_node(),
5077                            range.start,
5078                            &class_unit,
5079                            scope,
5080                            ancestry,
5081                        );
5082                        self.visit_recovered_fragment_constructor(
5083                            range,
5084                            body,
5085                            class_node,
5086                            &class_unit,
5087                            scope,
5088                            ancestry,
5089                        );
5090                    }
5091                }
5092                if let Some(boundary) = displaced_namespace {
5093                    for item in boundary.namespace_items {
5094                        self.recovered_class_sibling_scopes
5095                            .insert(item.id(), scope.clone());
5096                    }
5097                }
5098                stack.extend(class_stack);
5099                return;
5100            }
5101            let mut stack = Vec::new();
5102            let class_unit = self.visit_named_class_like_shape(
5103                class_node,
5104                name,
5105                body,
5106                body.is_some(),
5107                None,
5108                raw_supertypes,
5109                scope,
5110                &mut stack,
5111                ancestry,
5112            );
5113            self.parsed
5114                .record_materialization(MaterializationRecord::RecoveredDeclaration {
5115                    recovery: cpp_declaration_range(node),
5116                    unit: class_unit,
5117                });
5118            // Issue #1524: the bogus `function_definition` body can run past
5119            // the class's true closing brace (the parse ends it with a
5120            // zero-width `MISSING "}"`), swallowing following namespace-scope
5121            // siblings -- they would index as members of the recovered class.
5122            // When the body's text-balanced close lands before the body's own
5123            // end, re-own the swallowed tail with the outer scope instead.
5124            if let Some(body) = body
5125                && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
5126                && class_close < body.end_byte()
5127            {
5128                let split = {
5129                    let mut cursor = body.walk();
5130                    body.named_children(&mut cursor)
5131                        .position(|child| child.start_byte() > class_close)
5132                };
5133                if let Some(split) = split {
5134                    // The seeded work is a single Container over the whole
5135                    // body with the class scope; replace it with the bounded
5136                    // head (class scope) plus the swallowed tail (outer
5137                    // scope). Push tail first so the head drains first.
5138                    let seeded = stack.pop();
5139                    match seeded {
5140                        Some(CppWork::Container(container)) => {
5141                            push_cpp_sibling_range(
5142                                body,
5143                                split,
5144                                usize::MAX,
5145                                scope.clone(),
5146                                &mut stack,
5147                            );
5148                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
5149                        }
5150                        // visit_named_class_like_shape always seeds exactly
5151                        // one Container when a body is present.
5152                        _ => unreachable!("exported-class seed is always one Container"),
5153                    }
5154                }
5155            }
5156            while let Some(work) = stack.pop() {
5157                match work {
5158                    CppWork::Container(container) => {
5159                        push_cpp_container_work(container.node, container.scope, &mut stack);
5160                    }
5161                    CppWork::Siblings(siblings) => {
5162                        advance_cpp_siblings(siblings, self.source, &mut stack);
5163                    }
5164                    CppWork::Node(work) => {
5165                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
5166                    }
5167                }
5168            }
5169            return;
5170        }
5171        let recovered_constraint_constructor =
5172            cpp_recovered_template_macro_constructor(node, self.source);
5173        let declarator = recovered_constraint_constructor
5174            .map(|(declarator, _)| declarator)
5175            .or_else(|| node.child_by_field_name("declarator"));
5176        let Some(declarator) = declarator else {
5177            self.visit_malformed_function_definition_container(node, scope, stack);
5178            return;
5179        };
5180        let Some(function_declarator) = extract_function_declarator(declarator) else {
5181            self.visit_malformed_function_definition_container(node, scope, stack);
5182            return;
5183        };
5184        let function = if let Some((_, callable_name)) =
5185            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
5186        {
5187            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
5188        } else {
5189            extract_function_info(function_declarator, self.source, scope)
5190        };
5191        let Some(mut function) = function else {
5192            self.visit_malformed_function_definition_container(node, scope, stack);
5193            return;
5194        };
5195        if let Some((_, template_parameter)) = recovered_constraint_constructor {
5196            function.signature = format!(
5197                "template <{}>{}",
5198                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
5199                function.signature
5200            );
5201        }
5202        let code_unit = function.code_unit(self.file.clone());
5203        // Keep an earlier same-file prototype as another physical occurrence
5204        // of this callable. `CodeUnit` already identifies the role-neutral
5205        // overload, while ranges and signature metadata describe its
5206        // declaration/definition occurrences.
5207        self.add_declaration(code_unit.clone(), node, None, None);
5208        let signature = if recovered_constraint_constructor.is_some() {
5209            normalize_cpp_whitespace(node_text(function_declarator, self.source))
5210        } else {
5211            render_cpp_function_display_signature_from_node(
5212                node,
5213                self.source,
5214                scope.template_signature.as_deref(),
5215                true,
5216                ancestry,
5217            )
5218        };
5219        self.parsed.add_signature_with_metadata(
5220            code_unit.clone(),
5221            cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
5222                .with_declaration_only(false)
5223                .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
5224        );
5225        if let Some(parent) = &scope.class_unit {
5226            self.parsed.add_child(parent.clone(), code_unit);
5227        } else if let Some(module) = &scope.module {
5228            self.parsed.add_child(module.clone(), code_unit);
5229        }
5230    }
5231
5232    /// Recover the namespace lost when tree-sitter promotes an export-macro
5233    /// class definition to a root-level `function_definition`.  Only a
5234    /// body-bearing, top-level recovery may borrow a namespace, and only when
5235    /// one earlier namespace-scope forward declaration proves the identity.
5236    fn scope_for_recovered_exported_class<'tree>(
5237        &mut self,
5238        node: Node<'tree>,
5239        name: &str,
5240        definition_body_present: bool,
5241        scope: &ScopeInfo,
5242        ancestry: &ParentIndex<'tree>,
5243    ) -> ScopeInfo {
5244        if !definition_body_present
5245            || !scope.package_name.is_empty()
5246            || scope.class_unit.is_some()
5247            || !(is_recovered_exported_class_container(node, self.source)
5248                || recover_function_like_export_class_pair(node, self.source).is_some()
5249                || recover_embedded_function_like_export_classes(node, self.source)
5250                    .iter()
5251                    .any(|recovered| recovered.name == name)
5252                || matches!(node.kind(), "declaration" | "field_declaration")
5253                    && recover_exported_class_declaration(node, self.source).is_some()
5254                || matches!(
5255                    node.kind(),
5256                    "class_specifier" | "struct_specifier" | "union_specifier"
5257                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
5258                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
5259                        name_node,
5260                        self.source,
5261                    )))
5262                }) || ancestry.parent(node).is_some_and(|parent| {
5263                    matches!(parent.kind(), "declaration" | "field_declaration")
5264                        && recover_exported_class_declaration(parent, self.source).is_some()
5265                        || is_recovered_exported_class_container(parent, self.source)
5266                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
5267        {
5268            return scope.clone();
5269        }
5270        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
5271        let Some(package_name) = borrowed_namespace
5272            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
5273        else {
5274            return scope.clone();
5275        };
5276
5277        let module = CodeUnit::new_fq(
5278            self.file.clone(),
5279            CodeUnitType::Module,
5280            "",
5281            package_name.clone(),
5282            cpp_namespace_fq(&package_name),
5283        );
5284        let mut recovered = scope.clone();
5285        recovered.package_name = package_name;
5286        recovered.module = Some(module);
5287        recovered
5288    }
5289
5290    /// The unique namespace-scope forward declaration of `name` that precedes
5291    /// `recovered_node`, answered from the walk's carried-forward scan of the
5292    /// tree `recovered_node` belongs to.
5293    ///
5294    /// The scan is built on the first question and advanced by each later one,
5295    /// so a file that never reaches this path -- almost every file -- pays
5296    /// nothing, and one that reaches it thousands of times pays a single pass
5297    /// (#2754).
5298    fn unique_earlier_namespace_forward<'tree>(
5299        &mut self,
5300        recovered_node: Node<'tree>,
5301        name: &str,
5302        ancestry: &ParentIndex<'tree>,
5303    ) -> Option<String> {
5304        let mut root = recovered_node;
5305        while let Some(parent) = ancestry.parent(root) {
5306            root = parent;
5307        }
5308        let source = self.source;
5309        let scan = self
5310            .namespace_forward_scans
5311            .entry(CppTreeIdentity::of(root))
5312            .or_default();
5313        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
5314        let borrowed = scan.unique_earlier_forward(name, recovered_node);
5315
5316        #[cfg(debug_assertions)]
5317        assert_eq!(
5318            borrowed,
5319            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
5320            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
5321             for {name} at byte {}",
5322            recovered_node.start_byte()
5323        );
5324
5325        borrowed
5326    }
5327
5328    fn visit_malformed_function_definition_container<'tree>(
5329        &mut self,
5330        node: Node<'tree>,
5331        scope: &ScopeInfo,
5332        stack: &mut Vec<CppWork<'tree>>,
5333    ) {
5334        let Some(body) = cpp_body_node(node) else {
5335            return;
5336        };
5337        if !cpp_contains_namespace_definition(body) {
5338            return;
5339        }
5340        stack.push(CppWork::Container(CppContainer {
5341            node: body,
5342            scope: scope.clone(),
5343        }));
5344    }
5345
5346    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
5347    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
5348    /// emits for a sentinel-prefixed region, reparse the interior after the
5349    /// sentinel identifier as real C++ items -- confined to the region so
5350    /// every reparsed node keeps its original byte/line position -- and run the
5351    /// ordinary container visitation over the result. Returns `true` when it fired
5352    /// (the caller must then skip normal function processing). Nested sentinel
5353    /// regions recover recursively: the reparsed interior is walked through the
5354    /// same `visit_function_definition` path, so a sentinel inside the region hits
5355    /// this recovery again.
5356    /// Runs `reparse_walk` and records every declaration it mints as a
5357    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
5358    /// `recovery` (issue #1657). A reparsed sentinel region has no single
5359    /// recovered envelope unit: the ordinary visitors mint namespaces,
5360    /// classes, and members directly from the reparsed tree, so the walk's
5361    /// declaration delta is the recovered set. Records are ordered by
5362    /// declaration start byte so the parse product stays deterministic.
5363    fn record_recovered_declarations(
5364        &mut self,
5365        recovery: Range,
5366        reparse_walk: impl FnOnce(&mut Self),
5367    ) {
5368        // The set difference this used to be, kept as the oracle every answer
5369        // is asserted against (#2787).
5370        #[cfg(any(debug_assertions, test))]
5371        let before = self.parsed.declarations().clone();
5372
5373        self.recovery_captures.push(CppRecoveryCapture::default());
5374        reparse_walk(self);
5375        let captured = self
5376            .recovery_captures
5377            .pop()
5378            .expect("the capture this call pushed is the one it pops");
5379
5380        // The capture holds every declaration created while it was open, once
5381        // each and in creation order, so the recovered set costs what the
5382        // recovery made rather than everything the file has declared so far.
5383        // One filter is left to apply: a created declaration that a later
5384        // deferred replacement removed is not in the parse product to report.
5385        let mut minted: Vec<CodeUnit> = captured
5386            .created
5387            .into_iter()
5388            .filter(|unit| self.parsed.contains_declaration(unit))
5389            .collect();
5390        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
5391
5392        #[cfg(any(debug_assertions, test))]
5393        {
5394            let mut rediscovered: Vec<CodeUnit> = self
5395                .parsed
5396                .declarations()
5397                .iter()
5398                .filter(|unit| !before.contains(*unit))
5399                .cloned()
5400                .collect();
5401            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
5402            assert_eq!(
5403                minted, rediscovered,
5404                "the captured recovered set must be the declaration delta of the reparse \
5405                 walk over {recovery:?}"
5406            );
5407        }
5408
5409        for unit in minted {
5410            self.parsed
5411                .record_materialization(MaterializationRecord::RecoveredDeclaration {
5412                    recovery,
5413                    unit,
5414                });
5415        }
5416    }
5417
5418    /// Where one recovered declaration sorts: by start byte, then by name, so
5419    /// the parse product stays deterministic.
5420    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
5421        let start = self
5422            .parsed
5423            .declaration_ranges(unit)
5424            .first()
5425            .map(|range| range.start_byte)
5426            .unwrap_or(usize::MAX);
5427        (start, unit.fq_name().to_string())
5428    }
5429
5430    fn visit_sentinel_macro_region<'tree>(
5431        &mut self,
5432        node: Node<'tree>,
5433        scope: &ScopeInfo,
5434        stack: &mut Vec<CppWork<'tree>>,
5435        ancestry: &ParentIndex<'tree>,
5436    ) -> bool {
5437        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
5438            return true;
5439        }
5440        if let Some((
5441            reparse_start,
5442            class_start,
5443            body_start,
5444            class_close_start,
5445            class_close_end,
5446            class_close_line,
5447        )) = cpp_sentinel_macro_class_region(node, self.source)
5448        {
5449            let Some(class_tree) =
5450                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
5451            else {
5452                return false;
5453            };
5454            let class_root = class_tree.root_node();
5455            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
5456            // A region reparse is its own tree and needs its own parent index.
5457            let class_ancestry = ParentIndex::new(class_root);
5458            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
5459                class_root,
5460                template_node,
5461                self.source,
5462                &class_ancestry,
5463            ) else {
5464                return false;
5465            };
5466            let class_node = reparsed_class.declaration_node;
5467            let name = reparsed_class.name;
5468            let mut class_scope = scope.clone();
5469            if let Some(template_node) = template_node {
5470                class_scope.template_signature =
5471                    cpp_template_signature(template_node, class_node, self.source);
5472                class_scope.template_metadata =
5473                    cpp_template_metadata(template_node, class_node, self.source, ancestry);
5474            }
5475            let Some(body_tree) =
5476                cpp_reparse_region_items(self.source, body_start, class_close_start)
5477            else {
5478                return false;
5479            };
5480            let raw_supertypes = reparsed_class.raw_supertypes;
5481            let class_range = Range {
5482                start_byte: class_start,
5483                end_byte: class_close_end,
5484                start_line: class_node.start_position().row + 1,
5485                end_line: class_close_line,
5486            };
5487            let class_scope = self.scope_for_recovered_exported_class(
5488                class_node,
5489                &name,
5490                true,
5491                &class_scope,
5492                ancestry,
5493            );
5494            let mut class_stack = Vec::new();
5495            let class_unit = self.visit_named_class_like_shape(
5496                class_node,
5497                name,
5498                None,
5499                true,
5500                Some(class_range),
5501                raw_supertypes,
5502                &class_scope,
5503                &mut class_stack,
5504                ancestry,
5505            );
5506            self.parsed
5507                .record_materialization(MaterializationRecord::RecoveredDeclaration {
5508                    recovery: class_range,
5509                    unit: class_unit.clone(),
5510                });
5511            let member_scope = ScopeInfo {
5512                package_name: class_scope.package_name.clone(),
5513                module: class_scope.module.clone(),
5514                class_unit: Some(class_unit),
5515                template_signature: class_scope.template_signature.clone(),
5516                template_metadata: None,
5517                declarations_are_fields: true,
5518                recovered_specialization_member_scope: false,
5519                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
5520            };
5521            // The padded body reparse is its own tree, so it indexes itself.
5522            let body_root = body_tree.root_node();
5523            self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
5524            // Register only after the padded body reparse: its nodes deliberately
5525            // retain offsets inside the consumed region and must be visited first.
5526            self.consumed_fragment_regions
5527                .push((node.start_byte(), class_close_end));
5528            // An ERROR envelope can hold real sibling declarations after the
5529            // recovered class's close (the suffix-reparse boundary in
5530            // `cpp_sentinel_macro_class_region` partitions, it does not
5531            // consume). Walk the envelope's remaining children normally; the
5532            // consumed region above keeps the recovered class from being
5533            // indexed twice.
5534            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
5535                stack.push(CppWork::Container(CppContainer {
5536                    node,
5537                    scope: scope.clone(),
5538                }));
5539            }
5540            return true;
5541        }
5542        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
5543            return false;
5544        };
5545        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5546            return false;
5547        };
5548        let root = tree.root_node();
5549        if !cpp_reparsed_items_are_indexable(root, self.source) {
5550            return false;
5551        }
5552        let recovery = cpp_recovery_window(self.source, start, end);
5553        // The reparsed region is its own tree, so this walk indexes it itself.
5554        let reparsed_ancestry = ParentIndex::new(root);
5555        self.record_recovered_declarations(recovery, |visitor| {
5556            visitor.visit_container(
5557                root,
5558                &reparsed_ancestry,
5559                &scope.package_name,
5560                scope.module.clone(),
5561                scope.class_unit.clone(),
5562                scope.template_signature.clone(),
5563                scope.visible_using_namespaces.clone(),
5564            );
5565        });
5566        if end > node.end_byte() {
5567            self.consumed_fragment_regions
5568                .push((node.start_byte(), end));
5569        } else if node.kind() == "ERROR" && node.end_byte() > end {
5570            // The sentinel region ended at the first recovered class-like item
5571            // but the ERROR envelope keeps real sibling declarations after it
5572            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
5573            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
5574            // envelope's remaining children normally; the consumed region
5575            // keeps the reparsed prefix from being indexed twice.
5576            self.consumed_fragment_regions
5577                .push((node.start_byte(), end));
5578            stack.push(CppWork::Container(CppContainer {
5579                node,
5580                scope: scope.clone(),
5581            }));
5582        }
5583        true
5584    }
5585
5586    /// Re-own complete class declarations from the structured Abseil
5587    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
5588    /// its direct CST children already prove both namespace components and the
5589    /// class bodies, so the ordinary class/member visitor can retain ownership
5590    /// and exact source ranges without admitting unrelated callable bodies.
5591    fn visit_nested_namespace_sentinel<'tree>(
5592        &mut self,
5593        node: Node<'tree>,
5594        scope: &ScopeInfo,
5595        ancestry: &ParentIndex<'tree>,
5596    ) -> bool {
5597        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
5598            return false;
5599        };
5600
5601        let mut package_name = scope.package_name.clone();
5602        let mut module = scope.module.clone();
5603        for component in recovered.namespace_components {
5604            package_name = if package_name.is_empty() {
5605                component
5606            } else {
5607                format!("{package_name}::{component}")
5608            };
5609            let namespace_module = CodeUnit::new_fq(
5610                self.file.clone(),
5611                CodeUnitType::Module,
5612                "",
5613                package_name.clone(),
5614                cpp_namespace_fq(&package_name),
5615            );
5616            if !self.parsed.contains_declaration(&namespace_module) {
5617                self.add_declaration(namespace_module.clone(), recovered.function, None, None);
5618            }
5619            module = Some(namespace_module);
5620        }
5621
5622        let recovered_scope = ScopeInfo {
5623            package_name,
5624            module,
5625            class_unit: scope.class_unit.clone(),
5626            template_signature: scope.template_signature.clone(),
5627            template_metadata: scope.template_metadata.clone(),
5628            declarations_are_fields: false,
5629            recovered_specialization_member_scope: false,
5630            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5631        };
5632        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
5633            recovered.function,
5634            recovered.body,
5635            self.source,
5636            ancestry,
5637        ) {
5638            let mut class_scope = recovered_scope.clone();
5639            if let Some(template_node) = fragmented.template_node {
5640                class_scope.template_signature =
5641                    cpp_template_signature(template_node, fragmented.class_node, self.source);
5642                class_scope.template_metadata = cpp_template_metadata(
5643                    template_node,
5644                    fragmented.class_node,
5645                    self.source,
5646                    ancestry,
5647                );
5648            }
5649            if let Some(outcome) = self
5650                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
5651            {
5652                let mut class_stack = Vec::new();
5653                let class_unit = self.visit_named_class_like_shape(
5654                    fragmented.class_node,
5655                    fragmented.name.clone(),
5656                    None,
5657                    true,
5658                    Some(fragmented.fragmented.class_range),
5659                    fragmented.raw_supertypes.clone(),
5660                    &class_scope,
5661                    &mut class_stack,
5662                    ancestry,
5663                );
5664                self.parsed
5665                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
5666                        recovery: fragmented.fragmented.class_range,
5667                        unit: class_unit.clone(),
5668                    });
5669                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
5670                    self.consumed_fragment_regions.push((
5671                        fragmented.consumed_start,
5672                        fragmented.fragmented.class_range.end_byte,
5673                    ));
5674                }
5675            }
5676        }
5677        // The class requirement above is the admission gate; once admitted,
5678        // traverse the whole proven inner namespace body so sibling aliases,
5679        // functions, and variables are not silently discarded. The body is a
5680        // node of the tree being walked, so it reuses that tree's index.
5681        self.run_container_work(recovered.body, recovered_scope, ancestry);
5682        true
5683    }
5684
5685    fn visit_declaration<'tree>(
5686        &mut self,
5687        node: Node<'tree>,
5688        scope: &ScopeInfo,
5689        in_class_body: bool,
5690        stack: &mut Vec<CppWork<'tree>>,
5691        ancestry: &ParentIndex<'tree>,
5692    ) {
5693        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5694            return;
5695        }
5696        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
5697            !cpp_active_template_type_parameter(
5698                node,
5699                node_text(declarator, self.source),
5700                self.source,
5701                ancestry,
5702            )
5703        }) {
5704            return;
5705        }
5706        if in_class_body
5707            && let Some(parent) = scope.class_unit.as_ref()
5708            && let Some(call) =
5709                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
5710        {
5711            self.visit_recovered_macro_qualified_constructor_definition(
5712                node, call, scope, ancestry,
5713            );
5714            return;
5715        }
5716        if in_class_body
5717            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
5718        {
5719            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
5720            return;
5721        }
5722        if in_class_body
5723            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
5724        {
5725            for member in members {
5726                self.add_macro_wrapped_declaration(member, scope, ancestry);
5727            }
5728            return;
5729        }
5730        if in_class_body
5731            && let Some(declarators) =
5732                recovered_macro_qualified_field_declarators(node, self.source)
5733        {
5734            for declarator in declarators {
5735                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
5736            }
5737            return;
5738        }
5739        let recovered_alias_names = recovered_type_alias_names(node, self.source);
5740        if !recovered_alias_names.is_empty() {
5741            self.add_type_aliases(node, scope, recovered_alias_names);
5742            return;
5743        }
5744        if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
5745        {
5746            return;
5747        }
5748
5749        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
5750            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
5751                // Issue #938: the members tree-sitter scattered out of the fragmented
5752                // multiple-base export node are reparsed from their true body region
5753                // and re-owned as members of the recovered class, with an explicit
5754                // navigation range spanning to the displaced closing brace.
5755                if let Some(outcome) =
5756                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
5757                {
5758                    let consumed_region = (
5759                        recovered.declaration_node.end_byte(),
5760                        fragmented.class_range.end_byte,
5761                    );
5762                    let code_unit = self.visit_named_class_like_shape(
5763                        recovered.declaration_node,
5764                        recovered.name,
5765                        None,
5766                        true,
5767                        Some(fragmented.class_range),
5768                        recovered.raw_supertypes,
5769                        scope,
5770                        stack,
5771                        ancestry,
5772                    );
5773                    self.parsed.record_materialization(
5774                        MaterializationRecord::RecoveredDeclaration {
5775                            recovery: fragmented.class_range,
5776                            unit: code_unit.clone(),
5777                        },
5778                    );
5779                    let consume_fragment =
5780                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
5781                    // Everything between the fragmented declaration and its displaced
5782                    // closing brace now belongs to the recovered class; keep the
5783                    // ordinary walk from re-indexing those scattered siblings at top
5784                    // level. Register the consumed region only after indexing because
5785                    // the reparsed nodes retain byte offsets inside that same region.
5786                    if consume_fragment {
5787                        self.consumed_fragment_regions.push(consumed_region);
5788                    }
5789                    return;
5790                }
5791            }
5792            let uses_initializer_body = recovered.uses_initializer_body;
5793            let definition_body_present = recovered.body.is_some();
5794            let class_unit = self.visit_named_class_like_shape(
5795                recovered.declaration_node,
5796                recovered.name,
5797                recovered.body,
5798                definition_body_present,
5799                None,
5800                recovered.raw_supertypes,
5801                scope,
5802                stack,
5803                ancestry,
5804            );
5805            self.parsed
5806                .record_materialization(MaterializationRecord::RecoveredDeclaration {
5807                    recovery: cpp_declaration_range(node),
5808                    unit: class_unit,
5809                });
5810            if uses_initializer_body {
5811                return;
5812            }
5813        }
5814
5815        let mut handled_function = false;
5816        let mut handled_declarator = false;
5817        let mut cursor = node.walk();
5818        for child in node.named_children(&mut cursor) {
5819            if matches!(
5820                child.kind(),
5821                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
5822            ) {
5823                // A named class-like definition remains a declaration even when
5824                // the same statement also declares an object, for example
5825                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
5826                // declaration's type and `kind` as its declarator.  Dropping the
5827                // type here loses both its nested owner and every later lexical
5828                // reference to it.  A body is the structured proof that this is
5829                // a definition rather than an elaborated type use such as
5830                // `class Kind value;`.
5831                if cpp_body_node(child).is_some() {
5832                    self.visit_class_like(child, scope, stack, ancestry);
5833                }
5834                continue;
5835            }
5836        }
5837
5838        let mut cursor = node.walk();
5839        for child in node.children_by_field_name("declarator", &mut cursor) {
5840            if crate::structural::is_recovered_designator_init_declarator(child) {
5841                handled_declarator = true;
5842                continue;
5843            }
5844            if let Some(kind) = classify_declarator(child) {
5845                handled_declarator = true;
5846                match kind {
5847                    DeclaratorKind::Function(function_declarator) => {
5848                        handled_function = true;
5849                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
5850                    }
5851                    DeclaratorKind::Variable(variable_declarator) => {
5852                        self.visit_variable_declaration(
5853                            node,
5854                            variable_declarator,
5855                            scope,
5856                            in_class_body,
5857                            ancestry,
5858                        );
5859                    }
5860                }
5861            }
5862        }
5863
5864        if !handled_declarator {
5865            let mut cursor = node.walk();
5866            for child in node.named_children(&mut cursor) {
5867                if crate::structural::is_recovered_designator_init_declarator(child) {
5868                    handled_declarator = true;
5869                    continue;
5870                }
5871                if !is_unfielded_declarator_candidate(child) {
5872                    continue;
5873                }
5874                let Some(kind) = classify_declarator(child) else {
5875                    continue;
5876                };
5877                handled_declarator = true;
5878                match kind {
5879                    DeclaratorKind::Function(function_declarator) => {
5880                        handled_function = true;
5881                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
5882                    }
5883                    DeclaratorKind::Variable(variable_declarator) => {
5884                        self.visit_variable_declaration(
5885                            node,
5886                            variable_declarator,
5887                            scope,
5888                            in_class_body,
5889                            ancestry,
5890                        );
5891                    }
5892                }
5893            }
5894        }
5895
5896        if handled_function {
5897            return;
5898        }
5899
5900        if !handled_declarator {
5901            if in_class_body {
5902                self.visit_class_members_from_declaration(node, scope, ancestry);
5903            } else {
5904                self.visit_global_variables_from_declaration(node, scope, ancestry);
5905            }
5906        }
5907    }
5908
5909    /// Preserve the member structure of an anonymous C aggregate.
5910    ///
5911    /// An anonymous union with no declarator promotes its fields into the
5912    /// containing aggregate. An anonymous struct/union followed by a named
5913    /// declarator, such as `struct { T *ops; } sock`, declares both the field
5914    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
5915    /// the declarator's structured nested identity so a later `value.sock.ops`
5916    /// chain can traverse it without parsing a type spelling (#2407).
5917    fn visit_c_anonymous_aggregate_declaration<'tree>(
5918        &mut self,
5919        node: Node<'tree>,
5920        scope: &ScopeInfo,
5921        in_class_body: bool,
5922        stack: &mut Vec<CppWork<'tree>>,
5923        ancestry: &ParentIndex<'tree>,
5924    ) -> bool {
5925        if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
5926            return false;
5927        }
5928        let Some(aggregate) = node.child_by_field_name("type") else {
5929            return false;
5930        };
5931        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
5932            || aggregate.child_by_field_name("name").is_some()
5933        {
5934            return false;
5935        }
5936        let Some(body) = cpp_body_node(aggregate) else {
5937            return false;
5938        };
5939
5940        let mut cursor = node.walk();
5941        let declarators = node
5942            .children_by_field_name("declarator", &mut cursor)
5943            .filter_map(|declarator| match classify_declarator(declarator) {
5944                Some(DeclaratorKind::Variable(variable)) => Some(variable),
5945                Some(DeclaratorKind::Function(_)) | None => None,
5946            })
5947            .collect::<Vec<_>>();
5948        if declarators.is_empty() {
5949            stack.push(CppWork::Container(CppContainer {
5950                node: body,
5951                scope: scope.clone(),
5952            }));
5953            return true;
5954        }
5955
5956        for declarator in declarators {
5957            let Some(name) = extract_variable_name(declarator, self.source) else {
5958                continue;
5959            };
5960            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
5961            self.visit_named_class_like_shape(
5962                aggregate,
5963                name,
5964                Some(body),
5965                true,
5966                None,
5967                None,
5968                scope,
5969                stack,
5970                ancestry,
5971            );
5972        }
5973        true
5974    }
5975
5976    fn visit_function_declaration<'tree>(
5977        &mut self,
5978        declaration_node: Node<'tree>,
5979        declarator: Node<'tree>,
5980        scope: &ScopeInfo,
5981        ancestry: &ParentIndex<'tree>,
5982    ) {
5983        let Some(function) = extract_function_info(declarator, self.source, scope) else {
5984            return;
5985        };
5986        let code_unit =
5987            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
5988        if self.parsed.contains_declaration(&code_unit) {
5989            self.parsed
5990                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
5991            return;
5992        }
5993        self.add_declaration(code_unit.clone(), declaration_node, None, None);
5994        let signature = render_cpp_function_display_signature_from_node(
5995            declaration_node,
5996            self.source,
5997            scope.template_signature.as_deref(),
5998            false,
5999            ancestry,
6000        );
6001        self.parsed.add_signature_with_metadata(
6002            code_unit.clone(),
6003            cpp_signature_metadata(signature, declarator, self.source, ancestry)
6004                .with_declaration_only(true)
6005                .with_callable_linkage(cpp_callable_linkage(
6006                    declaration_node,
6007                    self.source,
6008                    ancestry,
6009                )),
6010        );
6011        if let Some(parent) = &scope.class_unit {
6012            self.parsed.add_child(parent.clone(), code_unit);
6013        } else if let Some(module) = &scope.module {
6014            self.parsed.add_child(module.clone(), code_unit);
6015        }
6016    }
6017
6018    fn visit_recovered_macro_qualified_function_declaration<'tree>(
6019        &mut self,
6020        declaration_node: Node<'tree>,
6021        call: Node<'tree>,
6022        scope: &ScopeInfo,
6023        ancestry: &ParentIndex<'tree>,
6024    ) {
6025        let Some(parent) = &scope.class_unit else {
6026            return;
6027        };
6028        let Some(name_node) = call.child_by_field_name("function") else {
6029            return;
6030        };
6031        let Some(arguments) = call.child_by_field_name("arguments") else {
6032            return;
6033        };
6034        let Some((signature, parameter_labels)) =
6035            recovered_macro_qualified_function_parameters(arguments, self.source)
6036        else {
6037            return;
6038        };
6039        let arity = parameter_labels.len();
6040        let function = FunctionInfo {
6041            package_name: scope.package_name.clone(),
6042            owner: Some(CppMemberOwner::Unit(parent.clone())),
6043            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
6044            signature,
6045        };
6046        if function.name.is_empty() {
6047            return;
6048        }
6049        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6050        if self.parsed.contains_declaration(&code_unit) {
6051            self.parsed
6052                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
6053            return;
6054        }
6055        self.add_declaration(code_unit.clone(), declaration_node, None, None);
6056        let signature_label = render_cpp_function_display_signature_from_node(
6057            declaration_node,
6058            self.source,
6059            scope.template_signature.as_deref(),
6060            false,
6061            ancestry,
6062        );
6063        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
6064            .with_declaration_only(true)
6065            .with_callable_arity(CallableArity::exact(arity))
6066            .with_callable_linkage(cpp_callable_linkage(
6067                declaration_node,
6068                self.source,
6069                ancestry,
6070            ));
6071        self.parsed
6072            .add_signature_with_metadata(code_unit.clone(), metadata);
6073        self.parsed.add_child(parent.clone(), code_unit);
6074    }
6075
6076    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
6077        &mut self,
6078        declaration_node: Node<'tree>,
6079        call: Node<'tree>,
6080        scope: &ScopeInfo,
6081        ancestry: &ParentIndex<'tree>,
6082    ) {
6083        let Some(parent) = &scope.class_unit else {
6084            return;
6085        };
6086        let Some(arguments) = call.child_by_field_name("arguments") else {
6087            return;
6088        };
6089        let Some((mut signature, parameter_labels)) =
6090            recovered_macro_qualified_function_parameters(arguments, self.source)
6091        else {
6092            return;
6093        };
6094        if let Some(template_signature) = &scope.template_signature {
6095            signature = format!("{template_signature}{signature}");
6096        }
6097        let arity = parameter_labels.len();
6098        let function = FunctionInfo {
6099            package_name: scope.package_name.clone(),
6100            owner: Some(CppMemberOwner::Unit(parent.clone())),
6101            name: parent.identifier().to_string(),
6102            signature,
6103        };
6104        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6105        self.add_declaration(code_unit.clone(), declaration_node, None, None);
6106        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
6107        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
6108            .with_declaration_only(false)
6109            .with_callable_arity(CallableArity::exact(arity))
6110            .with_callable_linkage(cpp_callable_linkage(
6111                declaration_node,
6112                self.source,
6113                ancestry,
6114            ));
6115        self.parsed
6116            .add_signature_with_metadata(code_unit.clone(), metadata);
6117        self.parsed.add_child(parent.clone(), code_unit);
6118    }
6119
6120    fn visit_variable_declaration<'tree>(
6121        &mut self,
6122        declaration_node: Node<'tree>,
6123        declarator: Node<'tree>,
6124        scope: &ScopeInfo,
6125        in_class_body: bool,
6126        ancestry: &ParentIndex<'tree>,
6127    ) {
6128        let Some(name) = extract_variable_name(declarator, self.source) else {
6129            return;
6130        };
6131        let parent = if in_class_body {
6132            let Some(parent) = &scope.class_unit else {
6133                return;
6134            };
6135            Some(parent)
6136        } else {
6137            None
6138        };
6139        let short_name = match parent {
6140            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
6141            None => name.clone(),
6142        };
6143        let fq = cpp_leaf_fq(
6144            &scope.package_name,
6145            parent,
6146            &name,
6147            SegmentKind::Member,
6148            SegmentKind::Member,
6149        );
6150        let code_unit = CodeUnit::new_fq(
6151            self.file.clone(),
6152            CodeUnitType::Field,
6153            scope.package_name.clone(),
6154            short_name,
6155            fq,
6156        );
6157        if self.parsed.contains_declaration(&code_unit) {
6158            return;
6159        }
6160        self.add_declaration(code_unit.clone(), declaration_node, None, None);
6161        self.parsed.add_signature_with_metadata(
6162            code_unit.clone(),
6163            SignatureMetadata::new(
6164                render_cpp_field_signature(declaration_node, declarator, self.source),
6165                Vec::new(),
6166            )
6167            .with_cpp_field_linkage(cpp_field_declaration_linkage(
6168                declaration_node,
6169                self.source,
6170                ancestry,
6171            )),
6172        );
6173        if let Some(parent) = &scope.class_unit {
6174            self.parsed.add_child(parent.clone(), code_unit);
6175        } else if let Some(module) = &scope.module {
6176            self.parsed.add_child(module.clone(), code_unit);
6177        }
6178    }
6179
6180    fn visit_class_members_from_declaration<'tree>(
6181        &mut self,
6182        node: Node<'tree>,
6183        scope: &ScopeInfo,
6184        ancestry: &ParentIndex<'tree>,
6185    ) {
6186        let mut cursor = node.walk();
6187        for child in node.named_children(&mut cursor) {
6188            if child.kind() == "init_declarator"
6189                && let Some(inner) = child.child_by_field_name("declarator")
6190            {
6191                self.visit_variable_declaration(node, inner, scope, true, ancestry);
6192            } else if matches!(
6193                child.kind(),
6194                "identifier"
6195                    | "field_identifier"
6196                    | "pointer_declarator"
6197                    | "reference_declarator"
6198                    | "array_declarator"
6199                    | "parenthesized_declarator"
6200            ) {
6201                self.visit_variable_declaration(node, child, scope, true, ancestry);
6202            }
6203        }
6204    }
6205
6206    fn visit_global_variables_from_declaration<'tree>(
6207        &mut self,
6208        node: Node<'tree>,
6209        scope: &ScopeInfo,
6210        ancestry: &ParentIndex<'tree>,
6211    ) {
6212        let mut cursor = node.walk();
6213        for child in node.named_children(&mut cursor) {
6214            if child.kind() == "init_declarator"
6215                && let Some(inner) = child.child_by_field_name("declarator")
6216            {
6217                self.visit_variable_declaration(node, inner, scope, false, ancestry);
6218            } else if matches!(
6219                child.kind(),
6220                "identifier"
6221                    | "field_identifier"
6222                    | "pointer_declarator"
6223                    | "reference_declarator"
6224                    | "array_declarator"
6225                    | "parenthesized_declarator"
6226            ) {
6227                self.visit_variable_declaration(node, child, scope, false, ancestry);
6228            }
6229        }
6230    }
6231
6232    fn visit_type_declaration<'tree>(
6233        &mut self,
6234        node: Node<'tree>,
6235        scope: &ScopeInfo,
6236        stack: &mut Vec<CppWork<'tree>>,
6237        ancestry: &ParentIndex<'tree>,
6238    ) {
6239        let type_node = node.child_by_field_name("type");
6240        if let Some(type_node) = type_node
6241            && matches!(
6242                type_node.kind(),
6243                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6244            )
6245        {
6246            self.visit_class_like(type_node, scope, stack, ancestry);
6247        }
6248
6249        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
6250            let range = Range {
6251                start_byte: node.start_byte(),
6252                end_byte: recovered.end_node.end_byte(),
6253                start_line: node.start_position().row + 1,
6254                end_line: recovered.end_node.end_position().row + 1,
6255            };
6256            let signature = self
6257                .source
6258                .get(range.start_byte..range.end_byte)
6259                .map(normalize_cpp_whitespace)
6260                .unwrap_or_default();
6261            self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
6262            return;
6263        }
6264
6265        let alias_names = match node.kind() {
6266            "alias_declaration" => extract_alias_declaration_name(node, self.source)
6267                .into_iter()
6268                .collect::<Vec<_>>(),
6269            "type_definition" => extract_typedef_alias_names(node, self.source),
6270            _ => Vec::new(),
6271        };
6272        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
6273            (type_node, alias_names.as_slice())
6274            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
6275            && type_node.child_by_field_name("name").is_none()
6276        {
6277            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
6278        } else {
6279            None
6280        };
6281        self.add_type_aliases(node, scope, alias_names);
6282        if let Some((body, alias_name)) = anonymous_aggregate {
6283            // The typedef alias is also the only user-visible identity of an
6284            // anonymous aggregate. Reuse it as the member owner instead of
6285            // minting a second signatureless class with the same FQN. The
6286            // latter makes forward lookup ambiguous when conditional aliases
6287            // coexist and returns duplicate definitions even without guards.
6288            let signature = normalize_cpp_whitespace(node_text(node, self.source));
6289            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
6290            debug_assert!(self.parsed.contains_declaration(&alias_unit));
6291            let mut nested_scope = scope.clone();
6292            nested_scope.class_unit = Some(alias_unit);
6293            nested_scope.template_signature = scope.template_signature.clone();
6294            nested_scope.template_metadata = None;
6295            nested_scope.declarations_are_fields = false;
6296            nested_scope.recovered_specialization_member_scope = false;
6297            stack.push(CppWork::Container(CppContainer {
6298                node: body,
6299                scope: nested_scope,
6300            }));
6301        }
6302    }
6303
6304    fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
6305        let signature = normalize_cpp_whitespace(node_text(node, self.source));
6306        self.record_type_aliases(
6307            node,
6308            scope,
6309            alias_names,
6310            signature,
6311            cpp_declaration_range(node),
6312        );
6313    }
6314
6315    fn record_type_aliases(
6316        &mut self,
6317        node: Node<'_>,
6318        scope: &ScopeInfo,
6319        alias_names: Vec<String>,
6320        signature: String,
6321        range: Range,
6322    ) {
6323        if signature.is_empty() {
6324            return;
6325        }
6326        let type_name = node
6327            .child_by_field_name("type")
6328            .and_then(|type_node| type_node.child_by_field_name("name"))
6329            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
6330        for alias_name in alias_names {
6331            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
6332                continue;
6333            }
6334            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
6335            // Declaration identity does not include the alias signature. Keep
6336            // each physical range so conditional aliases retain their guards.
6337            self.add_declaration_with_range(code_unit.clone(), range, None, None);
6338            self.parsed
6339                .add_signature(code_unit.clone(), signature.clone());
6340            if let Some(metadata) = &scope.template_metadata {
6341                let mut metadata = metadata.clone();
6342                metadata.primary_fq_name = code_unit.fq_name();
6343                self.parsed
6344                    .set_cpp_template_metadata(code_unit.clone(), metadata);
6345            }
6346            if let Some(parent) = &scope.class_unit {
6347                self.parsed.add_child(parent.clone(), code_unit.clone());
6348            } else if let Some(module) = &scope.module {
6349                self.parsed.add_child(module.clone(), code_unit.clone());
6350            }
6351            self.parsed.mark_type_alias(code_unit);
6352        }
6353    }
6354
6355    fn type_alias_unit(
6356        &self,
6357        scope: &ScopeInfo,
6358        alias_name: String,
6359        signature: String,
6360    ) -> CodeUnit {
6361        let short_name = if let Some(parent) = &scope.class_unit {
6362            cpp_join_nested_short(parent.short_name(), &alias_name)
6363        } else {
6364            alias_name.clone()
6365        };
6366        let fq = cpp_leaf_fq(
6367            &scope.package_name,
6368            scope.class_unit.as_ref(),
6369            &alias_name,
6370            SegmentKind::Nested,
6371            SegmentKind::Type,
6372        );
6373        CodeUnit::with_signature_and_fq(
6374            self.file.clone(),
6375            CodeUnitType::Class,
6376            scope.package_name.clone(),
6377            short_name,
6378            Some(signature),
6379            false,
6380            fq,
6381        )
6382    }
6383
6384    fn visit_macro(&mut self, node: Node<'_>) {
6385        let Some(name) = extract_macro_name(node, self.source) else {
6386            return;
6387        };
6388        let signature = node_text(node, self.source).trim_end().to_string();
6389        if signature.is_empty() {
6390            return;
6391        }
6392        let fq = cpp_member_fq("", &name);
6393        // A macro can be undefined and redefined later in the same file. Its
6394        // structured directive is part of the declaration identity so the
6395        // temporal environment can navigate to the definition active at a
6396        // reference instead of collapsing every spelling to the first range.
6397        // The same physical directive parsed through another C/C++ reading
6398        // still produces the same unit and remains deduplicated.
6399        let code_unit = CodeUnit::with_signature_and_fq(
6400            self.file.clone(),
6401            CodeUnitType::Macro,
6402            "",
6403            name,
6404            Some(signature.clone()),
6405            false,
6406            fq,
6407        );
6408        if self.parsed.contains_declaration(&code_unit) {
6409            return;
6410        }
6411        self.add_declaration(code_unit.clone(), node, None, None);
6412        let name_range = node
6413            .child_by_field_name("name")
6414            .map(cpp_declaration_range)
6415            .unwrap_or_else(|| cpp_declaration_range(node));
6416        self.parsed
6417            .record_materialization(MaterializationRecord::GeneratedDeclaration {
6418                site: cpp_declaration_range(node),
6419                argument: name_range,
6420                kind: GenerationKind::PreprocessorDefinition,
6421                unit: code_unit.clone(),
6422            });
6423        self.parsed.add_signature(code_unit, signature);
6424    }
6425}
6426
6427/// Classify a C++ field while its declaration syntax is already available.
6428///
6429/// The persisted result lets later visibility queries avoid reparsing the
6430/// complete source file only to recover linkage.
6431pub fn cpp_field_declaration_linkage<'tree>(
6432    declaration: Node<'tree>,
6433    source: &str,
6434    ancestry: &ParentIndex<'tree>,
6435) -> CppFieldLinkage {
6436    let mut current = ancestry.parent(declaration);
6437    let mut enclosed_by_class = false;
6438    while let Some(node) = current {
6439        if node.kind() == "namespace_definition"
6440            && node
6441                .child_by_field_name("name")
6442                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6443        {
6444            return CppFieldLinkage::Internal;
6445        }
6446        if matches!(
6447            node.kind(),
6448            "class_specifier" | "struct_specifier" | "union_specifier"
6449        ) && node
6450            .child_by_field_name("name")
6451            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6452        {
6453            return CppFieldLinkage::Internal;
6454        }
6455        if matches!(
6456            node.kind(),
6457            "class_specifier" | "struct_specifier" | "union_specifier"
6458        ) {
6459            enclosed_by_class = true;
6460        }
6461        if matches!(node.kind(), "function_definition" | "lambda_expression") {
6462            return CppFieldLinkage::Internal;
6463        }
6464        current = ancestry.parent(node);
6465    }
6466    if enclosed_by_class {
6467        return CppFieldLinkage::External;
6468    }
6469    let mut cursor = declaration.walk();
6470    let mut has_static = false;
6471    let mut has_extern = false;
6472    let mut has_inline = false;
6473    let mut has_const = false;
6474    let mut has_constexpr = false;
6475    for child in declaration.named_children(&mut cursor) {
6476        let text = normalize_cpp_whitespace(node_text(child, source));
6477        match (child.kind(), text.as_str()) {
6478            ("storage_class_specifier", "static") => has_static = true,
6479            ("storage_class_specifier", "extern") => has_extern = true,
6480            ("storage_class_specifier", "inline") => has_inline = true,
6481            ("storage_class_specifier", "constexpr") => has_constexpr = true,
6482            ("type_qualifier", "const") => has_const = true,
6483            ("type_qualifier", "constexpr") => has_constexpr = true,
6484            _ => {}
6485        }
6486    }
6487    if has_static {
6488        CppFieldLinkage::Internal
6489    } else if has_extern || has_inline {
6490        CppFieldLinkage::External
6491    } else if has_const || has_constexpr {
6492        CppFieldLinkage::InternalUnlessExternalPeer
6493    } else {
6494        CppFieldLinkage::External
6495    }
6496}
6497
6498fn cpp_declaration_range(node: Node<'_>) -> Range {
6499    Range {
6500        start_byte: node.start_byte(),
6501        end_byte: node.end_byte(),
6502        start_line: node.start_position().row + 1,
6503        end_line: node.end_position().row + 1,
6504    }
6505}
6506
6507/// A recovery interval as a [`Range`], for materialization records whose
6508/// window is a byte region rather than one parser node (the sentinel-macro
6509/// region reparses, issue #941/#1657).
6510fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
6511    let line_at = |byte: usize| {
6512        source.as_bytes()[..byte]
6513            .iter()
6514            .filter(|&&b| b == b'\n')
6515            .count()
6516            + 1
6517    };
6518    Range {
6519        start_byte,
6520        end_byte,
6521        start_line: line_at(start_byte),
6522        end_line: line_at(end_byte),
6523    }
6524}
6525
6526/// Every `#include` directive the tree holds, in source order.
6527///
6528/// A preorder sweep rather than a step of the declaration walk: the container
6529/// walk descends only through declaration scopes, so an include written inside
6530/// a class body or a function body would otherwise never be seen, and an
6531/// include is an include wherever it is written.
6532pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
6533    walk_named_tree_preorder(root, true, |node| {
6534        if node.kind() == "preproc_include" {
6535            let raw = normalize_cpp_whitespace(node_text(node, source));
6536            if !raw.is_empty() {
6537                parsed.imports.push(ImportInfo {
6538                    raw_snippet: raw,
6539                    is_wildcard: false,
6540                    is_global: false,
6541                    identifier: None,
6542                    alias: None,
6543                    path: None,
6544                    binder_span: None,
6545                });
6546            }
6547            return WalkControl::SkipChildren;
6548        }
6549        WalkControl::Continue
6550    });
6551}
6552
6553pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
6554    let mut in_block_comment = false;
6555    for line in source.lines() {
6556        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
6557        let trimmed = stripped.trim();
6558        if !looks_like_quoted_include_line(trimmed) {
6559            continue;
6560        }
6561
6562        let raw = normalize_cpp_whitespace(trimmed);
6563        // The tree-sitter walk already recorded every `#include` it could see;
6564        // this line scan only recovers the ones a parse error hid, so skip a
6565        // snippet that is already an import binding.
6566        if parsed
6567            .imports
6568            .iter()
6569            .any(|import| import.raw_snippet == raw)
6570        {
6571            continue;
6572        }
6573
6574        parsed.imports.push(ImportInfo {
6575            raw_snippet: raw,
6576            is_wildcard: false,
6577            is_global: false,
6578            identifier: None,
6579            alias: None,
6580            path: None,
6581            binder_span: None,
6582        });
6583    }
6584}
6585
6586fn looks_like_quoted_include_line(line: &str) -> bool {
6587    let Some(rest) = line.trim_start().strip_prefix('#') else {
6588        return false;
6589    };
6590    let Some(rest) = rest.trim_start().strip_prefix("include") else {
6591        return false;
6592    };
6593    rest.trim_start().starts_with('"')
6594}
6595
6596fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
6597    let mut raw = Vec::new();
6598    let mut cursor = node.walk();
6599    for child in node.named_children(&mut cursor) {
6600        if child.kind() == "base_class_clause" {
6601            collect_cpp_base_nodes(child, source, &mut raw);
6602        }
6603    }
6604    raw
6605}
6606
6607fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
6608    walk_named_tree_preorder(node, false, |child| match child.kind() {
6609        "type_identifier" | "qualified_identifier" | "template_type" => {
6610            let text = normalize_cpp_whitespace(node_text(child, source));
6611            if !text.is_empty() {
6612                raw.push(text);
6613            }
6614            WalkControl::SkipChildren
6615        }
6616        _ => WalkControl::Continue,
6617    });
6618}
6619
6620fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
6621    let mut out = String::new();
6622    let chars: Vec<char> = line.chars().collect();
6623    let mut index = 0;
6624    let mut in_string = false;
6625    let mut in_char = false;
6626    let mut escape = false;
6627
6628    while index < chars.len() {
6629        let ch = chars[index];
6630        let next = chars.get(index + 1).copied();
6631
6632        if *in_block_comment {
6633            if ch == '*' && next == Some('/') {
6634                *in_block_comment = false;
6635                index += 2;
6636            } else {
6637                index += 1;
6638            }
6639            continue;
6640        }
6641
6642        if in_string {
6643            out.push(ch);
6644            if escape {
6645                escape = false;
6646            } else if ch == '\\' {
6647                escape = true;
6648            } else if ch == '"' {
6649                in_string = false;
6650            }
6651            index += 1;
6652            continue;
6653        }
6654
6655        if in_char {
6656            out.push(ch);
6657            if escape {
6658                escape = false;
6659            } else if ch == '\\' {
6660                escape = true;
6661            } else if ch == '\'' {
6662                in_char = false;
6663            }
6664            index += 1;
6665            continue;
6666        }
6667
6668        if ch == '/' && next == Some('/') {
6669            break;
6670        }
6671        if ch == '/' && next == Some('*') {
6672            *in_block_comment = true;
6673            index += 2;
6674            continue;
6675        }
6676        if ch == '"' {
6677            in_string = true;
6678            out.push(ch);
6679            index += 1;
6680            continue;
6681        }
6682        if ch == '\'' {
6683            in_char = true;
6684            out.push(ch);
6685            index += 1;
6686            continue;
6687        }
6688
6689        out.push(ch);
6690        index += 1;
6691    }
6692
6693    out
6694}
6695
6696#[derive(Clone)]
6697struct FunctionInfo {
6698    package_name: String,
6699    owner: Option<CppMemberOwner>,
6700    name: String,
6701    signature: String,
6702}
6703
6704/// Owner of a member function, kept structured so a literal `$` inside a
6705/// source-level class name never crosses a join/split boundary: the legacy
6706/// `$`-joined owner string was re-split at fq construction, dropping a leading
6707/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
6708/// and tripping the package/short boundary assert -- the #2140 corruption one
6709/// level up (#2362).
6710#[derive(Clone)]
6711enum CppMemberOwner {
6712    /// Source-level owner class chain from a qualified declarator-id, one
6713    /// class name per component (`Outer::Inner::method` -> `["Outer",
6714    /// "Inner"]`); each component may itself contain a literal `$`.
6715    Chain(Vec<String>),
6716    /// The lexically enclosing or recovered class unit; the member fq extends
6717    /// its fq directly instead of re-splitting its `$`-joined short chain.
6718    Unit(CodeUnit),
6719}
6720
6721impl CppMemberOwner {
6722    /// The legacy `$`-joined owner chain used in the member's short name.
6723    fn short_chain(&self) -> String {
6724        match self {
6725            Self::Chain(chain) => chain.join("$"),
6726            Self::Unit(parent) => parent.short_name().to_string(),
6727        }
6728    }
6729}
6730
6731enum DeclaratorKind<'a> {
6732    Function(Node<'a>),
6733    Variable(Node<'a>),
6734}
6735
6736impl FunctionInfo {
6737    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
6738        self.code_unit_with_synthetic(file, false)
6739    }
6740
6741    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
6742        let short_name = match &self.owner {
6743            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
6744            None => self.name.clone(),
6745        };
6746        let fq = match &self.owner {
6747            Some(CppMemberOwner::Chain(chain)) => {
6748                debug_assert!(
6749                    !chain.is_empty(),
6750                    "an empty owner chain is no owner; producers return None instead"
6751                );
6752                let mut fq = FqName::new();
6753                cpp_push_package(&mut fq, &self.package_name);
6754                let mut first = true;
6755                for component in chain {
6756                    let kind = if first {
6757                        SegmentKind::Type
6758                    } else {
6759                        SegmentKind::Nested
6760                    };
6761                    fq.push(cpp_segment(component, kind));
6762                    first = false;
6763                }
6764                fq.push(cpp_segment(&self.name, SegmentKind::Member));
6765                fq
6766            }
6767            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
6768                .fq()
6769                .clone()
6770                .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
6771            // An anonymous parent (empty short chain) contributes no owner
6772            // segment -- the same guard as cpp_join_member_short above.
6773            Some(CppMemberOwner::Unit(_)) | None => {
6774                let mut fq = FqName::new();
6775                cpp_push_package(&mut fq, &self.package_name);
6776                fq.push(cpp_segment(&self.name, SegmentKind::Member));
6777                fq
6778            }
6779        };
6780        CodeUnit::with_signature_and_fq(
6781            file,
6782            CodeUnitType::Function,
6783            self.package_name.clone(),
6784            short_name,
6785            Some(self.signature.clone()),
6786            synthetic,
6787            fq,
6788        )
6789    }
6790}
6791
6792fn extract_function_info(
6793    declarator: Node<'_>,
6794    source: &str,
6795    scope: &ScopeInfo,
6796) -> Option<FunctionInfo> {
6797    let parameters_node = declarator.child_by_field_name("parameters")?;
6798    let declarator_name_node = declarator
6799        .child_by_field_name("declarator")
6800        .or_else(|| parameters_node.prev_named_sibling())?;
6801    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
6802}
6803
6804fn extract_function_info_from_name(
6805    declarator: Node<'_>,
6806    declarator_name_node: Node<'_>,
6807    source: &str,
6808    scope: &ScopeInfo,
6809) -> Option<FunctionInfo> {
6810    let parameters_node = declarator.child_by_field_name("parameters")?;
6811    let parameters_text = cpp_parameter_signature(parameters_node, source);
6812    let recovered_specialization_member = scope
6813        .recovered_specialization_member_scope
6814        .then(|| {
6815            let terminal = declarator_name_node
6816                .child_by_field_name("name")
6817                .unwrap_or(declarator_name_node);
6818            let name = canonical_cpp_qualified_component(terminal, source)?.name;
6819            let owner = scope.class_unit.as_ref()?;
6820            Some((
6821                Some(CppMemberOwner::Unit(owner.clone())),
6822                name,
6823                scope.package_name.clone(),
6824            ))
6825        })
6826        .flatten();
6827    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
6828        parts
6829    } else if let Some(parts) =
6830        split_structured_templated_cpp_name(declarator_name_node, source, scope)
6831    {
6832        parts
6833    } else {
6834        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
6835            declarator_name_node,
6836            source,
6837        )?);
6838        if raw_name.is_empty() {
6839            return None;
6840        }
6841        split_cpp_name(&raw_name, scope)
6842    };
6843    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
6844    let mut signature = if suffix.is_empty() {
6845        parameters_text
6846    } else {
6847        format!("{parameters_text} {suffix}")
6848    };
6849    if let Some(template_signature) = &scope.template_signature {
6850        signature = format!("{template_signature}{signature}");
6851    }
6852
6853    Some(FunctionInfo {
6854        package_name,
6855        owner,
6856        name,
6857        signature,
6858    })
6859}
6860
6861/// Recover the semantic return type and callable name when a declaration macro
6862/// occupies a function definition's `type` field. Tree-sitter either exposes a
6863/// scalar return as the declarator's apparent name and the callable as the sole
6864/// identifier in an `ERROR`, or joins a template return and callable into a
6865/// qualified identifier with a missing `::`. Both shapes retain the complete
6866/// parameter list and body; a concrete separator remains an out-of-line member.
6867fn cpp_macro_displaced_callable_parts<'tree>(
6868    function_declarator: Node<'tree>,
6869    source: &str,
6870    ancestry: &ParentIndex<'tree>,
6871) -> Option<(Node<'tree>, Node<'tree>)> {
6872    let definition = ancestry.parent(function_declarator)?;
6873    if definition.kind() != "function_definition"
6874        || definition.child_by_field_name("declarator") != Some(function_declarator)
6875        || definition
6876            .child_by_field_name("body")
6877            .is_none_or(|body| body.kind() != "compound_statement")
6878    {
6879        return None;
6880    }
6881    let macro_type = definition.child_by_field_name("type")?;
6882    if macro_type.kind() != "type_identifier"
6883        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
6884    {
6885        return None;
6886    }
6887
6888    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
6889    if apparent_return_type.kind() == "qualified_identifier"
6890        && let (Some(return_type), Some(callable_name)) = (
6891            apparent_return_type.child_by_field_name("scope"),
6892            apparent_return_type.child_by_field_name("name"),
6893        )
6894        && return_type.kind() == "template_type"
6895        && matches!(callable_name.kind(), "identifier" | "field_identifier")
6896        && (0..apparent_return_type.child_count())
6897            .filter_map(|index| apparent_return_type.child(index))
6898            .any(|child| child.kind() == "::" && child.is_missing())
6899        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
6900        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
6901    {
6902        return Some((return_type, callable_name));
6903    }
6904    if !matches!(
6905        apparent_return_type.kind(),
6906        "identifier" | "field_identifier" | "type_identifier"
6907    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
6908    {
6909        return None;
6910    }
6911    let parameters = function_declarator.child_by_field_name("parameters")?;
6912    let mut cursor = function_declarator.walk();
6913    let between = function_declarator
6914        .named_children(&mut cursor)
6915        .filter(|child| child.kind() != "comment")
6916        .filter(|child| {
6917            child.start_byte() >= apparent_return_type.end_byte()
6918                && child.end_byte() <= parameters.start_byte()
6919                && !same_node(*child, apparent_return_type)
6920                && !same_node(*child, parameters)
6921        })
6922        .collect::<Vec<_>>();
6923    let [name_error] = between.as_slice() else {
6924        return None;
6925    };
6926    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
6927        return None;
6928    }
6929    let callable_name = name_error.named_child(0)?;
6930    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
6931        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
6932    {
6933        return None;
6934    }
6935    Some((apparent_return_type, callable_name))
6936}
6937
6938/// The part of a `function_declarator` after its parameter list that belongs to
6939/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
6940/// specification, a trailing return type and a trailing requires-clause.
6941///
6942/// The grammar makes each of these a distinct sibling of the `parameters`
6943/// field, so they are read from the tree. Splitting the declarator's text on
6944/// the parameter list instead silently dropped every qualifier whenever the
6945/// parameter list was spelled with whitespace that normalization rewrote - a
6946/// line break or a double space was enough to make a `const` member definition
6947/// a different logical symbol from its declaration (#1827).
6948///
6949/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
6950/// are deliberately excluded. C++ does not make them part of the signature and
6951/// an out-of-line definition never repeats them, so including them would split
6952/// a declaration from its own definition.
6953fn cpp_declarator_identity_suffix(
6954    declarator: Node<'_>,
6955    parameters_node: Node<'_>,
6956    source: &str,
6957) -> String {
6958    let mut cursor = declarator.walk();
6959    let parts = declarator
6960        .named_children(&mut cursor)
6961        .filter(|child| child.start_byte() >= parameters_node.end_byte())
6962        .filter(|child| {
6963            matches!(
6964                child.kind(),
6965                "type_qualifier"
6966                    | "ref_qualifier"
6967                    | "noexcept"
6968                    | "throw_specifier"
6969                    | "trailing_return_type"
6970                    | "requires_clause"
6971            )
6972        })
6973        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
6974        .filter(|text| !text.is_empty())
6975        .collect::<Vec<_>>();
6976    normalize_cpp_qualifier_suffix(&parts.join(" "))
6977}
6978
6979/// The identity suffix of one callable declarator, for a consumer that holds
6980/// the declarator rather than the declaration walk's parts.
6981///
6982/// The persisted signature concatenates the parameter spelling and this suffix,
6983/// so a comparison that must agree on the suffix alone recomputes it here
6984/// instead of splitting the stored string.
6985pub(crate) fn cpp_callable_identity_suffix(
6986    function_declarator: Node<'_>,
6987    source: &str,
6988) -> Option<String> {
6989    let parameters_node = function_declarator.child_by_field_name("parameters")?;
6990    Some(cpp_declarator_identity_suffix(
6991        function_declarator,
6992        parameters_node,
6993        source,
6994    ))
6995}
6996
6997pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
6998    match classify_declarator(node)? {
6999        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
7000        DeclaratorKind::Variable(_) => None,
7001    }
7002}
7003
7004fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
7005    match node.kind() {
7006        "function_declarator" => {
7007            let inner = node
7008                .child_by_field_name("declarator")
7009                .or_else(|| node.child_by_field_name("name"))
7010                .or_else(|| last_named_child(node));
7011            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
7012                Some(DeclaratorKind::Variable(node))
7013            } else {
7014                Some(DeclaratorKind::Function(node))
7015            }
7016        }
7017        "init_declarator"
7018        | "pointer_declarator"
7019        | "reference_declarator"
7020        | "parenthesized_declarator"
7021        | "array_declarator"
7022        | "attributed_declarator"
7023        | "template_function" => node
7024            .child_by_field_name("declarator")
7025            .or_else(|| node.child_by_field_name("name"))
7026            .or_else(|| last_named_child(node))
7027            .and_then(classify_declarator),
7028        "identifier" | "field_identifier" | "qualified_identifier" => {
7029            Some(DeclaratorKind::Variable(node))
7030        }
7031        _ => node
7032            .child_by_field_name("declarator")
7033            .or_else(|| node.child_by_field_name("name"))
7034            .or_else(|| last_named_child(node))
7035            .and_then(classify_declarator),
7036    }
7037}
7038
7039fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
7040    matches!(
7041        node.kind(),
7042        "function_declarator"
7043            | "init_declarator"
7044            | "pointer_declarator"
7045            | "reference_declarator"
7046            | "parenthesized_declarator"
7047            | "array_declarator"
7048            | "attributed_declarator"
7049            | "template_function"
7050            | "identifier"
7051            | "field_identifier"
7052            | "qualified_identifier"
7053    )
7054}
7055
7056fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
7057    let class_like = first_class_like_child(node);
7058    let mut cursor = node.walk();
7059    node.named_children(&mut cursor).any(|child| {
7060        matches!(
7061            child.kind(),
7062            "init_declarator"
7063                | "pointer_declarator"
7064                | "reference_declarator"
7065                | "array_declarator"
7066                | "function_declarator"
7067                | "parenthesized_declarator"
7068                | "attributed_declarator"
7069        ) || matches!(
7070            child.kind(),
7071            "identifier" | "field_identifier" | "qualified_identifier"
7072        ) && class_like.is_none_or(|class_node| {
7073            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
7074        })
7075    })
7076}
7077
7078/// One namespace-scope forward class declaration that a recovered export-macro
7079/// class definition may borrow its identity from.  Tree-sitter can close a
7080/// malformed class at the enclosing namespace's closing brace, leaving the later
7081/// class definitions as root-level recovered `function_definition` nodes.  A
7082/// preceding `class Name;` in the same namespace is the only structured identity
7083/// signal available in that shape.
7084///
7085/// Everything recorded here is a property of the forward declaration alone.
7086/// What depends on the node doing the asking -- that the forward and its
7087/// namespace both close before it, with nothing but recovery trivia between --
7088/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
7089/// tree answers every later question about it.
7090struct CppNamespaceForward {
7091    name: String,
7092    start_byte: usize,
7093    /// Where the malformed namespace that held the forward ended.  No query
7094    /// asks anything else about that node.
7095    namespace_end_byte: usize,
7096    package_name: String,
7097}
7098
7099/// Read `node` as a borrowable namespace forward declaration.
7100///
7101/// The admission is deliberately conservative: only a body-less class specifier
7102/// whose declaration has no declarator, at namespace scope rather than inside a
7103/// function or class body, in a namespace that itself failed to parse.
7104fn cpp_namespace_forward_entry<'tree>(
7105    node: Node<'tree>,
7106    source: &str,
7107    ancestry: &ParentIndex<'tree>,
7108) -> Option<CppNamespaceForward> {
7109    if !matches!(
7110        node.kind(),
7111        "class_specifier" | "struct_specifier" | "union_specifier"
7112    ) || cpp_body_node(node).is_some()
7113    {
7114        return None;
7115    }
7116    let parent = node.parent()?;
7117    if !(parent.kind() == "declaration_list"
7118        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
7119    {
7120        return None;
7121    }
7122    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
7123    // Borrowing is only justified by the parser-recovery shape we are
7124    // repairing: the namespace that held the forward must itself contain a
7125    // syntax error. A clean, unrelated namespace forward is not an identity
7126    // proof.
7127    if !namespace.has_error() {
7128        return None;
7129    }
7130    Some(CppNamespaceForward {
7131        name: class_like_name(node, source, ancestry)?,
7132        start_byte: node.start_byte(),
7133        namespace_end_byte: namespace.end_byte(),
7134        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
7135    })
7136}
7137
7138/// Whether `forward` stands in the recovery relation to the node asking about
7139/// it: it and its malformed namespace both closed before the recovered class,
7140/// and nothing but recovery trivia separates the two.
7141fn cpp_namespace_forward_matches_recovery(
7142    forward: &CppNamespaceForward,
7143    recovered_node: Node<'_>,
7144) -> bool {
7145    forward.start_byte < recovered_node.start_byte()
7146        && forward.namespace_end_byte < recovered_node.start_byte()
7147        && malformed_namespace_is_nearest_recovery_region(
7148            forward.namespace_end_byte,
7149            recovered_node,
7150        )
7151}
7152
7153/// What one open [`CppVisitor::record_recovered_declarations`] has watched
7154/// happen to the declaration set.
7155///
7156/// The recovered set used to be a difference against a clone of the whole
7157/// declaration set, taken once per recovery: O(recoveries x declarations) on
7158/// exactly the error-recovered files that already walk slowest (#2787). The
7159/// walk knows which declarations it creates, so the capture collects them as
7160/// they are made and the difference is never needed.
7161///
7162/// `removed_pre_existing` is what makes that equal to the difference. A
7163/// deferred replacement removes the replaced declaration's children
7164/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
7165/// re-creates them. Creation alone cannot tell that apart from a first mint, so
7166/// a removal of a declaration this capture did not create records that it was
7167/// already there when the capture opened.
7168#[derive(Debug, Default)]
7169pub struct CppRecoveryCapture {
7170    /// Declarations created while this capture was open, in creation order.
7171    created: Vec<CodeUnit>,
7172    /// Membership for `created`.
7173    created_units: HashSet<CodeUnit>,
7174    /// Declarations that predate this capture and have been removed during it.
7175    removed_pre_existing: HashSet<CodeUnit>,
7176}
7177
7178/// Which owners the parse product already holds field declarations for, folded
7179/// in as the walk records them.
7180///
7181/// [`CppVisitor::has_enum_enumerator_units`] asks that question once per enum
7182/// and used to answer it by scanning every declaration accumulated so far:
7183/// O(enums x declarations) on exactly the generated headers that declare many
7184/// of both (#2786). The answer only grows by declaration, so the walk carries
7185/// it. A field's short name names its owner chain, `Owner.member`, so one field
7186/// answers for every dotted prefix of its own short name; an anonymous enum's
7187/// or union's enumerators carry bare short names instead (#2140), which is what
7188/// an empty owner short name asks about.
7189#[derive(Debug, Default)]
7190pub struct CppFieldOwnerIndex {
7191    /// Package name -> the owner short names its fields name.
7192    owners: HashMap<String, HashSet<String>>,
7193    /// Packages holding at least one field that names no owner.
7194    ownerless_packages: HashSet<String>,
7195}
7196
7197impl CppFieldOwnerIndex {
7198    /// The index of the declarations recorded so far, built when the first
7199    /// question arrives.
7200    fn of<'unit>(
7201        declarations: impl IntoIterator<Item = &'unit CodeUnit>,
7202        file: &ProjectFile,
7203    ) -> Self {
7204        let mut index = Self::default();
7205        for declaration in declarations {
7206            index.record(declaration, file);
7207        }
7208        index
7209    }
7210
7211    fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
7212        if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
7213            return;
7214        }
7215        let short_name = code_unit.short_name();
7216        let package_name = code_unit.package_name();
7217        if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
7218            self.ownerless_packages.insert(package_name.to_string());
7219        }
7220        if !short_name.contains('.') {
7221            return;
7222        }
7223        if !self.owners.contains_key(package_name) {
7224            self.owners
7225                .insert(package_name.to_string(), HashSet::default());
7226        }
7227        let owners = self
7228            .owners
7229            .get_mut(package_name)
7230            .expect("the package entry was just ensured");
7231        for (offset, _) in short_name.match_indices('.') {
7232            let owner = &short_name[..offset];
7233            if !owners.contains(owner) {
7234                owners.insert(owner.to_string());
7235            }
7236        }
7237    }
7238
7239    /// Whether a field declaration in `package_name` names `owner_short_name`
7240    /// as its owner. An empty owner asks about ownerless fields instead.
7241    fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
7242        if owner_short_name.is_empty() {
7243            self.ownerless_packages.contains(package_name)
7244        } else {
7245            self.owners
7246                .get(package_name)
7247                .is_some_and(|owners| owners.contains(owner_short_name))
7248        }
7249    }
7250}
7251
7252/// The declaration scan [`CppFieldOwnerIndex`] replaces, kept as the oracle a
7253/// debug build asserts every carried answer against and as the release-mode
7254/// parity tests' reference (#2786).
7255#[cfg(any(debug_assertions, test))]
7256fn cpp_declarations_hold_owned_fields<'unit>(
7257    declarations: impl IntoIterator<Item = &'unit CodeUnit>,
7258    file: &ProjectFile,
7259    package_name: &str,
7260    owner_short_name: &str,
7261) -> bool {
7262    let prefix = format!("{owner_short_name}.");
7263    declarations.into_iter().any(|unit| {
7264        unit.kind() == CodeUnitType::Field
7265            && unit.source() == file
7266            && unit.package_name() == package_name
7267            && if owner_short_name.is_empty() {
7268                // Anonymous enum/union parent: its enumerators carry bare
7269                // short names (#2140), so presence means any ownerless
7270                // field in this file.
7271                !unit.short_name().contains(['.', '$'])
7272            } else {
7273                unit.short_name().starts_with(&prefix)
7274            }
7275    })
7276}
7277
7278/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
7279///
7280/// A region reparse is its own tree and is dropped while the walk that made it
7281/// continues, so a later parse can be allocated at the same address and hand out
7282/// the same node ids.  The root's span and shape pin the identity its address
7283/// alone does not: two roots agreeing on all of this are the same parse of the
7284/// same bytes, and a scan of one is a scan of the other.
7285#[derive(PartialEq, Eq, Hash)]
7286pub struct CppTreeIdentity {
7287    root_id: usize,
7288    start_byte: usize,
7289    end_byte: usize,
7290    kind_id: u16,
7291    child_count: usize,
7292}
7293
7294impl CppTreeIdentity {
7295    fn of(root: Node<'_>) -> Self {
7296        Self {
7297            root_id: root.id(),
7298            start_byte: root.start_byte(),
7299            end_byte: root.end_byte(),
7300            kind_id: root.kind_id(),
7301            child_count: root.child_count(),
7302        }
7303    }
7304}
7305
7306/// The namespace forward declarations one tree's prefix holds, folded in as the
7307/// walk asks about them.
7308///
7309/// `scope_for_recovered_exported_class` asks the same question once per
7310/// recovered class, and the answer depends only on the part of the tree that
7311/// starts before the asking node.  Rescanning that prefix per question is
7312/// quadratic in the file, and a generated header whose parse recovery leaves
7313/// thousands of class-like declarations at file scope pays all of it: 1,904
7314/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
7315/// (#2754).  This carries the scan forward instead.  Each question advances the
7316/// traversal from wherever the last one stopped to the asking node's start byte,
7317/// so a whole walk pays at most one pass over the prefix its furthest question
7318/// reaches, and each question then costs a name lookup.
7319#[derive(Default)]
7320pub struct CppNamespaceForwardScan {
7321    /// Every named node starting before this byte has been folded in.
7322    scanned_through: usize,
7323    forwards: HashMap<String, Vec<CppNamespaceForward>>,
7324}
7325
7326impl CppNamespaceForwardScan {
7327    /// Fold in every named node of `root` that starts at or after the fold
7328    /// watermark and before `cutoff`.
7329    ///
7330    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
7331    /// pass owes are exactly the ones no earlier pass reached, and a question
7332    /// about an earlier byte than one already answered costs nothing.
7333    fn advance_to<'tree>(
7334        &mut self,
7335        root: Node<'tree>,
7336        cutoff: usize,
7337        source: &str,
7338        ancestry: &ParentIndex<'tree>,
7339    ) {
7340        if cutoff <= self.scanned_through {
7341            return;
7342        }
7343        let folded_through = self.scanned_through;
7344        let mut cursor = root.walk();
7345        let mut stack = vec![root];
7346        while let Some(current) = stack.pop() {
7347            if (folded_through..cutoff).contains(&current.start_byte())
7348                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
7349            {
7350                self.forwards
7351                    .entry(forward.name.clone())
7352                    .or_default()
7353                    .push(forward);
7354            }
7355            // A subtree ending before the watermark holds only nodes an earlier
7356            // pass already folded, and one starting at or after the cutoff is
7357            // outside the prefix being asked about. Skipping both is what keeps
7358            // the total traversal to one pass.
7359            for child in current.named_children(&mut cursor) {
7360                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
7361                    stack.push(child);
7362                }
7363            }
7364        }
7365        self.scanned_through = cutoff;
7366    }
7367
7368    /// The one namespace `name` was forward declared in before `recovered_node`.
7369    /// More than one matching forward declaration is ambiguous and answers
7370    /// nothing rather than guessing.
7371    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
7372        let mut matching = self
7373            .forwards
7374            .get(name)
7375            .into_iter()
7376            .flatten()
7377            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
7378        let first = matching.next()?;
7379        matching
7380            .next()
7381            .is_none()
7382            .then(|| first.package_name.clone())
7383    }
7384}
7385
7386/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
7387/// debug build checks every answer against (and the one the parity tests drive
7388/// directly).  It walks the whole prefix per question, which is exactly the cost
7389/// #2754 removed from the release path.
7390#[cfg(any(debug_assertions, test))]
7391fn unique_earlier_cpp_namespace_forward<'tree>(
7392    recovered_node: Node<'tree>,
7393    name: &str,
7394    source: &str,
7395    ancestry: &ParentIndex<'tree>,
7396) -> Option<String> {
7397    let mut root = recovered_node;
7398    while let Some(parent) = ancestry.parent(root) {
7399        root = parent;
7400    }
7401
7402    let mut candidates = Vec::new();
7403    let mut stack = vec![root];
7404    while let Some(current) = stack.pop() {
7405        if current.start_byte() < recovered_node.start_byte()
7406            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
7407            && forward.name == name
7408            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
7409        {
7410            candidates.push(forward.package_name);
7411        }
7412
7413        let mut cursor = current.walk();
7414        for child in current.named_children(&mut cursor) {
7415            if child.start_byte() < recovered_node.start_byte() {
7416                stack.push(child);
7417            }
7418        }
7419    }
7420
7421    if candidates.len() == 1 {
7422        candidates.pop()
7423    } else {
7424        None
7425    }
7426}
7427
7428fn malformed_namespace_is_nearest_recovery_region(
7429    namespace_end_byte: usize,
7430    recovered_node: Node<'_>,
7431) -> bool {
7432    let mut root = recovered_node;
7433    while let Some(parent) = root.parent() {
7434        root = parent;
7435    }
7436    let mut cursor = root.walk();
7437    root.named_children(&mut cursor)
7438        .filter(|sibling| {
7439            namespace_end_byte <= sibling.start_byte()
7440                && sibling.end_byte() <= recovered_node.start_byte()
7441        })
7442        .all(is_malformed_namespace_recovery_trivia)
7443}
7444
7445fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
7446    matches!(node.kind(), "ERROR" | "comment")
7447        || node.kind().starts_with("preproc_")
7448        || node.kind() == "expression_statement" && node.named_child_count() == 0
7449}
7450
7451/// Return the namespace path for a forward class only when the declaration is
7452/// at namespace scope.  A declaration nested in a function/class body may share
7453/// the same namespace ancestor but cannot identify a top-level class definition.
7454fn cpp_namespace_name_for_forward<'tree>(
7455    node: Node<'tree>,
7456    source: &str,
7457    ancestry: &ParentIndex<'tree>,
7458) -> Option<String> {
7459    cpp_namespace_definition_for_forward(node, ancestry)?;
7460    cpp_lexical_namespace_name(node, source, ancestry)
7461}
7462
7463fn cpp_namespace_definition_for_forward<'tree>(
7464    node: Node<'tree>,
7465    ancestry: &ParentIndex<'tree>,
7466) -> Option<Node<'tree>> {
7467    let declaration = ancestry.parent(node)?;
7468    let mut ancestor = ancestry.parent(declaration);
7469    while let Some(current) = ancestor {
7470        if matches!(
7471            current.kind(),
7472            "compound_statement"
7473                | "field_declaration_list"
7474                | "class_specifier"
7475                | "struct_specifier"
7476                | "union_specifier"
7477                | "function_definition"
7478                | "lambda_expression"
7479        ) {
7480            return None;
7481        }
7482        if current.kind() == "namespace_definition" {
7483            return Some(current);
7484        }
7485        ancestor = ancestry.parent(current);
7486    }
7487    None
7488}
7489
7490fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
7491    match node.kind() {
7492        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
7493        "parenthesized_declarator" => node
7494            .child_by_field_name("declarator")
7495            .or_else(|| last_named_child(node))
7496            .is_some_and(is_pointer_wrapper_declarator),
7497        "template_function" => node
7498            .child_by_field_name("name")
7499            .is_some_and(is_function_pointer_like_inner_declarator),
7500        _ => false,
7501    }
7502}
7503
7504fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
7505    match node.kind() {
7506        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
7507        "parenthesized_declarator" => node
7508            .child_by_field_name("declarator")
7509            .or_else(|| last_named_child(node))
7510            .is_some_and(is_pointer_wrapper_declarator),
7511        _ => false,
7512    }
7513}
7514
7515fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
7516    let cleaned = raw_name.trim_start_matches("template ").trim();
7517    // A leading `::` is the explicit-global marker, not an empty owner segment.
7518    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
7519    // erroneous macro envelope swallowing the first identifier of an
7520    // out-of-line `X::X` constructor, chromium #1573); without this strip the
7521    // split below yields owner_parts `[""]`, constructing a unit with an empty
7522    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
7523    let cleaned = cleaned.trim_start_matches("::");
7524    // Parser recovery can preserve two adjacent scope operators around a
7525    // missing component (for example `X::/**/::method` in compiler diagnostic
7526    // fixtures). Empty components are syntax-recovery artifacts, never C++
7527    // owners. Keeping one as the final owner constructed `short_name=".method"`
7528    // and violated the structured package/short boundary during a large LLVM
7529    // workspace build. This is the same legacy-string-to-FqName bridge as the
7530    // ordinary split above; discard only components that the delimiter itself
7531    // proves empty.
7532    let parts: Vec<_> = cleaned
7533        .split("::")
7534        .filter(|component| !component.is_empty())
7535        .collect();
7536    if parts.is_empty() {
7537        return (None, cleaned.to_string(), scope.package_name.clone());
7538    }
7539    if parts.len() > 1 {
7540        let name = parts.last().unwrap_or(&cleaned).to_string();
7541        let owner_parts = &parts[..parts.len() - 1];
7542        if let Some(class_unit) = &scope.class_unit {
7543            // Lexically inside a class body: the owner is that class, whatever
7544            // the declarator re-qualifies it as.
7545            return (
7546                Some(CppMemberOwner::Unit(class_unit.clone())),
7547                name,
7548                scope.package_name.clone(),
7549            );
7550        }
7551        if !scope.package_name.is_empty() {
7552            // Out-of-line member definition written *inside* an enclosing
7553            // `namespace {}` block (scope package is that namespace). Every
7554            // owner segment before the terminal member is a class-nesting step
7555            // -- an out-of-line nested-class member `Outer::Inner::method` in
7556            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
7557            // namespace path: `using namespace` never brings nested-class
7558            // access into unqualified scope, so C++ always writes the full
7559            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
7560            // that redundantly re-states the enclosing namespace it already
7561            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
7562            // strip that re-qualifying prefix (which duplicates a suffix of the
7563            // enclosing package path) before treating what remains as the
7564            // nested-class chain, so the redundant spelling still lands on the
7565            // same `log4cxx.Foo.method` identity as its header declaration.
7566            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
7567            let owner = (!nested.is_empty()).then(|| {
7568                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
7569            });
7570            return (owner, name, scope.package_name.clone());
7571        }
7572        // File scope (no enclosing `namespace {}` block, scope package empty).
7573        let (owner, package_name) = if owner_parts.len() > 1 {
7574            // A multi-segment qualifier at file scope with no enclosing
7575            // namespace: treat all but the last owner segment as the namespace
7576            // path and the last as the owning class (`ns1::ns2::Class::method`
7577            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
7578            // is really a namespace or an outer class cannot be told from the
7579            // declarator text alone here, and no enclosing namespace or
7580            // in-index owner is available at per-file extraction to confirm the
7581            // class reading, so the far-more-common namespace interpretation is
7582            // kept rather than guessed away (the nested-class-at-file-scope and
7583            // using-directive-qualified nested-class shapes remain on this
7584            // behavior; see #1121).
7585            (
7586                Some(CppMemberOwner::Chain(vec![
7587                    owner_parts.last().unwrap_or(&"").to_string(),
7588                ])),
7589                owner_parts[..owner_parts.len() - 1].join("::"),
7590            )
7591        } else {
7592            // A bare `Class::member` qualifier at file scope carries no
7593            // namespace segment of its own. The declarator alone cannot say
7594            // which namespace owns `Class` -- but a `using namespace X;`
7595            // directive already in effect at this point in the file (#1093,
7596            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
7597            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
7598            // is the remaining structural signal for it, so fall back to it
7599            // rather than leaving the definition's package empty while its
7600            // header declaration (parsed inside the `namespace {}` block) keeps
7601            // the real one -- an identity split that made the same member
7602            // unresolvable under its own displayed spelling.
7603            (
7604                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
7605                cpp_using_directive_namespace_for_bare_owner(scope),
7606            )
7607        };
7608        return (owner, name, package_name);
7609    }
7610
7611    let package_name = scope.package_name.clone();
7612    let owner = scope
7613        .class_unit
7614        .as_ref()
7615        .map(|parent| CppMemberOwner::Unit(parent.clone()));
7616    (owner, cleaned.to_string(), package_name)
7617}
7618
7619/// Drop the leading owner segments of an out-of-line member qualifier that
7620/// merely re-state the enclosing namespace the definition already sits in, so
7621/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
7622/// definition may redundantly write `a::b::Outer::Inner::method` (or the
7623/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
7624/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
7625/// noise, not class-nesting steps. Returns the owner segments with the longest
7626/// such re-qualifying prefix removed (possibly all of them, when the qualifier
7627/// names only the enclosing namespace before the terminal member -- a
7628/// re-qualified free function). `package_name` is the enclosing namespace path
7629/// in its stored `::`-joined form; both sides are split on the same delimiter
7630/// the namespace walker joined them with, so this compares namespace *segments*
7631/// rather than scanning text.
7632fn strip_redundant_namespace_prefix<'a>(
7633    owner_parts: &'a [&'a str],
7634    package_name: &str,
7635) -> &'a [&'a str] {
7636    if package_name.is_empty() {
7637        return owner_parts;
7638    }
7639    let package_segments: Vec<&str> = package_name.split("::").collect();
7640    let max_prefix = owner_parts.len().min(package_segments.len());
7641    for prefix_len in (1..=max_prefix).rev() {
7642        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
7643        if &owner_parts[..prefix_len] == package_suffix {
7644            return &owner_parts[prefix_len..];
7645        }
7646    }
7647    owner_parts
7648}
7649
7650/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
7651/// class name at file/namespace scope, from the `using namespace` directives
7652/// visible at this point in the file. Several may be in scope at once (a
7653/// primary `using namespace NS;` alongside deeper conveniences like `using
7654/// namespace NS::helpers;`); since the declarator gives no way to tell which
7655/// one actually declares the owner class, prefer the shallowest (fewest
7656/// `::`-separated segments) as the file's most likely "home" namespace,
7657/// breaking ties by declaration order. Returns an empty string (leaving the
7658/// caller's package unqualified, as before) when no using-namespace directive
7659/// is in scope.
7660fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
7661    scope
7662        .visible_using_namespaces
7663        .iter()
7664        .min_by_key(|namespace| namespace.split("::").count())
7665        .cloned()
7666        .unwrap_or_default()
7667}
7668
7669struct CppQualifiedNameComponent {
7670    name: String,
7671    is_template_id: bool,
7672}
7673
7674/// Canonical nested-class chain for an out-of-line class definition written
7675/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
7676/// component per class (`["Outer", "Inner"]`).
7677///
7678/// The enclosing namespace fixes the namespace/class boundary: after an
7679/// optional redundant spelling of that namespace, every component belongs to
7680/// the class chain. File-scope qualified class names remain untouched because
7681/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
7682///
7683/// The components stay structured (rather than being `$`-joined here) so the
7684/// fq construction can push one Type/Nested segment per class; the `$`-joined
7685/// short-name display form is derived at the call sites that need it.
7686fn qualified_class_name_chain(
7687    class_node: Node<'_>,
7688    source: &str,
7689    scope: &ScopeInfo,
7690) -> Option<Vec<String>> {
7691    if scope.package_name.is_empty() || scope.class_unit.is_some() {
7692        return None;
7693    }
7694    let name = class_node.child_by_field_name("name")?;
7695    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
7696    if explicitly_global
7697        || components.len() < 2
7698        || components.iter().any(|component| component.is_template_id)
7699    {
7700        return None;
7701    }
7702    let names = components
7703        .iter()
7704        .map(|component| component.name.as_str())
7705        .collect::<Vec<_>>();
7706    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
7707    if class_chain.is_empty() {
7708        return None;
7709    }
7710    Some(class_chain.iter().map(|name| name.to_string()).collect())
7711}
7712
7713fn structured_cpp_qualified_components(
7714    qualified_name: Node<'_>,
7715    source: &str,
7716) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
7717    if qualified_name.kind() != "qualified_identifier" {
7718        return None;
7719    }
7720
7721    let mut components = Vec::new();
7722    let mut current = qualified_name;
7723    let mut explicitly_global = false;
7724    loop {
7725        if current.kind() == "qualified_identifier" {
7726            if let Some(component) = current.child_by_field_name("scope") {
7727                components.push(canonical_cpp_qualified_component(component, source)?);
7728            } else if components.is_empty() {
7729                explicitly_global = true;
7730            } else {
7731                return None;
7732            }
7733            current = current.child_by_field_name("name")?;
7734        } else {
7735            components.push(canonical_cpp_qualified_component(current, source)?);
7736            break;
7737        }
7738    }
7739    Some((components, explicitly_global))
7740}
7741
7742fn split_structured_templated_cpp_name(
7743    declarator_name: Node<'_>,
7744    source: &str,
7745    scope: &ScopeInfo,
7746) -> Option<(Option<CppMemberOwner>, String, String)> {
7747    let (mut components, explicitly_global) =
7748        structured_cpp_qualified_components(declarator_name, source)?;
7749
7750    let terminal = components.pop()?;
7751    let owner_start = components
7752        .iter()
7753        .position(|component| component.is_template_id)?;
7754    let explicit_package = components[..owner_start]
7755        .iter()
7756        .map(|component| component.name.as_str())
7757        .collect::<Vec<_>>()
7758        .join("::");
7759    let explicit_package_is_empty = explicit_package.is_empty();
7760    let package_name = match (
7761        explicitly_global,
7762        scope.package_name.is_empty(),
7763        explicit_package_is_empty,
7764    ) {
7765        (true, _, _) => explicit_package,
7766        (false, _, true) => scope.package_name.clone(),
7767        (false, true, false) => explicit_package,
7768        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
7769    };
7770    // Same identity-split fallback as `split_cpp_name` (#1093): a template
7771    // specialization's owner class named with no namespace segment of its own
7772    // (`explicit_package` empty) at file scope (`explicitly_global` false)
7773    // with nothing enclosing (`package_name` still empty) has no structural
7774    // signal for its namespace besides an in-scope `using namespace X;`.
7775    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
7776    {
7777        cpp_using_directive_namespace_for_bare_owner(scope)
7778    } else {
7779        package_name
7780    };
7781    let owner_chain = components[owner_start..]
7782        .iter()
7783        .map(|component| component.name.clone())
7784        .collect::<Vec<_>>();
7785    if owner_chain.is_empty() || terminal.name.is_empty() {
7786        return None;
7787    }
7788
7789    Some((
7790        Some(CppMemberOwner::Chain(owner_chain)),
7791        terminal.name,
7792        package_name,
7793    ))
7794}
7795
7796fn canonical_cpp_qualified_component(
7797    mut component: Node<'_>,
7798    source: &str,
7799) -> Option<CppQualifiedNameComponent> {
7800    let mut is_template_id = false;
7801    loop {
7802        match component.kind() {
7803            "template_type" => {
7804                is_template_id = true;
7805                component = component.child_by_field_name("name")?;
7806            }
7807            "dependent_name" => component = component.named_child(0)?,
7808            "identifier"
7809            | "field_identifier"
7810            | "namespace_identifier"
7811            | "type_identifier"
7812            | "operator_name"
7813            | "destructor_name" => {
7814                let name = normalize_cpp_whitespace(node_text(component, source));
7815                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
7816                    name,
7817                    is_template_id,
7818                });
7819            }
7820            _ => component = component.child_by_field_name("name")?,
7821        }
7822    }
7823}
7824
7825fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
7826    if let Some(name) = macro_decorated_unqualified_name(node) {
7827        return extract_declarator_name(name, source);
7828    }
7829    match node.kind() {
7830        "identifier"
7831        | "field_identifier"
7832        | "type_identifier"
7833        | "operator_name"
7834        | "destructor_name"
7835        | "qualified_identifier" => node_text(node, source).to_string(),
7836        "function_declarator"
7837        | "pointer_declarator"
7838        | "reference_declarator"
7839        | "parenthesized_declarator"
7840        | "array_declarator"
7841        | "template_function" => node
7842            .child_by_field_name("declarator")
7843            .or_else(|| node.child_by_field_name("name"))
7844            .or_else(|| last_named_child(node))
7845            .map(|child| extract_declarator_name(child, source))
7846            .unwrap_or_else(|| node_text(node, source).to_string()),
7847        _ => node
7848            .child_by_field_name("name")
7849            .map(|child| extract_declarator_name(child, source))
7850            .unwrap_or_else(|| node_text(node, source).to_string()),
7851    }
7852}
7853
7854/// Extract a callable identity only through declaration-shaped AST nodes.
7855/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
7856/// expose the call's parameter list as a false function declarator; accepting
7857/// arbitrary node text there emitted bogus names such as `.*f`.
7858fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
7859    if let Some(name) = macro_decorated_unqualified_name(node) {
7860        return extract_callable_declarator_name(name, source);
7861    }
7862    match node.kind() {
7863        "identifier"
7864        | "field_identifier"
7865        | "type_identifier"
7866        | "operator_name"
7867        | "destructor_name"
7868        | "qualified_identifier" => Some(node_text(node, source).to_string()),
7869        "function_declarator"
7870        | "pointer_declarator"
7871        | "reference_declarator"
7872        | "parenthesized_declarator"
7873        | "array_declarator"
7874        | "template_function" => node
7875            .child_by_field_name("declarator")
7876            .or_else(|| node.child_by_field_name("name"))
7877            .and_then(|child| extract_callable_declarator_name(child, source)),
7878        _ => None,
7879    }
7880}
7881
7882fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
7883    match node.kind() {
7884        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
7885            let name = node_text(node, source).trim().to_string();
7886            (!name.is_empty()).then_some(name)
7887        }
7888        _ => node
7889            .child_by_field_name("declarator")
7890            .or_else(|| node.child_by_field_name("name"))
7891            .or_else(|| last_named_child(node))
7892            .and_then(|child| extract_variable_name(child, source)),
7893    }
7894}
7895
7896fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
7897    let count = node.named_child_count();
7898    if count == 0 {
7899        None
7900    } else {
7901        node.named_child(count - 1)
7902    }
7903}
7904
7905fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
7906    let name_node = node.child_by_field_name("name")?;
7907    let name = normalize_cpp_whitespace(node_text(name_node, source));
7908    (!name.is_empty()).then_some(name)
7909}
7910
7911fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
7912    if node.kind() != "declaration" {
7913        return Vec::new();
7914    }
7915    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
7916        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
7917    }) else {
7918        return Vec::new();
7919    };
7920    let Some(declarator) = node.child_by_field_name("declarator") else {
7921        return Vec::new();
7922    };
7923    if node_text(keyword, source) == "using"
7924        && (declarator.kind() != "init_declarator"
7925            || declarator.child_by_field_name("value").is_none())
7926    {
7927        return Vec::new();
7928    }
7929    if node_text(keyword, source) == "typedef"
7930        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
7931    {
7932        return vec![alias_name];
7933    }
7934    extract_typedef_declarator_name(declarator, source)
7935        .into_iter()
7936        .collect()
7937}
7938
7939fn recovered_typedef_error_alias_name(
7940    declaration: Node<'_>,
7941    declarator: Node<'_>,
7942    source: &str,
7943) -> Option<String> {
7944    // An export macro between `class` and its name can make tree-sitter parse
7945    // the recovered class body as a function body. In that shape,
7946    //
7947    //     typedef spi::Filter BASE_CLASS;
7948    //
7949    // becomes a declaration whose `declarator` is the underlying qualified
7950    // type (`spi::Filter`) and whose actual alias name is displaced into the
7951    // following ERROR node. Do not publish the terminal underlying type
7952    // (`Filter`) as a false class-owned alias.
7953    if declarator.kind() != "qualified_identifier" {
7954        return None;
7955    }
7956    let mut cursor = declaration.walk();
7957    let mut errors = declaration
7958        .named_children(&mut cursor)
7959        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
7960    let error = errors.next()?;
7961    if errors.next().is_some() || error.named_child_count() != 1 {
7962        return None;
7963    }
7964    let name = error.named_child(0)?;
7965    if !matches!(
7966        name.kind(),
7967        "identifier" | "field_identifier" | "type_identifier"
7968    ) {
7969        return None;
7970    }
7971    let name = normalize_cpp_whitespace(node_text(name, source));
7972    (!name.is_empty()).then_some(name)
7973}
7974
7975fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
7976    // A function-like token in the type position can make tree-sitter expose
7977    // its argument as a parenthesized declarator. Do not publish that argument
7978    // as an alias. The macro-specific recovery below handles the proven shape.
7979    if fragmented_parenthesized_typedef_type(node).is_some() {
7980        return Vec::new();
7981    }
7982    let has_function_like_macro_type = node
7983        .child_by_field_name("type")
7984        .filter(|type_node| type_node.kind() == "type_identifier")
7985        .is_some_and(|type_node| {
7986            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
7987        });
7988    let mut names = Vec::new();
7989    let mut cursor = node.walk();
7990    for declarator in node.children_by_field_name("declarator", &mut cursor) {
7991        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
7992            continue;
7993        }
7994        if let Some(name) = extract_typedef_declarator_name(declarator, source)
7995            && !names.contains(&name)
7996        {
7997            names.push(name);
7998        }
7999    }
8000    names
8001}
8002
8003struct RecoveredMacroTypedefAlias<'tree> {
8004    name: String,
8005    end_node: Node<'tree>,
8006}
8007
8008/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
8009/// into an identifier expression statement. The uppercase macro token, missing
8010/// typedef terminator, and complete sibling terminator prove this exact shape.
8011fn recovered_macro_typedef_alias<'tree>(
8012    node: Node<'tree>,
8013    source: &str,
8014) -> Option<RecoveredMacroTypedefAlias<'tree>> {
8015    let type_node = fragmented_parenthesized_typedef_type(node)?;
8016    if type_node.kind() != "type_identifier"
8017        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
8018    {
8019        return None;
8020    }
8021
8022    let end_node = node.next_named_sibling()?;
8023    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
8024        return None;
8025    }
8026    let name_node = end_node.named_child(0)?;
8027    if name_node.kind() != "identifier" {
8028        return None;
8029    }
8030    let has_terminator = (0..end_node.child_count()).any(|index| {
8031        end_node
8032            .child(index)
8033            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
8034    });
8035    if !has_terminator {
8036        return None;
8037    }
8038    let name = normalize_cpp_whitespace(node_text(name_node, source));
8039    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
8040}
8041
8042fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
8043    if node.kind() != "type_definition" {
8044        return None;
8045    }
8046    let mut declarator_cursor = node.walk();
8047    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
8048    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
8049        return None;
8050    }
8051    let has_missing_terminator = (0..node.child_count()).any(|index| {
8052        node.child(index)
8053            .is_some_and(|child| child.kind() == ";" && child.is_missing())
8054    });
8055    if !has_missing_terminator {
8056        return None;
8057    }
8058    node.child_by_field_name("type")
8059}
8060
8061fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
8062    match node.kind() {
8063        "identifier" | "field_identifier" | "type_identifier" => {
8064            let name = normalize_cpp_whitespace(node_text(node, source));
8065            (!name.is_empty()).then_some(name)
8066        }
8067        "qualified_identifier" => node
8068            .child_by_field_name("name")
8069            .and_then(|name| extract_typedef_declarator_name(name, source)),
8070        _ => node
8071            .child_by_field_name("declarator")
8072            .or_else(|| node.child_by_field_name("name"))
8073            .or_else(|| last_named_child(node))
8074            .and_then(|child| extract_typedef_declarator_name(child, source)),
8075    }
8076}
8077
8078fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
8079    let name = node
8080        .child_by_field_name("name")
8081        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
8082        .or_else(|| {
8083            let mut cursor = node.walk();
8084            node.named_children(&mut cursor)
8085                .find(|child| {
8086                    matches!(
8087                        child.kind(),
8088                        "identifier" | "field_identifier" | "type_identifier"
8089                    )
8090                })
8091                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
8092        })?;
8093    (!name.is_empty()).then_some(name)
8094}
8095
8096fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
8097    left.id() == right.id()
8098}
8099
8100fn render_cpp_type_signature(
8101    node: Node<'_>,
8102    source: &str,
8103    template_signature: Option<&str>,
8104) -> String {
8105    let text = normalize_cpp_whitespace(node_text(node, source));
8106    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
8107    let rendered = if head.ends_with(';') {
8108        head.to_string()
8109    } else {
8110        format!("{head} {{")
8111    };
8112    if let Some(template_signature) = template_signature {
8113        format!("template {template_signature} {rendered}")
8114    } else {
8115        rendered
8116    }
8117}
8118
8119fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
8120    if let Some(signature) =
8121        render_recovered_macro_qualified_field_signature(node, declarator, source)
8122    {
8123        return signature;
8124    }
8125    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
8126    let prefix = cpp_declaration_prefix(node, source);
8127    let name = extract_variable_name(declarator, source).unwrap_or_default();
8128    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
8129    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
8130        || (prefix.ends_with('&') && raw_suffix == "&")
8131    {
8132        String::new()
8133    } else {
8134        raw_suffix
8135    };
8136
8137    let mut rendered = if suffix.is_empty() {
8138        format!("{prefix} {name}")
8139    } else if suffix.starts_with('*') || suffix.starts_with('&') {
8140        format!("{prefix}{suffix} {name}")
8141    } else if suffix.starts_with('[') || suffix.starts_with('(') {
8142        format!("{prefix} {name}{suffix}")
8143    } else {
8144        format!("{prefix} {suffix}{name}")
8145    };
8146    rendered = collapse_cpp_whitespace(&rendered);
8147
8148    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
8149        format!("{rendered} = {initializer};")
8150    } else if declaration_text.ends_with(';') {
8151        format!("{rendered};")
8152    } else {
8153        rendered
8154    }
8155}
8156
8157fn render_recovered_macro_qualified_field_signature(
8158    node: Node<'_>,
8159    declarator: Node<'_>,
8160    source: &str,
8161) -> Option<String> {
8162    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
8163    if !recovered
8164        .iter()
8165        .any(|candidate| same_node(*candidate, declarator))
8166    {
8167        return None;
8168    }
8169    let pseudo_declarator = node.child_by_field_name("declarator")?;
8170    let mut cursor = node.walk();
8171    let clause = node
8172        .named_children(&mut cursor)
8173        .find(|child| child.kind() == "bitfield_clause")?;
8174    let mut cursor = clause.walk();
8175    let error = clause
8176        .named_children(&mut cursor)
8177        .find(|child| child.kind() == "ERROR")?;
8178    let qualified_type =
8179        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
8180    let prefix = cpp_declaration_prefix(node, source);
8181    let name = extract_variable_name(declarator, source)?;
8182    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
8183    let mut rendered = if suffix.is_empty() {
8184        format!("{prefix} {qualified_type} {name}")
8185    } else {
8186        format!("{prefix} {qualified_type} {suffix} {name}")
8187    };
8188    rendered = collapse_cpp_whitespace(&rendered);
8189
8190    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
8191        Some(format!(
8192            "{rendered} = {};",
8193            normalize_cpp_whitespace(node_text(initializer, source))
8194        ))
8195    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
8196        Some(format!("{rendered} = {initializer};"))
8197    } else {
8198        Some(format!("{rendered};"))
8199    }
8200}
8201
8202fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
8203    match node.kind() {
8204        "pointer_expression" => {
8205            let operator = node
8206                .child_by_field_name("operator")
8207                .or_else(|| node.child(0))
8208                .map(|operator| node_text(operator, source))
8209                .unwrap_or("*");
8210            let argument = node
8211                .child_by_field_name("argument")
8212                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
8213                .unwrap_or_default();
8214            format!("{operator}{argument}")
8215        }
8216        "unary_expression" => {
8217            let operator = node
8218                .child_by_field_name("operator")
8219                .or_else(|| node.child(0))
8220                .map(|operator| node_text(operator, source))
8221                .unwrap_or_default();
8222            let argument = node
8223                .child_by_field_name("argument")
8224                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
8225                .unwrap_or_default();
8226            format!("{operator}{argument}")
8227        }
8228        "identifier" | "field_identifier" => String::new(),
8229        _ => cpp_declarator_suffix_without_name(node, source),
8230    }
8231}
8232
8233fn recovered_macro_qualified_field_initializer<'tree>(
8234    clause: Node<'tree>,
8235    declarator: Node<'tree>,
8236) -> Option<Node<'tree>> {
8237    let mut stack = vec![clause];
8238    while let Some(current) = stack.pop() {
8239        if current.kind() == "assignment_expression"
8240            && current
8241                .child_by_field_name("left")
8242                .is_some_and(|left| same_node(left, declarator))
8243        {
8244            return current.child_by_field_name("right");
8245        }
8246        let mut cursor = current.walk();
8247        stack.extend(current.named_children(&mut cursor));
8248    }
8249    None
8250}
8251
8252fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
8253    let text = node_text(node, source);
8254    let mut cursor = node.walk();
8255    let first_declarator = node.named_children(&mut cursor).find(|child| {
8256        matches!(
8257            child.kind(),
8258            "init_declarator"
8259                | "identifier"
8260                | "field_identifier"
8261                | "pointer_declarator"
8262                | "reference_declarator"
8263                | "array_declarator"
8264                | "function_declarator"
8265        )
8266    });
8267    let prefix = if let Some(first_declarator) = first_declarator {
8268        let end = first_declarator
8269            .start_byte()
8270            .saturating_sub(node.start_byte());
8271        let mut prefix = text.get(..end).unwrap_or(text).to_string();
8272        let declarator_suffix = match first_declarator.kind() {
8273            "init_declarator" => first_declarator
8274                .child_by_field_name("declarator")
8275                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
8276                .unwrap_or_default(),
8277            _ => cpp_declarator_suffix_without_name(first_declarator, source),
8278        };
8279        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
8280            prefix.push_str(&declarator_suffix);
8281        }
8282        return collapse_cpp_whitespace(&prefix)
8283            .trim_end_matches(',')
8284            .trim_end_matches(';')
8285            .trim()
8286            .to_string();
8287    } else {
8288        text
8289    };
8290    collapse_cpp_whitespace(prefix)
8291        .trim_end_matches(',')
8292        .trim_end_matches(';')
8293        .trim()
8294        .to_string()
8295}
8296
8297fn cpp_preserved_initializer(
8298    declaration_node: Node<'_>,
8299    declarator: Node<'_>,
8300    source: &str,
8301) -> Option<String> {
8302    let name = extract_variable_name(declarator, source)?;
8303    let mut cursor = declaration_node.walk();
8304    for child in declaration_node.named_children(&mut cursor) {
8305        if child.kind() != "init_declarator" {
8306            continue;
8307        }
8308        let Some(inner) = child.child_by_field_name("declarator") else {
8309            continue;
8310        };
8311        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
8312            continue;
8313        }
8314        let value = child.child_by_field_name("value")?;
8315        let kind = value.kind();
8316        if matches!(
8317            kind,
8318            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
8319        ) {
8320            return Some(normalize_cpp_whitespace(node_text(value, source)));
8321        }
8322        break;
8323    }
8324    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
8325    let pattern = format!(
8326        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
8327        regex::escape(&name)
8328    );
8329    Regex::new(&pattern)
8330        .ok()
8331        .and_then(|regex| regex.captures(&declaration_text))
8332        .and_then(|captures| captures.get(1))
8333        .map(|value| value.as_str().to_string())
8334}
8335
8336fn render_cpp_function_display_signature_from_node<'tree>(
8337    node: Node<'tree>,
8338    source: &str,
8339    template_signature: Option<&str>,
8340    has_body: bool,
8341    ancestry: &ParentIndex<'tree>,
8342) -> String {
8343    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
8344    let parent_text = node_text(root, source);
8345    let body_local_start = root
8346        .child_by_field_name("body")
8347        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
8348        .unwrap_or(parent_text.len());
8349    let display = parent_text
8350        .get(..body_local_start)
8351        .unwrap_or(parent_text)
8352        .trim()
8353        .trim();
8354    let display = if let Some(template_signature) = template_signature {
8355        if display.starts_with("template ") {
8356            display.to_string()
8357        } else {
8358            format!("template {template_signature} {display}")
8359        }
8360    } else {
8361        display.to_string()
8362    };
8363    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
8364    if has_body {
8365        format!("{display} {{...}}")
8366    } else {
8367        format!("{display};")
8368    }
8369}
8370
8371fn cpp_template_signature(
8372    template_node: Node<'_>,
8373    declaration_child: Node<'_>,
8374    source: &str,
8375) -> Option<String> {
8376    let text = source
8377        .get(template_node.start_byte()..declaration_child.start_byte())
8378        .unwrap_or("");
8379    let text = normalize_cpp_whitespace(text);
8380    let start = text.find('<')?;
8381    let end = text.rfind('>')?;
8382    if end < start {
8383        return None;
8384    }
8385    Some(text[start..=end].to_string())
8386}
8387
8388struct RecoveredFragmentedPartialSpecialization<'tree> {
8389    declaration_node: Node<'tree>,
8390    name: String,
8391    range: Range,
8392    prefix_members: Vec<Node<'tree>>,
8393    member_siblings: Vec<Node<'tree>>,
8394    following_declarations: Vec<Node<'tree>>,
8395}
8396
8397struct RecoveredFragmentedPreprocessorClass<'tree> {
8398    declaration_node: Node<'tree>,
8399    class_node: Node<'tree>,
8400    body: Node<'tree>,
8401    name: String,
8402    range: Range,
8403    tail_members: Vec<Node<'tree>>,
8404    member_siblings: Vec<Node<'tree>>,
8405}
8406
8407/// Recover a class whose preprocessor-fragmented parse closes at an early
8408/// member body and publishes the remaining in-class declarations as siblings
8409/// of the surrounding alternative. Primary classes are admitted only when an
8410/// earlier branch contains the matching bodyless declaration and the class
8411/// node retains the displaced `#endif`. Partial specializations instead carry
8412/// their identity structurally in the `template_type` name and template
8413/// metadata. Retain the original AST nodes and re-own only the siblings through
8414/// the displaced structural `};` terminator.
8415fn recover_fragmented_preprocessor_class<'tree>(
8416    template_node: Node<'tree>,
8417    source: &str,
8418    ancestry: &ParentIndex<'tree>,
8419) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
8420    let alternative = ancestry.parent(template_node)?;
8421    if alternative.kind() != "preproc_else" {
8422        return None;
8423    }
8424    let conditional = alternative.parent()?;
8425    if conditional.kind() != "preproc_if" {
8426        return None;
8427    }
8428    let declaration_node = template_node
8429        .named_children(&mut template_node.walk())
8430        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
8431    let class_node = declaration_node
8432        .named_children(&mut declaration_node.walk())
8433        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
8434    let body = cpp_body_node(class_node)?;
8435    if class_node.end_byte() >= declaration_node.end_byte() {
8436        return None;
8437    }
8438    let name = class_like_name(class_node, source, ancestry)?;
8439    let is_partial_specialization = class_node
8440        .child_by_field_name("name")
8441        .is_some_and(|class_name| class_name.kind() == "template_type");
8442    if is_partial_specialization {
8443        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
8444        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
8445            return None;
8446        }
8447    } else {
8448        if !class_has_displaced_preprocessor_terminator(class_node) {
8449            return None;
8450        }
8451        let matching_other_branch = conditional
8452            .named_children(&mut conditional.walk())
8453            .take_while(|child| !same_node(*child, alternative))
8454            .filter(|child| child.kind() == "template_declaration")
8455            .filter_map(first_class_like_child)
8456            .any(|candidate| {
8457                cpp_body_node(candidate).is_none()
8458                    && class_like_name(candidate, source, ancestry).as_deref()
8459                        == Some(name.as_str())
8460            });
8461        if !matching_other_branch {
8462            return None;
8463        }
8464    }
8465
8466    let mut tail_members = Vec::new();
8467    let mut saw_class = false;
8468    let mut declaration_cursor = declaration_node.walk();
8469    for child in declaration_node.named_children(&mut declaration_cursor) {
8470        if same_node(child, class_node) {
8471            saw_class = true;
8472        } else if saw_class {
8473            tail_members.push(child);
8474        }
8475    }
8476
8477    let mut member_siblings = Vec::new();
8478    let mut saw_template = false;
8479    let mut terminator = None;
8480    for index in 0..alternative.child_count() {
8481        let Some(child) = alternative.child(index) else {
8482            continue;
8483        };
8484        if same_node(child, template_node) {
8485            saw_template = true;
8486            continue;
8487        }
8488        if !saw_template {
8489            continue;
8490        }
8491        if displaced_fragmented_class_terminator(alternative, index) {
8492            terminator = alternative.child(index + 1);
8493            break;
8494        }
8495        if child.is_named() {
8496            member_siblings.push(child);
8497        }
8498    }
8499    let terminator = terminator?;
8500    Some(RecoveredFragmentedPreprocessorClass {
8501        declaration_node,
8502        class_node,
8503        body,
8504        name,
8505        range: Range {
8506            start_byte: class_node.start_byte(),
8507            end_byte: terminator.end_byte(),
8508            start_line: class_node.start_position().row + 1,
8509            end_line: terminator.end_position().row + 1,
8510        },
8511        tail_members,
8512        member_siblings,
8513    })
8514}
8515
8516fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
8517    (0..class_node.child_count()).any(|index| {
8518        class_node.child(index).is_some_and(|child| {
8519            child.kind() == "ERROR"
8520                && (0..child.child_count()).any(|error_index| {
8521                    child
8522                        .child(error_index)
8523                        .is_some_and(|token| token.kind() == "#endif")
8524                })
8525        })
8526    })
8527}
8528
8529/// The real `#endif` that tree-sitter consumed inside an error subtree.
8530///
8531/// A preprocessor directive inside a malformed array bound can cause later
8532/// declarations to remain children of the conditional. The non-missing token
8533/// still gives the exact structured boundary. Ignore nested conditionals and
8534/// select the last error-owned token. Tree-sitter can pair a later outer
8535/// `#endif` with this conditional, so the direct terminator is not necessarily
8536/// missing.
8537pub fn cpp_displaced_preprocessor_terminator<'tree>(
8538    conditional: Node<'tree>,
8539) -> Option<Node<'tree>> {
8540    if !conditional.has_error() {
8541        return None;
8542    }
8543    let has_concrete_direct_terminator = conditional
8544        .child_count()
8545        .checked_sub(1)
8546        .and_then(|index| conditional.child(index))
8547        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
8548    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
8549        // A structured alternative proves that the direct `#endif` closes
8550        // this family. An error-owned terminator inside either branch belongs
8551        // to a damaged nested conditional, not to this one.
8552        return None;
8553    }
8554    let mut displaced = None;
8555    let mut stack = (0..conditional.child_count())
8556        .filter_map(|index| conditional.child(index))
8557        .map(|child| (child, false))
8558        .collect::<Vec<_>>();
8559    while let Some((node, inside_error)) = stack.pop() {
8560        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
8561            continue;
8562        }
8563        if node.kind() == "#endif" && !node.is_missing() && inside_error {
8564            if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
8565                displaced = Some(node);
8566            }
8567            continue;
8568        }
8569        if node != conditional
8570            && matches!(
8571                node.kind(),
8572                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
8573            )
8574        {
8575            continue;
8576        }
8577        let inside_error = inside_error || node.kind() == "ERROR";
8578        for index in 0..node.child_count() {
8579            if let Some(child) = node.child(index) {
8580                stack.push((child, inside_error));
8581            }
8582        }
8583    }
8584    displaced
8585}
8586
8587/// The effective end of a conditional whose real terminator tree-sitter
8588/// displaced into declaration recovery.
8589///
8590/// Most damaged conditionals retain a concrete `#endif` token below an
8591/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
8592/// boundary. A preprocessor family that selects the middle of a declaration
8593/// can lose the directive tokens entirely. In that shape tree-sitter leaves
8594/// the declaration's `typedef` token as the sole child of the immediately
8595/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
8596/// declarator name inside the conditional's first declaration. The declaration
8597/// end is then the smallest structured boundary that contains the whole split
8598/// declaration.
8599#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8600pub struct CppDisplacedPreprocessorBoundary {
8601    pub end_byte: usize,
8602    pub end_line: usize,
8603}
8604
8605pub fn cpp_displaced_preprocessor_boundary(
8606    conditional: Node<'_>,
8607) -> Option<CppDisplacedPreprocessorBoundary> {
8608    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
8609        return Some(CppDisplacedPreprocessorBoundary {
8610            end_byte: terminator.end_byte(),
8611            end_line: terminator.end_position().row + 1,
8612        });
8613    }
8614    if let Some(declaration) = displaced_split_declaration(conditional) {
8615        return Some(CppDisplacedPreprocessorBoundary {
8616            end_byte: declaration.end_byte(),
8617            end_line: declaration.end_position().row + 1,
8618        });
8619    }
8620    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
8621        return Some(CppDisplacedPreprocessorBoundary {
8622            end_byte: terminator.end_byte(),
8623            end_line: terminator.end_position().row + 1,
8624        });
8625    }
8626    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
8627        return Some(CppDisplacedPreprocessorBoundary {
8628            end_byte: terminator.end_byte(),
8629            end_line: terminator.end_position().row + 1,
8630        });
8631    }
8632    None
8633}
8634
8635/// Recover an outer terminator that tree-sitter assigned to a damaged nested
8636/// conditional. This occurs when a split construct such as `extern "C"`
8637/// consumes the nested `#endif` inside an error node: the nested conditional's
8638/// direct terminator is then the outer conditional's real terminator, while
8639/// the outer node ends with a missing token and absorbs later declarations.
8640fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
8641    if !conditional.has_error()
8642        || conditional.child_by_field_name("alternative").is_some()
8643        || conditional
8644            .child(conditional.child_count().saturating_sub(1))
8645            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
8646    {
8647        return None;
8648    }
8649    let mut recovered = None;
8650    for index in 0..conditional.named_child_count() {
8651        let Some(nested) = conditional.named_child(index) else {
8652            continue;
8653        };
8654        if !matches!(
8655            nested.kind(),
8656            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
8657        ) || nested.child_by_field_name("alternative").is_some()
8658        {
8659            continue;
8660        }
8661        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
8662            continue;
8663        };
8664        if direct.kind() != "#endif" || direct.is_missing() {
8665            continue;
8666        }
8667        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
8668            continue;
8669        };
8670        if displaced.end_byte() >= direct.start_byte() {
8671            continue;
8672        }
8673        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
8674            recovered = Some(direct);
8675        }
8676    }
8677    recovered
8678}
8679
8680fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
8681    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
8682        return None;
8683    }
8684    let mut cursor = conditional.walk();
8685    let declarations = conditional
8686        .named_children(&mut cursor)
8687        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
8688        .collect::<Vec<_>>();
8689    let declaration = *declarations.first()?;
8690    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
8691        return None;
8692    }
8693    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
8694    let mut terminator = None;
8695    let mut stack = (0..declaration.child_count())
8696        .filter_map(|index| declaration.child(index))
8697        .filter(|child| child.start_byte() < declarator_start)
8698        .map(|child| (child, false))
8699        .collect::<Vec<_>>();
8700    while let Some((node, inside_error)) = stack.pop() {
8701        let inside_error = inside_error || node.kind() == "ERROR";
8702        if inside_error && node.kind() == "#endif" && !node.is_missing() {
8703            terminator = Some(node);
8704            continue;
8705        }
8706        for index in 0..node.child_count() {
8707            if let Some(child) = node.child(index)
8708                && child.start_byte() < declarator_start
8709            {
8710                stack.push((child, inside_error));
8711            }
8712        }
8713    }
8714    terminator
8715}
8716
8717fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
8718    if !conditional.has_error()
8719        || conditional.child_by_field_name("alternative").is_some()
8720        || conditional
8721            .prev_named_sibling()
8722            .filter(|sibling| {
8723                sibling.kind() == "ERROR"
8724                    && sibling.child_count() == 1
8725                    && sibling
8726                        .child(0)
8727                        .is_some_and(|child| child.kind() == "typedef")
8728            })
8729            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
8730            .is_none()
8731    {
8732        return None;
8733    }
8734    let mut cursor = conditional.walk();
8735    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
8736    let declaration_index = children
8737        .iter()
8738        .position(|child| child.kind() == "declaration" && child.has_error())?;
8739    let declaration = children[declaration_index];
8740    if !children
8741        .iter()
8742        .skip(declaration_index + 1)
8743        .any(|child| child.end_byte() > declaration.end_byte())
8744    {
8745        return None;
8746    }
8747    let declarator = declaration.child_by_field_name("declarator")?;
8748    let mut error_end = None;
8749    let mut names = Vec::new();
8750    let mut stack = vec![declarator];
8751    while let Some(node) = stack.pop() {
8752        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
8753            error_end =
8754                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
8755            continue;
8756        }
8757        if matches!(node.kind(), "identifier" | "type_identifier") {
8758            names.push(node.start_byte());
8759        }
8760        for index in (0..node.named_child_count()).rev() {
8761            if let Some(child) = node.named_child(index) {
8762                stack.push(child);
8763            }
8764        }
8765    }
8766    let error_end = error_end?;
8767    names
8768        .into_iter()
8769        .any(|start| start >= error_end)
8770        .then_some(declaration)
8771}
8772
8773fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
8774    let Some(error) = parent.child(error_index) else {
8775        return false;
8776    };
8777    if error.kind() != "ERROR"
8778        || error.child_count() != 1
8779        || error.child(0).is_none_or(|child| child.kind() != "}")
8780    {
8781        return false;
8782    }
8783    let Some(semicolon) = parent.child(error_index + 1) else {
8784        return false;
8785    };
8786    semicolon.kind() == "expression_statement"
8787        && semicolon.child_count() == 1
8788        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
8789}
8790
8791/// Locate the real end of a class-like declaration when a macro invocation
8792/// without a source semicolon absorbs the class's `};` into its parsed field.
8793/// The grammar then keeps following namespace declarations as later children
8794/// of the same field list. The direct ERROR-plus-semicolon pair proves the
8795/// boundary structurally; no source-text delimiter scan is needed.
8796fn displaced_macro_class_tail(
8797    declaration_node: Node<'_>,
8798    body: Node<'_>,
8799    source: &str,
8800) -> Option<DisplacedMacroClassTail> {
8801    if !matches!(
8802        declaration_node.kind(),
8803        "class_specifier" | "struct_specifier" | "union_specifier"
8804    ) || body.kind() != "field_declaration_list"
8805    {
8806        return None;
8807    }
8808
8809    let child_count = body.named_child_count();
8810    for index in 0..child_count {
8811        let child = body.named_child(index)?;
8812        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
8813            continue;
8814        };
8815        let split_index = index + 1;
8816        if split_index >= child_count {
8817            return None;
8818        }
8819        let mut cursor = body.walk();
8820        if !body
8821            .named_children(&mut cursor)
8822            .skip(split_index)
8823            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
8824        {
8825            return None;
8826        }
8827        return Some(DisplacedMacroClassTail {
8828            split_index,
8829            class_range: Range {
8830                start_byte: declaration_node.start_byte(),
8831                end_byte: terminator.end_byte(),
8832                start_line: declaration_node.start_position().row + 1,
8833                end_line: terminator.end_position().row + 1,
8834            },
8835        });
8836    }
8837    None
8838}
8839
8840fn displaced_macro_field_terminator<'tree>(
8841    field: Node<'tree>,
8842    source: &str,
8843) -> Option<Node<'tree>> {
8844    if field.kind() != "field_declaration" {
8845        return None;
8846    }
8847    let macro_type = field.child_by_field_name("type")?;
8848    if macro_type.kind() != "type_identifier"
8849        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8850        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
8851    {
8852        return None;
8853    }
8854    for index in 0..field.child_count() {
8855        let error = field.child(index)?;
8856        if error.kind() != "ERROR"
8857            || error.child_count() != 1
8858            || error.child(0).is_none_or(|child| child.kind() != "}")
8859        {
8860            continue;
8861        }
8862        let semicolon = field.child(index + 1)?;
8863        if semicolon.kind() == ";" {
8864            return Some(semicolon);
8865        }
8866    }
8867    None
8868}
8869
8870fn recover_fragmented_partial_specialization<'tree>(
8871    template_node: Node<'tree>,
8872    declaration_child: Node<'tree>,
8873    source: &str,
8874    ancestry: &ParentIndex<'tree>,
8875) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
8876    if declaration_child.kind() != "function_definition" {
8877        return None;
8878    }
8879    let class_node = declaration_child.child_by_field_name("type")?;
8880    if !matches!(
8881        class_node.kind(),
8882        "class_specifier" | "struct_specifier" | "union_specifier"
8883    ) || !class_node
8884        .child_by_field_name("name")
8885        .and_then(|name| direct_identifier_name(name, source))
8886        .is_some_and(|name| cpp_export_macro_token(&name))
8887    {
8888        return None;
8889    }
8890    let declarator = declaration_child.child_by_field_name("declarator")?;
8891    if declarator.kind() != "template_function" {
8892        return None;
8893    }
8894    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
8895    if metadata.specialization_arguments.is_empty() {
8896        return None;
8897    }
8898    let body = declaration_child.child_by_field_name("body")?;
8899    if body.kind() != "compound_statement" {
8900        return None;
8901    }
8902    let complete_prefix = body.named_child(0).filter(|first| {
8903        first.kind() == "labeled_statement"
8904            && first.has_error()
8905            && first
8906                .named_child(first.named_child_count().saturating_sub(1))
8907                .is_some_and(recovered_declaration_has_class_terminator)
8908    });
8909    let complete_body = complete_prefix.is_some();
8910    let mut prefix_members = Vec::new();
8911    if let Some(prefix) = complete_prefix {
8912        prefix_members.push(prefix);
8913    } else {
8914        let mut body_cursor = body.walk();
8915        for child in body.named_children(&mut body_cursor) {
8916            if !is_structurally_valid_fragmented_class_prefix_member(child) {
8917                break;
8918            }
8919            prefix_members.push(child);
8920        }
8921    }
8922    let containing_declarations = template_node.parent()?;
8923    if !matches!(
8924        containing_declarations.kind(),
8925        "declaration_list" | "compound_statement"
8926    ) {
8927        return None;
8928    }
8929    let mut member_siblings = Vec::new();
8930    let mut following_declarations = Vec::new();
8931    let terminator;
8932    if complete_body {
8933        terminator = complete_prefix?;
8934        let mut cursor = body.walk();
8935        let mut after_prefix = false;
8936        for child in body.named_children(&mut cursor) {
8937            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
8938                after_prefix = true;
8939            } else if after_prefix {
8940                following_declarations.push(child);
8941            }
8942        }
8943    } else {
8944        let mut found_template = false;
8945        let mut cursor = containing_declarations.walk();
8946        let mut class_terminator = None;
8947        for child in containing_declarations.children(&mut cursor) {
8948            if same_node(child, template_node) {
8949                found_template = true;
8950                continue;
8951            }
8952            if found_template && child.kind() == "}" {
8953                class_terminator = Some(child);
8954                break;
8955            }
8956            // A namespace can never be a class member: reaching one before the
8957            // terminator proves the class's true close was swallowed upstream
8958            // and this scan has crossed into the enclosing scope, so the
8959            // recovery cannot be bounded -- continuing re-owns the namespace
8960            // block (and its template specializations) as class members under
8961            // a re-appended package, desyncing the fq boundary (#2306).
8962            if found_template && child.kind() == "namespace_definition" {
8963                return None;
8964            }
8965            if found_template && child.is_named() {
8966                member_siblings.push(child);
8967            }
8968        }
8969        terminator = class_terminator?;
8970    }
8971    let name = format!(
8972        "{}<{}>",
8973        metadata.primary_name,
8974        metadata
8975            .specialization_arguments
8976            .iter()
8977            .map(|argument| argument.text.as_str())
8978            .collect::<Vec<_>>()
8979            .join(", ")
8980    );
8981    Some(RecoveredFragmentedPartialSpecialization {
8982        declaration_node: declaration_child,
8983        name,
8984        range: Range {
8985            start_byte: declaration_child.start_byte(),
8986            end_byte: terminator.end_byte(),
8987            start_line: declaration_child.start_position().row + 1,
8988            end_line: terminator.end_position().row + 1,
8989        },
8990        prefix_members,
8991        member_siblings,
8992        following_declarations,
8993    })
8994}
8995
8996fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
8997    if declaration.kind() != "declaration" {
8998        return false;
8999    }
9000    // With an export macro between `class` and its name, tree-sitter folds a
9001    // complete class body into a function-shaped declaration. The class's own
9002    // `};` remains structurally identifiable as a direct ERROR child holding
9003    // `}`, immediately followed by the declaration's direct `;` child.
9004    (0..declaration.child_count().saturating_sub(1)).any(|index| {
9005        let Some(error) = declaration.child(index) else {
9006            return false;
9007        };
9008        error.kind() == "ERROR"
9009            && error.child_count() == 1
9010            && error.child(0).is_some_and(|child| child.kind() == "}")
9011            && declaration
9012                .child(index + 1)
9013                .is_some_and(|child| child.kind() == ";")
9014    })
9015}
9016
9017fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
9018    if node.has_error() {
9019        return false;
9020    }
9021    match node.kind() {
9022        "declaration"
9023        | "field_declaration"
9024        | "alias_declaration"
9025        | "type_definition"
9026        | "static_assert_declaration" => true,
9027        "labeled_statement" => node
9028            .named_child(node.named_child_count().saturating_sub(1))
9029            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
9030        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
9031            matches!(
9032                child.kind(),
9033                "declaration"
9034                    | "field_declaration"
9035                    | "alias_declaration"
9036                    | "type_definition"
9037                    | "function_definition"
9038            )
9039        }),
9040        _ => false,
9041    }
9042}
9043
9044fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
9045    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
9046        .then(|| node.child_by_field_name("declarator"))
9047        .flatten()
9048        .and_then(|declarator| extract_variable_name(declarator, source))
9049}
9050
9051fn cpp_template_metadata<'tree>(
9052    template_node: Node<'tree>,
9053    declaration_child: Node<'tree>,
9054    source: &str,
9055    ancestry: &ParentIndex<'tree>,
9056) -> Option<CppTemplateMetadata> {
9057    let parameters_node = template_node.child_by_field_name("parameters")?;
9058    let name_node = cpp_templated_class_name_node(declaration_child)?;
9059    let primary_node = match name_node.kind() {
9060        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
9061        _ => name_node,
9062    };
9063    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
9064    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
9065        return None;
9066    }
9067
9068    let mut parameter_nodes = Vec::new();
9069    let mut parameter_names = Vec::new();
9070    let mut cursor = parameters_node.walk();
9071    for parameter in parameters_node.named_children(&mut cursor) {
9072        if !matches!(
9073            parameter.kind(),
9074            "type_parameter_declaration"
9075                | "optional_type_parameter_declaration"
9076                | "variadic_type_parameter_declaration"
9077                | "template_template_parameter_declaration"
9078                | "parameter_declaration"
9079                | "optional_parameter_declaration"
9080                | "variadic_parameter_declaration"
9081        ) {
9082            continue;
9083        }
9084        let index = parameter_nodes.len();
9085        // An unnamed parameter still contributes template arity and kind. Use
9086        // an impossible C++ identifier so positional reconciliation can bind
9087        // it without making source expressions refer to a name that was not
9088        // written.
9089        let name = cpp_template_parameter_name(parameter, source)
9090            .unwrap_or_else(|| format!("<anonymous:{index}>"));
9091        parameter_names.push(name);
9092        parameter_nodes.push(parameter);
9093    }
9094    let parameters = parameter_nodes
9095        .into_iter()
9096        .zip(parameter_names.iter().cloned())
9097        .map(|(parameter, name)| CppTemplateParameterMetadata {
9098            name,
9099            kind: cpp_template_parameter_kind(parameter),
9100            variadic: matches!(
9101                parameter.kind(),
9102                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
9103            ),
9104            default: cpp_template_parameter_default_expression(
9105                parameter,
9106                source,
9107                &parameter_names,
9108                ancestry,
9109            ),
9110        })
9111        .collect();
9112    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
9113        Vec::new()
9114    } else {
9115        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
9116            .unwrap_or_default()
9117    };
9118    let alias_target = (declaration_child.kind() == "alias_declaration")
9119        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
9120        .flatten();
9121    Some(CppTemplateMetadata {
9122        primary_name,
9123        primary_fq_name: String::new(),
9124        parameters,
9125        specialization_arguments,
9126        alias_target,
9127    })
9128}
9129
9130fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
9131    match node.kind() {
9132        "class_specifier" | "struct_specifier" | "union_specifier" => {
9133            node.child_by_field_name("name")
9134        }
9135        "function_definition" => {
9136            let declarator = node.child_by_field_name("declarator")?;
9137            if matches!(declarator.kind(), "identifier" | "template_function") {
9138                Some(declarator)
9139            } else {
9140                None
9141            }
9142        }
9143        "alias_declaration" => node.child_by_field_name("name"),
9144        _ => None,
9145    }
9146}
9147
9148fn cpp_template_alias_target<'tree>(
9149    alias: Node<'tree>,
9150    source: &str,
9151    parameter_names: &[String],
9152    ancestry: &ParentIndex<'tree>,
9153) -> Option<CppTemplateAliasTargetMetadata> {
9154    let mut type_node = alias.child_by_field_name("type")?;
9155    while type_node.kind() == "type_descriptor" {
9156        type_node = type_node.child_by_field_name("type")?;
9157    }
9158    let global = type_node.child_by_field_name("scope").is_none()
9159        && type_node.child(0).is_some_and(|child| child.kind() == "::");
9160    let mut components = Vec::new();
9161    cpp_template_target_components(type_node, source, &mut components)?;
9162    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
9163    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
9164        components,
9165        global,
9166        arguments,
9167    })
9168}
9169
9170fn cpp_template_target_components(
9171    node: Node<'_>,
9172    source: &str,
9173    out: &mut Vec<String>,
9174) -> Option<()> {
9175    match node.kind() {
9176        "identifier" | "namespace_identifier" | "type_identifier" => {
9177            out.push(node_text(node, source).to_string());
9178            Some(())
9179        }
9180        "template_type" => {
9181            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
9182        }
9183        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
9184            if let Some(scope) = node.child_by_field_name("scope") {
9185                cpp_template_target_components(scope, source, out)?;
9186            }
9187            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
9188        }
9189        _ => None,
9190    }
9191}
9192
9193fn cpp_template_argument_expressions<'tree>(
9194    mut node: Node<'tree>,
9195    source: &str,
9196    parameter_names: &[String],
9197    ancestry: &ParentIndex<'tree>,
9198) -> Option<Vec<CppTemplateExpression>> {
9199    loop {
9200        match node.kind() {
9201            "template_type" | "template_function" => {
9202                let arguments = node.child_by_field_name("arguments")?;
9203                let mut cursor = arguments.walk();
9204                return Some(
9205                    arguments
9206                        .named_children(&mut cursor)
9207                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
9208                        .map(|argument| {
9209                            cpp_template_expression(argument, source, parameter_names, ancestry)
9210                        })
9211                        .collect(),
9212                );
9213            }
9214            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
9215                node = node
9216                    .child_by_field_name("name")
9217                    .or_else(|| node.child_by_field_name("type"))?;
9218            }
9219            _ => return None,
9220        }
9221    }
9222}
9223
9224fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
9225    let candidate = node
9226        .child_by_field_name("name")
9227        .or_else(|| node.child_by_field_name("declarator"))
9228        .or_else(|| {
9229            let mut cursor = node.walk();
9230            node.named_children(&mut cursor).find(|child| {
9231                matches!(
9232                    child.kind(),
9233                    "identifier" | "type_identifier" | "field_identifier"
9234                )
9235            })
9236        })?;
9237    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
9238    (!name.is_empty()).then_some(name)
9239}
9240
9241fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
9242    match node.kind() {
9243        "type_parameter_declaration"
9244        | "optional_type_parameter_declaration"
9245        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
9246        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
9247        _ => CppTemplateParameterKind::Value,
9248    }
9249}
9250
9251fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
9252    node.child_by_field_name("default_type")
9253        .or_else(|| node.child_by_field_name("default_value"))
9254}
9255
9256fn cpp_template_parameter_default_expression<'tree>(
9257    parameter: Node<'tree>,
9258    source: &str,
9259    parameter_names: &[String],
9260    ancestry: &ParentIndex<'tree>,
9261) -> Option<CppTemplateExpression> {
9262    let default = cpp_template_parameter_default(parameter)?;
9263    let base = cpp_template_expression(default, source, parameter_names, ancestry);
9264    let Some(pointer_error) = parameter.next_named_sibling() else {
9265        return Some(base);
9266    };
9267    let Some(pointer_declarator) =
9268        recovered_abstract_pointer_declarator_term(pointer_error, source)
9269    else {
9270        return Some(base);
9271    };
9272    Some(CppTemplateExpression {
9273        text: format!(
9274            "{}{}",
9275            base.text,
9276            normalize_cpp_whitespace(node_text(pointer_error, source))
9277        ),
9278        term: CppTemplateTerm::Node {
9279            kind: "type_descriptor".to_string(),
9280            children: vec![base.term, pointer_declarator],
9281        },
9282    })
9283}
9284
9285fn recovered_abstract_pointer_declarator_term(
9286    node: Node<'_>,
9287    source: &str,
9288) -> Option<CppTemplateTerm> {
9289    if node.kind() != "ERROR" || node.child_count() == 0 {
9290        return None;
9291    }
9292    let mut children = Vec::new();
9293    for index in 0..node.child_count() {
9294        let child = node.child(index)?;
9295        if child.kind() != "*" {
9296            return None;
9297        }
9298        children.push(CppTemplateTerm::Atom {
9299            kind: "*".to_string(),
9300            text: normalize_cpp_whitespace(node_text(child, source)),
9301        });
9302    }
9303    Some(CppTemplateTerm::Node {
9304        kind: "abstract_pointer_declarator".to_string(),
9305        children,
9306    })
9307}
9308
9309fn cpp_template_expression<'tree>(
9310    node: Node<'tree>,
9311    source: &str,
9312    parameter_names: &[String],
9313    ancestry: &ParentIndex<'tree>,
9314) -> CppTemplateExpression {
9315    let text = normalize_cpp_whitespace(node_text(node, source));
9316    CppTemplateExpression {
9317        text,
9318        term: cpp_template_term(node, source, parameter_names, ancestry),
9319    }
9320}
9321
9322pub fn cpp_template_term<'tree>(
9323    node: Node<'tree>,
9324    source: &str,
9325    parameter_names: &[String],
9326    ancestry: &ParentIndex<'tree>,
9327) -> CppTemplateTerm {
9328    enum Work<'tree> {
9329        Visit(Node<'tree>),
9330        Build { kind: String, child_count: usize },
9331    }
9332
9333    let mut work = vec![Work::Visit(node)];
9334    let mut terms = Vec::new();
9335    while let Some(next) = work.pop() {
9336        match next {
9337            Work::Visit(current) => {
9338                let text = normalize_cpp_whitespace(node_text(current, source));
9339                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
9340                    terms.push(CppTemplateTerm::Parameter(text));
9341                    continue;
9342                }
9343                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
9344                    let mut cursor = current.walk();
9345                    let named = current
9346                        .named_children(&mut cursor)
9347                        .filter(|child| !child.is_extra() && child.kind() != "comment")
9348                        .collect::<Vec<_>>();
9349                    if let [child] = named.as_slice() {
9350                        work.push(Work::Visit(*child));
9351                        continue;
9352                    }
9353                }
9354                if current.child_count() == 0 {
9355                    terms.push(CppTemplateTerm::Atom {
9356                        kind: if matches!(
9357                            current.kind(),
9358                            "identifier"
9359                                | "type_identifier"
9360                                | "field_identifier"
9361                                | "namespace_identifier"
9362                        ) {
9363                            "identifier".to_string()
9364                        } else {
9365                            current.kind().to_string()
9366                        },
9367                        text,
9368                    });
9369                    continue;
9370                }
9371                let children = (0..current.child_count())
9372                    .filter_map(|index| current.child(index))
9373                    .filter(|child| !child.is_extra() && child.kind() != "comment")
9374                    .collect::<Vec<_>>();
9375                work.push(Work::Build {
9376                    kind: current.kind().to_string(),
9377                    child_count: children.len(),
9378                });
9379                work.extend(children.into_iter().rev().map(Work::Visit));
9380            }
9381            Work::Build { kind, child_count } => {
9382                let children = terms.split_off(terms.len() - child_count);
9383                terms.push(CppTemplateTerm::Node { kind, children });
9384            }
9385        }
9386    }
9387    terms.pop().expect("template term traversal emits one root")
9388}
9389
9390fn cpp_template_term_leaf_is_parameter<'tree>(
9391    node: Node<'tree>,
9392    text: &str,
9393    parameter_names: &[String],
9394    ancestry: &ParentIndex<'tree>,
9395) -> bool {
9396    if !parameter_names.iter().any(|parameter| parameter == text) {
9397        return false;
9398    }
9399    !ancestry.parent(node).is_some_and(|parent| {
9400        matches!(
9401            parent.kind(),
9402            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
9403        ) && parent.child_by_field_name("scope").is_some()
9404            && parent.child_by_field_name("name") == Some(node)
9405    })
9406}
9407
9408fn enclosing_cpp_declaration_node<'tree>(
9409    mut node: Node<'tree>,
9410    ancestry: &ParentIndex<'tree>,
9411) -> Option<Node<'tree>> {
9412    loop {
9413        match node.kind() {
9414            "declaration"
9415            | "function_declaration"
9416            | "field_declaration"
9417            | "function_definition" => return Some(node),
9418            _ => node = ancestry.parent(node)?,
9419        }
9420    }
9421}
9422
9423fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
9424    let mut params = Vec::new();
9425    let mut cursor = parameters_node.walk();
9426    for child in parameters_node.children(&mut cursor) {
9427        match child.kind() {
9428            "parameter_declaration" | "optional_parameter_declaration" => {
9429                params.push(cpp_parameter_type(child, source));
9430            }
9431            "variadic_parameter_declaration" => {
9432                params.push(cpp_parameter_type(child, source));
9433            }
9434            "variadic_parameter" | "..." => params.push("...".to_string()),
9435            _ => {}
9436        }
9437    }
9438
9439    if params.is_empty() {
9440        "()".to_string()
9441    } else {
9442        format!("({})", params.join(", "))
9443    }
9444}
9445
9446fn cpp_signature_metadata<'tree>(
9447    signature: String,
9448    function_declarator: Node<'tree>,
9449    source: &str,
9450    ancestry: &ParentIndex<'tree>,
9451) -> SignatureMetadata {
9452    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
9453    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
9454    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
9455    let return_type_identity =
9456        cpp_callable_return_type_identity(function_declarator, source, ancestry);
9457    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
9458        return enrich(
9459            SignatureMetadata::new(signature, Vec::new())
9460                .with_return_type_text(return_type_text)
9461                .with_return_type_identity(return_type_identity),
9462        );
9463    };
9464    let callable_arity = cpp_callable_arity(parameters_node, source);
9465    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
9466    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
9467    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
9468    let Some(relative_start) = signature
9469        .get(search_from..)
9470        .and_then(|suffix| suffix.find(&parameter_text))
9471    else {
9472        return enrich(
9473            SignatureMetadata::new(signature, Vec::new())
9474                .with_callable_arity(callable_arity)
9475                .with_callable_parameter_types(callable_parameter_types)
9476                .with_return_type_text(return_type_text)
9477                .with_return_type_identity(return_type_identity),
9478        );
9479    };
9480    let parameters_start = search_from + relative_start;
9481    let parameters_end = parameters_start + parameter_text.len();
9482    let mut search_start = parameters_start;
9483    let parameters = cpp_parameter_label_nodes(parameters_node)
9484        .into_iter()
9485        .filter_map(|label_node| {
9486            let label = normalize_cpp_whitespace(node_text(label_node, source));
9487            if label.is_empty() || search_start > parameters_end {
9488                return None;
9489            }
9490            let haystack = signature.get(search_start..parameters_end)?;
9491            let relative_start = haystack.find(&label)?;
9492            let start_byte = search_start + relative_start;
9493            let end_byte = start_byte + label.len();
9494            search_start = end_byte;
9495            Some(ParameterMetadata::new(label, start_byte, end_byte))
9496        })
9497        .collect();
9498    enrich(
9499        SignatureMetadata::new(signature, parameters)
9500            .with_callable_arity(callable_arity)
9501            .with_callable_parameter_types(callable_parameter_types)
9502            .with_return_type_text(return_type_text)
9503            .with_return_type_identity(return_type_identity),
9504    )
9505}
9506
9507fn cpp_callable_is_structural_constructor<'tree>(
9508    function_declarator: Node<'tree>,
9509    source: &str,
9510    ancestry: &ParentIndex<'tree>,
9511) -> bool {
9512    let Some(name_node) = function_declarator
9513        .child_by_field_name("declarator")
9514        .or_else(|| function_declarator.child_by_field_name("name"))
9515        .or_else(|| last_named_child(function_declarator))
9516    else {
9517        return false;
9518    };
9519    let Some(callable_name) = direct_identifier_name(name_node, source) else {
9520        return false;
9521    };
9522
9523    let mut current = ancestry.parent(function_declarator);
9524    while let Some(ancestor) = current {
9525        let owner_name = match ancestor.kind() {
9526            "class_specifier" | "struct_specifier" | "union_specifier" => {
9527                class_like_name(ancestor, source, ancestry)
9528            }
9529            "ERROR" => malformed_class_error_owner_name(ancestor, source),
9530            _ => None,
9531        };
9532        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
9533            return true;
9534        }
9535        current = ancestry.parent(ancestor);
9536    }
9537    false
9538}
9539
9540/// Recover the owner name from the direct grammar shape retained when a later
9541/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
9542/// `ERROR` node:
9543///
9544/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
9545///
9546/// Direct-child checks keep this distinct from an unrelated nested class inside
9547/// a broader error region. The closing brace may be displaced past the error
9548/// node, so the opening body token is the available structural boundary.
9549fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
9550    if node.kind() != "ERROR" {
9551        return None;
9552    }
9553    let keyword = node.child(0)?;
9554    if !matches!(keyword.kind(), "class" | "struct" | "union") {
9555        return None;
9556    }
9557    let name_node = node.child(1)?;
9558    let name = direct_identifier_name(name_node, source)?;
9559    let has_body = (2..node.child_count())
9560        .filter_map(|index| node.child(index))
9561        .any(|child| child.kind() == "{");
9562    has_body.then_some(name)
9563}
9564
9565fn cpp_callable_return_type_identity<'tree>(
9566    function_declarator: Node<'tree>,
9567    source: &str,
9568    ancestry: &ParentIndex<'tree>,
9569) -> Option<StructuredTypeIdentity> {
9570    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
9571        return None;
9572    }
9573    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
9574    if let Some((return_type, _)) =
9575        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
9576    {
9577        return cpp_structured_type_identity(return_type, source, &lexical_scope);
9578    }
9579    let mut cursor = function_declarator.walk();
9580    if let Some(trailing) = function_declarator
9581        .named_children(&mut cursor)
9582        .find(|child| child.kind() == "trailing_return_type")
9583        && let Some(type_descriptor) = trailing.named_child(0)
9584    {
9585        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
9586    }
9587
9588    let mut current = function_declarator;
9589    let mut wrappers = Vec::new();
9590    while let Some(parent) = ancestry.parent(current) {
9591        if matches!(
9592            parent.kind(),
9593            "function_definition" | "declaration" | "field_declaration"
9594        ) {
9595            let type_node = parent.child_by_field_name("type")?;
9596            if cpp_export_macro_token(node_text(type_node, source))
9597                && (0..parent.named_child_count()).any(|index| {
9598                    parent
9599                        .named_child(index)
9600                        .is_some_and(|child| child.kind() == "ERROR")
9601                })
9602            {
9603                return None;
9604            }
9605            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
9606            for wrapper in wrappers.into_iter().rev() {
9607                identity = cpp_wrap_structured_type(identity, wrapper)?;
9608            }
9609            return Some(identity);
9610        }
9611        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
9612            || (matches!(
9613                parent.kind(),
9614                "pointer_declarator"
9615                    | "reference_declarator"
9616                    | "array_declarator"
9617                    | "parenthesized_declarator"
9618            ) && parent.named_child_count() == 1
9619                && parent.named_child(0) == Some(current));
9620        if !wraps_current_declarator {
9621            return None;
9622        }
9623        match parent.kind() {
9624            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
9625            "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
9626            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
9627            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
9628            _ => return None,
9629        }
9630        current = parent;
9631    }
9632    None
9633}
9634
9635fn cpp_structured_type_identity(
9636    node: Node<'_>,
9637    source: &str,
9638    lexical_scope: &[String],
9639) -> Option<StructuredTypeIdentity> {
9640    enum Work<'tree> {
9641        Visit(Node<'tree>),
9642        Wrap(CppStructuredTypeWrapper),
9643        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
9644        BuildGeneric { argument_count: usize },
9645    }
9646
9647    let mut work = vec![Work::Visit(node)];
9648    let mut values = Vec::new();
9649    let mut builder = StructuredTypeIdentityBuilder::default();
9650    while let Some(next) = work.pop() {
9651        match next {
9652            Work::Visit(current) => match current.kind() {
9653                "type_descriptor" => {
9654                    let type_node = current
9655                        .child_by_field_name("type")
9656                        .or_else(|| current.named_child(0))?;
9657                    let mut wrappers = Vec::new();
9658                    let mut cursor = current.walk();
9659                    for child in current.named_children(&mut cursor) {
9660                        if child.id() != type_node.id() {
9661                            wrappers.extend(cpp_structured_declarator_wrappers(child));
9662                        }
9663                    }
9664                    work.push(Work::ApplyWrappers(wrappers));
9665                    work.push(Work::Visit(type_node));
9666                }
9667                "pointer_declarator" | "abstract_pointer_declarator" => {
9668                    let child = current
9669                        .child_by_field_name("declarator")
9670                        .or_else(|| current.named_child(0))?;
9671                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
9672                    work.push(Work::Visit(child));
9673                }
9674                "reference_declarator" => {
9675                    let child = current
9676                        .child_by_field_name("declarator")
9677                        .or_else(|| current.named_child(0))?;
9678                    work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
9679                    work.push(Work::Visit(child));
9680                }
9681                "array_declarator" | "abstract_array_declarator" => {
9682                    let child = current
9683                        .child_by_field_name("declarator")
9684                        .or_else(|| current.named_child(0))?;
9685                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
9686                    work.push(Work::Visit(child));
9687                }
9688                "template_type" => {
9689                    let name_node = current.child_by_field_name("name")?;
9690                    let arguments = current
9691                        .child_by_field_name("arguments")
9692                        .map(|arguments_node| {
9693                            let mut cursor = arguments_node.walk();
9694                            arguments_node
9695                                .named_children(&mut cursor)
9696                                .filter(|child| !child.is_extra() && child.kind() != "comment")
9697                                .collect::<Vec<_>>()
9698                        })
9699                        .unwrap_or_default();
9700                    work.push(Work::BuildGeneric {
9701                        argument_count: arguments.len(),
9702                    });
9703                    work.extend(arguments.into_iter().rev().map(Work::Visit));
9704                    work.push(Work::Visit(name_node));
9705                }
9706                "qualified_identifier"
9707                | "scoped_identifier"
9708                | "scoped_type_identifier"
9709                | "type_identifier"
9710                | "field_identifier"
9711                | "identifier"
9712                | "namespace_identifier"
9713                | "primitive_type" => {
9714                    values.push(builder.named(cpp_structured_named_type(
9715                        current,
9716                        source,
9717                        lexical_scope,
9718                    )?)?);
9719                }
9720                _ => {
9721                    let child = current.child_by_field_name("type").or_else(|| {
9722                        (current.named_child_count() == 1)
9723                            .then(|| current.named_child(0))
9724                            .flatten()
9725                    })?;
9726                    work.push(Work::Visit(child));
9727                }
9728            },
9729            Work::Wrap(wrapper) => {
9730                let root = values.pop()?;
9731                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
9732            }
9733            Work::ApplyWrappers(wrappers) => {
9734                let mut root = values.pop()?;
9735                for wrapper in wrappers.into_iter().rev() {
9736                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
9737                }
9738                values.push(root);
9739            }
9740            Work::BuildGeneric { argument_count } => {
9741                let value_count = argument_count.checked_add(1)?;
9742                let start = values.len().checked_sub(value_count)?;
9743                let mut built = values.split_off(start);
9744                let base = built.remove(0);
9745                values.push(builder.generic(base, built)?);
9746            }
9747        }
9748    }
9749    (values.len() == 1)
9750        .then(|| values.pop())
9751        .flatten()
9752        .and_then(|root| builder.finish(root))
9753}
9754
9755fn cpp_structured_named_type(
9756    node: Node<'_>,
9757    source: &str,
9758    lexical_scope: &[String],
9759) -> Option<StructuredTypeName> {
9760    let path = cpp_structured_type_path(node, source)?;
9761    let absolute = node.child_by_field_name("scope").is_none()
9762        && node.child(0).is_some_and(|child| child.kind() == "::");
9763    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
9764}
9765
9766#[derive(Clone, Copy)]
9767enum CppStructuredTypeWrapper {
9768    Pointer,
9769    Reference,
9770    Array,
9771}
9772
9773fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
9774    let mut wrappers = Vec::new();
9775    let mut current = node;
9776    loop {
9777        match current.kind() {
9778            "pointer_declarator" | "abstract_pointer_declarator" => {
9779                wrappers.push(CppStructuredTypeWrapper::Pointer)
9780            }
9781            "reference_declarator" | "abstract_reference_declarator" => {
9782                wrappers.push(CppStructuredTypeWrapper::Reference)
9783            }
9784            "array_declarator" | "abstract_array_declarator" => {
9785                wrappers.push(CppStructuredTypeWrapper::Array)
9786            }
9787            _ => break,
9788        }
9789        let Some(child) = current
9790            .child_by_field_name("declarator")
9791            .or_else(|| current.named_child(0))
9792        else {
9793            break;
9794        };
9795        current = child;
9796    }
9797    wrappers
9798}
9799
9800fn cpp_wrap_structured_type(
9801    identity: StructuredTypeIdentity,
9802    wrapper: CppStructuredTypeWrapper,
9803) -> Option<StructuredTypeIdentity> {
9804    match wrapper {
9805        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
9806        CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
9807        CppStructuredTypeWrapper::Array => identity.wrap_array(),
9808    }
9809}
9810
9811fn cpp_wrap_structured_type_node(
9812    builder: &mut StructuredTypeIdentityBuilder,
9813    inner: StructuredTypeNodeId,
9814    wrapper: CppStructuredTypeWrapper,
9815) -> Option<StructuredTypeNodeId> {
9816    match wrapper {
9817        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
9818        CppStructuredTypeWrapper::Reference => builder.reference(inner),
9819        CppStructuredTypeWrapper::Array => builder.array(inner),
9820    }
9821}
9822
9823fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
9824    let mut path = Vec::new();
9825    let mut stack = vec![node];
9826    while let Some(current) = stack.pop() {
9827        match current.kind() {
9828            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
9829                let component = node_text(current, source).to_string();
9830                if component.is_empty() {
9831                    return None;
9832                }
9833                path.push(component);
9834            }
9835            "template_type" | "dependent_type" => {
9836                stack.push(current.child_by_field_name("name")?);
9837            }
9838            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
9839                stack.push(current.child_by_field_name("name")?);
9840                if let Some(scope) = current.child_by_field_name("scope") {
9841                    stack.push(scope);
9842                }
9843            }
9844            _ => return None,
9845        }
9846    }
9847    (!path.is_empty()).then_some(path)
9848}
9849
9850fn cpp_callable_lexical_scope<'tree>(
9851    node: Node<'tree>,
9852    source: &str,
9853    ancestry: &ParentIndex<'tree>,
9854) -> Vec<String> {
9855    let mut groups = Vec::new();
9856    let mut current = ancestry.parent(node);
9857    while let Some(parent) = current {
9858        if matches!(
9859            parent.kind(),
9860            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
9861        ) && let Some(name_node) = parent.child_by_field_name("name")
9862            && let Some(components) = cpp_structured_type_path(name_node, source)
9863            && !components.is_empty()
9864        {
9865            groups.push(components);
9866        }
9867        current = ancestry.parent(parent);
9868    }
9869    groups.reverse();
9870    groups.into_iter().flatten().collect()
9871}
9872
9873fn cpp_callable_dispatch_extensibility<'tree>(
9874    function_declarator: Node<'tree>,
9875    ancestry: &ParentIndex<'tree>,
9876) -> DispatchExtensibility {
9877    let mut declaration = None;
9878    let mut current = Some(function_declarator);
9879    while let Some(node) = current {
9880        match node.kind() {
9881            "template_declaration"
9882            | "preproc_if"
9883            | "preproc_ifdef"
9884            | "preproc_else"
9885            | "preproc_elif"
9886            | "preproc_call"
9887            | "ERROR" => return DispatchExtensibility::Open,
9888            "declaration" | "field_declaration" | "function_definition" => {
9889                declaration.get_or_insert(node);
9890            }
9891            "translation_unit" => break,
9892            _ => {}
9893        }
9894        current = ancestry.parent(node);
9895    }
9896    let Some(declaration) = declaration else {
9897        return DispatchExtensibility::Open;
9898    };
9899
9900    let mut saw_virtual_boundary = false;
9901    let mut stack = vec![declaration];
9902    while let Some(node) = stack.pop() {
9903        match node.kind() {
9904            "compound_statement" | "field_declaration_list" => continue,
9905            "final" | "final_specifier" => return DispatchExtensibility::Closed,
9906            "virtual"
9907            | "override"
9908            | "virtual_specifier"
9909            | "pure_virtual_clause"
9910            | "template_parameter_list"
9911            | "template_method"
9912            | "template_function"
9913            | "ERROR" => saw_virtual_boundary = true,
9914            _ => {}
9915        }
9916        let mut cursor = node.walk();
9917        stack.extend(node.children(&mut cursor));
9918    }
9919
9920    if saw_virtual_boundary {
9921        DispatchExtensibility::Open
9922    } else {
9923        DispatchExtensibility::Closed
9924    }
9925}
9926
9927fn cpp_callable_linkage<'tree>(
9928    declaration: Node<'tree>,
9929    source: &str,
9930    ancestry: &ParentIndex<'tree>,
9931) -> CallableLinkage {
9932    let mut enclosed_by_class = false;
9933    let mut current = ancestry.parent(declaration);
9934    while let Some(node) = current {
9935        if node.kind() == "namespace_definition"
9936            && node
9937                .child_by_field_name("name")
9938                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
9939        {
9940            return CallableLinkage::Internal;
9941        }
9942        if matches!(
9943            node.kind(),
9944            "class_specifier" | "struct_specifier" | "union_specifier"
9945        ) {
9946            if node
9947                .child_by_field_name("name")
9948                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
9949            {
9950                return CallableLinkage::Internal;
9951            }
9952            enclosed_by_class = true;
9953        }
9954        if matches!(node.kind(), "function_definition" | "lambda_expression") {
9955            return CallableLinkage::Internal;
9956        }
9957        current = ancestry.parent(node);
9958    }
9959
9960    if enclosed_by_class {
9961        return CallableLinkage::External;
9962    }
9963
9964    let mut cursor = declaration.walk();
9965    if declaration.named_children(&mut cursor).any(|child| {
9966        child.kind() == "storage_class_specifier"
9967            && normalize_cpp_whitespace(node_text(child, source)) == "static"
9968    }) {
9969        CallableLinkage::Internal
9970    } else {
9971        CallableLinkage::External
9972    }
9973}
9974
9975fn cpp_callable_return_type_text<'tree>(
9976    function_declarator: Node<'tree>,
9977    source: &str,
9978    ancestry: &ParentIndex<'tree>,
9979) -> Option<String> {
9980    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
9981        return None;
9982    }
9983    if let Some((return_type, _)) =
9984        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
9985    {
9986        let text = normalize_cpp_whitespace(node_text(return_type, source));
9987        return (!text.is_empty()).then_some(text);
9988    }
9989    let mut cursor = function_declarator.walk();
9990    if let Some(trailing) = function_declarator
9991        .named_children(&mut cursor)
9992        .find(|child| child.kind() == "trailing_return_type")
9993        && let Some(type_descriptor) = trailing.named_child(0)
9994    {
9995        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
9996        if !text.is_empty() {
9997            return Some(text);
9998        }
9999    }
10000
10001    let mut current = function_declarator;
10002    let mut indirection = String::new();
10003    while let Some(parent) = ancestry.parent(current) {
10004        if matches!(
10005            parent.kind(),
10006            "function_definition" | "declaration" | "field_declaration"
10007        ) {
10008            let type_node = parent.child_by_field_name("type")?;
10009            if cpp_export_macro_token(node_text(type_node, source))
10010                && (0..parent.named_child_count()).any(|index| {
10011                    parent
10012                        .named_child(index)
10013                        .is_some_and(|child| child.kind() == "ERROR")
10014                })
10015            {
10016                // Export/decorator macros commonly occupy the grammar's `type`
10017                // field and leave the semantic return type in an ERROR sibling.
10018                // Do not persist the macro token as a return type. The malformed
10019                // declaration does not carry enough structured evidence here.
10020                return None;
10021            }
10022            let base = normalize_cpp_whitespace(node_text(type_node, source));
10023            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
10024        }
10025        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
10026            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
10027                && parent.named_child_count() == 1
10028                && parent.named_child(0) == Some(current));
10029        if wraps_current_declarator {
10030            match parent.kind() {
10031                "pointer_declarator" => indirection.push('*'),
10032                "reference_declarator" => {
10033                    let reference = parent
10034                        .children(&mut parent.walk())
10035                        .find(|child| !child.is_named())
10036                        .map(|child| node_text(child, source))
10037                        .unwrap_or("&");
10038                    indirection.push_str(reference);
10039                }
10040                "init_declarator" | "parenthesized_declarator" => {}
10041                _ => return None,
10042            }
10043            current = parent;
10044            continue;
10045        }
10046        return None;
10047    }
10048    None
10049}
10050
10051fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
10052    let mut required = 0;
10053    let mut total = 0;
10054    let mut repeated = false;
10055    let mut cursor = parameters_node.walk();
10056    for child in parameters_node.children(&mut cursor) {
10057        match child.kind() {
10058            "parameter_declaration" => {
10059                if cpp_parameter_is_explicit_object(child, source) {
10060                    continue;
10061                }
10062                if child.child_by_field_name("declarator").is_none()
10063                    && child
10064                        .child_by_field_name("type")
10065                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
10066                {
10067                    continue;
10068                }
10069                required += 1;
10070                total += 1;
10071            }
10072            "optional_parameter_declaration" => total += 1,
10073            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
10074                repeated = true;
10075            }
10076            _ => {}
10077        }
10078    }
10079    CallableArity::new(required, total, repeated)
10080}
10081
10082fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
10083    parameter
10084        .child_by_field_name("type")
10085        .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
10086        .and_then(|type_node| type_node.child_by_field_name("constraint"))
10087        .is_some_and(|constraint| {
10088            constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
10089        })
10090}
10091
10092/// One entry of a callable's invocation parameter list.
10093///
10094/// The list excludes an explicit object parameter and a lone `void`, so its
10095/// length is the callable's invocation arity. Every derivation of a parameter
10096/// type - the rendered spelling used for overload discrimination and the
10097/// structured identity used by dependency-pack production - starts from this
10098/// same sequence, so the two can never disagree about which parameters exist.
10099#[derive(Clone, Copy)]
10100enum CppParameterSlot<'tree> {
10101    Declared(Node<'tree>),
10102    Ellipsis,
10103}
10104
10105fn cpp_callable_parameter_slots<'tree>(
10106    parameters_node: Node<'tree>,
10107    source: &str,
10108) -> Vec<CppParameterSlot<'tree>> {
10109    let mut slots = Vec::new();
10110    let mut cursor = parameters_node.walk();
10111    for parameter in parameters_node.children(&mut cursor) {
10112        match parameter.kind() {
10113            "parameter_declaration" | "optional_parameter_declaration" => {
10114                if cpp_parameter_is_explicit_object(parameter, source)
10115                    || (parameter.child_by_field_name("declarator").is_none()
10116                        && parameter
10117                            .child_by_field_name("type")
10118                            .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
10119                {
10120                    continue;
10121                }
10122                slots.push(CppParameterSlot::Declared(parameter));
10123            }
10124            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
10125                slots.push(CppParameterSlot::Ellipsis);
10126            }
10127            _ => {}
10128        }
10129    }
10130    slots
10131}
10132
10133fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
10134    cpp_callable_parameter_slots(parameters_node, source)
10135        .into_iter()
10136        .map(|slot| match slot {
10137            CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
10138            CppParameterSlot::Ellipsis => "...".to_string(),
10139        })
10140        .collect()
10141}
10142
10143/// One callable parameter's parser-derived type.
10144///
10145/// A rendered spelling such as `const T&` is a source text, not a type name. A
10146/// consumer that must publish a type into a structured model - a semantic-pack
10147/// type reference, for example - reads this instead.
10148#[derive(Debug, Clone, PartialEq, Eq)]
10149pub enum CppParameterType {
10150    /// The written type reduced to a structured identity. C++ cv-qualifiers
10151    /// have no place in that model and are not represented.
10152    Structured(StructuredTypeIdentity),
10153    /// A `...` pack, which declares no parameter type at all.
10154    Ellipsis,
10155    /// A written type with no structured reduction, such as a macro-obscured,
10156    /// `decltype`-computed, or function-pointer parameter.
10157    Unstructured,
10158}
10159
10160/// The structured type of each invocation parameter, in declaration order.
10161///
10162/// The result is index-parallel with the rendered
10163/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
10164/// callable.
10165pub fn cpp_callable_parameter_type_identities<'tree>(
10166    function_declarator: Node<'tree>,
10167    source: &str,
10168    ancestry: &ParentIndex<'tree>,
10169) -> Vec<CppParameterType> {
10170    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
10171        return Vec::new();
10172    };
10173    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
10174    cpp_callable_parameter_slots(parameters_node, source)
10175        .into_iter()
10176        .map(|slot| match slot {
10177            CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
10178            CppParameterSlot::Declared(parameter) => {
10179                cpp_parameter_type_identity(parameter, source, &lexical_scope)
10180                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
10181            }
10182        })
10183        .collect()
10184}
10185
10186fn cpp_parameter_type_identity(
10187    parameter: Node<'_>,
10188    source: &str,
10189    lexical_scope: &[String],
10190) -> Option<StructuredTypeIdentity> {
10191    let type_node = parameter.child_by_field_name("type")?;
10192    let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
10193    if let Some(declarator) = cpp_parameter_declarator(parameter) {
10194        for wrapper in cpp_structured_declarator_wrappers(declarator)
10195            .into_iter()
10196            .rev()
10197        {
10198            identity = cpp_wrap_structured_type(identity, wrapper)?;
10199        }
10200    }
10201    Some(identity)
10202}
10203
10204/// One callable parameter's comparable shape.
10205///
10206/// [`CppParameterType`] above answers "which type is written here" for a
10207/// structured model and deliberately records no cv-qualifiers, so it reports
10208/// the same value for `f(char *)` and `f(const char *)`. Deciding whether two
10209/// callable declarations declare one function needs the opposite trade: every
10210/// cv-qualifier that C++ counts as part of the parameter type must survive,
10211/// while the two declarations may spell the same type through different
10212/// qualifications. This slot carries that comparand.
10213///
10214/// The result is index-parallel with [`cpp_callable_parameter_type_identities`]
10215/// and with the rendered parameter spellings of the same callable.
10216#[derive(Debug, Clone, PartialEq, Eq)]
10217pub enum CppComparableSlot {
10218    /// A declared parameter reduced to its comparable shape.
10219    Shape(CppComparableParameter),
10220    /// A `...` pack, which declares no parameter type at all.
10221    Ellipsis,
10222    /// A parameter with no comparable reduction, such as a macro-obscured,
10223    /// `decltype`-computed, or function-pointer parameter.
10224    Unstructured,
10225}
10226
10227/// A parameter type as a flat arena of nodes plus a root index.
10228///
10229/// The arena carries the same rationale as [`StructuredTypeIdentity`]: source
10230/// can nest types very deeply, and cloning, comparing or dropping the value
10231/// must not consume the Rust call stack. Nodes are appended in post-order, so
10232/// every child index is smaller than its parent's and the last appended node is
10233/// the root.
10234///
10235/// That post-order append is also what makes the derived `PartialEq` a correct
10236/// structural equality: the builder below is deterministic, so one type shape
10237/// has exactly one arena layout no matter which spelling produced it. Two
10238/// shapes are equal as values iff they are equal as type trees.
10239#[derive(Debug, Clone, PartialEq, Eq)]
10240pub struct CppComparableParameter {
10241    nodes: Vec<CppComparableNode>,
10242    root: usize,
10243}
10244
10245/// One node of a [`CppComparableParameter`] arena.
10246///
10247/// `Reference` and `Array` carry no qualifiers because the grammar writes none
10248/// on them: a reference cannot be cv-qualified in C++, and an array's
10249/// qualifiers belong to its element type. A cv-qualifier written on a generic
10250/// type (`const std::vector<int>`) is recorded on the generic's base leaf,
10251/// which is the only Named node the whole spelling produces.
10252#[derive(Debug, Clone, PartialEq, Eq)]
10253pub enum CppComparableNode {
10254    Named {
10255        name: StructuredTypeName,
10256        primitive: bool,
10257        konst: bool,
10258        volatil: bool,
10259    },
10260    Pointer {
10261        inner: usize,
10262        konst: bool,
10263        volatil: bool,
10264    },
10265    Reference {
10266        inner: usize,
10267    },
10268    Array {
10269        inner: usize,
10270    },
10271    Generic {
10272        base: usize,
10273        arguments: Vec<usize>,
10274    },
10275}
10276
10277impl CppComparableParameter {
10278    pub fn root(&self) -> usize {
10279        self.root
10280    }
10281
10282    pub fn node(&self, index: usize) -> &CppComparableNode {
10283        &self.nodes[index]
10284    }
10285
10286    /// Apply the [dcl.fct]/5 parameter-type adjustments, which hold at the
10287    /// parameter's top level only.
10288    ///
10289    /// A top-level cv-qualifier is discarded, so `f(const int)` and `f(int)`
10290    /// declare one function, and a top-level array type becomes a pointer to
10291    /// its element type, so `f(int[3])` and `f(int *)` do too. The outermost
10292    /// type constructor is this arena's root, which is why both adjustments
10293    /// are one match on it: cv on an inner pointer level, on a pointee, or on
10294    /// an array element keeps distinguishing the type, and an array behind a
10295    /// pointer or reference is not a top-level array.
10296    fn adjust_parameter_top_level(&mut self) {
10297        let root = self.root;
10298        match &mut self.nodes[root] {
10299            CppComparableNode::Named { konst, volatil, .. }
10300            | CppComparableNode::Pointer { konst, volatil, .. } => {
10301                *konst = false;
10302                *volatil = false;
10303            }
10304            CppComparableNode::Array { inner } => {
10305                let inner = *inner;
10306                self.nodes[root] = CppComparableNode::Pointer {
10307                    inner,
10308                    konst: false,
10309                    volatil: false,
10310                };
10311            }
10312            CppComparableNode::Generic { base, .. } => {
10313                let base = *base;
10314                let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
10315                    unreachable!("a comparable generic's base is always a named leaf");
10316                };
10317                *konst = false;
10318                *volatil = false;
10319            }
10320            CppComparableNode::Reference { .. } => {}
10321        }
10322    }
10323}
10324
10325/// The comparable shape of each invocation parameter, in declaration order.
10326///
10327/// The result is index-parallel with
10328/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
10329/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
10330/// must treat as evidence of nothing rather than as agreement.
10331pub fn cpp_comparable_parameter_shapes<'tree>(
10332    function_declarator: Node<'tree>,
10333    source: &str,
10334    ancestry: &ParentIndex<'tree>,
10335) -> Vec<CppComparableSlot> {
10336    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
10337        return Vec::new();
10338    };
10339    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
10340    cpp_callable_parameter_slots(parameters_node, source)
10341        .into_iter()
10342        .map(|slot| match slot {
10343            CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
10344            CppParameterSlot::Declared(parameter) => {
10345                cpp_comparable_parameter(parameter, source, &lexical_scope)
10346                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
10347            }
10348        })
10349        .collect()
10350}
10351
10352fn cpp_comparable_parameter(
10353    parameter: Node<'_>,
10354    source: &str,
10355    lexical_scope: &[String],
10356) -> Option<CppComparableParameter> {
10357    let type_node = parameter.child_by_field_name("type")?;
10358    let levels = match cpp_parameter_declarator(parameter) {
10359        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
10360        None => Vec::new(),
10361    };
10362    let mut shape = cpp_comparable_type_shape(
10363        type_node,
10364        cpp_cv_qualifiers(parameter, source),
10365        levels,
10366        source,
10367        lexical_scope,
10368    )?;
10369    shape.adjust_parameter_top_level();
10370    Some(shape)
10371}
10372
10373/// The `const` and `volatile` qualifiers written as direct named children of
10374/// `node`.
10375///
10376/// The grammar exposes `type_qualifier` as a non-field named child in exactly
10377/// the three places a parameter's qualifiers can be written: on the
10378/// `parameter_declaration` itself (the base type), on a `type_descriptor`
10379/// (inside a template argument list), and on each `pointer_declarator` level
10380/// (the pointer object). Every other qualifier the grammar admits - `restrict`
10381/// and friends - takes no part in C++ type identity, the same filter
10382/// `cpp_parameter_type` applies to the rendered spelling (#1827).
10383fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
10384    let mut qualifiers = CppCvQualifiers::default();
10385    let mut cursor = node.walk();
10386    for child in node.named_children(&mut cursor) {
10387        if child.kind() != "type_qualifier" {
10388            continue;
10389        }
10390        match node_text(child, source) {
10391            "const" => qualifiers.konst = true,
10392            "volatile" => qualifiers.volatil = true,
10393            _ => {}
10394        }
10395    }
10396    qualifiers
10397}
10398
10399#[derive(Clone, Copy, Default)]
10400struct CppCvQualifiers {
10401    konst: bool,
10402    volatil: bool,
10403}
10404
10405impl CppCvQualifiers {
10406    fn union(self, other: Self) -> Self {
10407        Self {
10408            konst: self.konst || other.konst,
10409            volatil: self.volatil || other.volatil,
10410        }
10411    }
10412}
10413
10414/// One pointer, reference or array level a declarator chain adds.
10415#[derive(Clone, Copy)]
10416enum CppComparableLevel {
10417    Pointer { konst: bool, volatil: bool },
10418    Reference,
10419    Array,
10420}
10421
10422/// The levels `declarator` adds, outermost written level first.
10423///
10424/// C++ declarator syntax binds inside out: the level written closest to the
10425/// declared name is the outermost type constructor, and tree-sitter nests it
10426/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
10427/// applies in order to reach "array of pointer to int", and the qualifier of
10428/// `int * const *p` is read on the level it was written next to, the inner
10429/// pointer of the resulting type.
10430///
10431/// A declarator chain that names a function type - a function-pointer
10432/// parameter - has no comparable shape and reports `None`, matching the
10433/// structured identity channel.
10434fn cpp_comparable_declarator_levels(
10435    declarator: Node<'_>,
10436    source: &str,
10437) -> Option<Vec<CppComparableLevel>> {
10438    let mut levels = Vec::new();
10439    let mut current = declarator;
10440    loop {
10441        match current.kind() {
10442            "pointer_declarator" | "abstract_pointer_declarator" => {
10443                let qualifiers = cpp_cv_qualifiers(current, source);
10444                levels.push(CppComparableLevel::Pointer {
10445                    konst: qualifiers.konst,
10446                    volatil: qualifiers.volatil,
10447                });
10448            }
10449            "reference_declarator" | "abstract_reference_declarator" => {
10450                levels.push(CppComparableLevel::Reference);
10451            }
10452            "array_declarator" | "abstract_array_declarator" => {
10453                levels.push(CppComparableLevel::Array);
10454            }
10455            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
10456            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
10457            _ => return None,
10458        }
10459        let Some(next) = cpp_nested_declarator(current) else {
10460            return Some(levels);
10461        };
10462        current = next;
10463    }
10464}
10465
10466/// Reduce one written type to a comparable arena.
10467///
10468/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
10469/// two additions: each visited type node carries the cv-qualifiers written on
10470/// it, and declarator levels arrive as a prepared list rather than being
10471/// rediscovered inside the walk.
10472fn cpp_comparable_type_shape(
10473    type_node: Node<'_>,
10474    qualifiers: CppCvQualifiers,
10475    levels: Vec<CppComparableLevel>,
10476    source: &str,
10477    lexical_scope: &[String],
10478) -> Option<CppComparableParameter> {
10479    enum Work<'tree> {
10480        Visit {
10481            node: Node<'tree>,
10482            qualifiers: CppCvQualifiers,
10483        },
10484        ApplyLevels(Vec<CppComparableLevel>),
10485        BuildGeneric {
10486            argument_count: usize,
10487        },
10488    }
10489
10490    let mut nodes: Vec<CppComparableNode> = Vec::new();
10491    let mut values: Vec<usize> = Vec::new();
10492    let mut work = vec![
10493        Work::ApplyLevels(levels),
10494        Work::Visit {
10495            node: type_node,
10496            qualifiers,
10497        },
10498    ];
10499    while let Some(next) = work.pop() {
10500        match next {
10501            Work::Visit { node, qualifiers } => match node.kind() {
10502                "type_descriptor" => {
10503                    let inner_type = node
10504                        .child_by_field_name("type")
10505                        .or_else(|| node.named_child(0))?;
10506                    let mut cursor = node.walk();
10507                    let declarator = node.child_by_field_name("declarator").or_else(|| {
10508                        node.named_children(&mut cursor).find(|child| {
10509                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
10510                        })
10511                    });
10512                    let levels = match declarator {
10513                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
10514                        None => Vec::new(),
10515                    };
10516                    work.push(Work::ApplyLevels(levels));
10517                    work.push(Work::Visit {
10518                        node: inner_type,
10519                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
10520                    });
10521                }
10522                "sized_type_specifier" => {
10523                    // `unsigned char` is one primitive type whose components are
10524                    // partly unnamed tokens, so the whole specifier is its own
10525                    // name component. Reducing it to the `type` child would make
10526                    // `f(unsigned char)` and `f(char)` compare equal.
10527                    let name = StructuredTypeName::new(
10528                        vec![normalize_cpp_whitespace(node_text(node, source))],
10529                        lexical_scope.to_vec(),
10530                        false,
10531                    )?;
10532                    values.push(cpp_push_comparable_node(
10533                        &mut nodes,
10534                        CppComparableNode::Named {
10535                            name,
10536                            primitive: true,
10537                            konst: qualifiers.konst,
10538                            volatil: qualifiers.volatil,
10539                        },
10540                    ));
10541                }
10542                "qualified_identifier"
10543                | "scoped_identifier"
10544                | "scoped_type_identifier"
10545                | "type_identifier"
10546                | "field_identifier"
10547                | "identifier"
10548                | "namespace_identifier"
10549                | "primitive_type"
10550                | "template_type" => {
10551                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
10552                    values.push(cpp_push_comparable_node(
10553                        &mut nodes,
10554                        CppComparableNode::Named {
10555                            name,
10556                            primitive: node.kind() == "primitive_type",
10557                            konst: qualifiers.konst,
10558                            volatil: qualifiers.volatil,
10559                        },
10560                    ));
10561                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
10562                        let mut cursor = arguments_node.walk();
10563                        let arguments = arguments_node
10564                            .named_children(&mut cursor)
10565                            .filter(|child| !child.is_extra() && child.kind() != "comment")
10566                            .collect::<Vec<_>>();
10567                        work.push(Work::BuildGeneric {
10568                            argument_count: arguments.len(),
10569                        });
10570                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
10571                            node: argument,
10572                            qualifiers: CppCvQualifiers::default(),
10573                        }));
10574                    }
10575                }
10576                _ => {
10577                    let inner = node.child_by_field_name("type").or_else(|| {
10578                        (node.named_child_count() == 1)
10579                            .then(|| node.named_child(0))
10580                            .flatten()
10581                    })?;
10582                    work.push(Work::Visit {
10583                        node: inner,
10584                        qualifiers,
10585                    });
10586                }
10587            },
10588            Work::ApplyLevels(levels) => {
10589                let mut root = values.pop()?;
10590                for level in levels {
10591                    let node = match level {
10592                        CppComparableLevel::Pointer { konst, volatil } => {
10593                            CppComparableNode::Pointer {
10594                                inner: root,
10595                                konst,
10596                                volatil,
10597                            }
10598                        }
10599                        CppComparableLevel::Reference => {
10600                            CppComparableNode::Reference { inner: root }
10601                        }
10602                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
10603                    };
10604                    root = cpp_push_comparable_node(&mut nodes, node);
10605                }
10606                values.push(root);
10607            }
10608            Work::BuildGeneric { argument_count } => {
10609                let value_count = argument_count.checked_add(1)?;
10610                let start = values.len().checked_sub(value_count)?;
10611                let mut built = values.split_off(start);
10612                let base = built.remove(0);
10613                values.push(cpp_push_comparable_node(
10614                    &mut nodes,
10615                    CppComparableNode::Generic {
10616                        base,
10617                        arguments: built,
10618                    },
10619                ));
10620            }
10621        }
10622    }
10623    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
10624    debug_assert_eq!(
10625        root,
10626        nodes.len().saturating_sub(1),
10627        "comparable nodes are appended in post-order, so the root is the last one"
10628    );
10629    Some(CppComparableParameter { nodes, root })
10630}
10631
10632fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
10633    nodes.push(node);
10634    nodes.len() - 1
10635}
10636
10637/// The template argument list of the name `node` terminates in, if any.
10638///
10639/// `std::vector<int>` writes its arguments on the `name` of a qualified
10640/// identifier, so a walk that stopped at the qualified node would reduce
10641/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
10642fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
10643    let mut current = node;
10644    loop {
10645        match current.kind() {
10646            "template_type" => return current.child_by_field_name("arguments"),
10647            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
10648                current = current.child_by_field_name("name")?;
10649            }
10650            _ => return None,
10651        }
10652    }
10653}
10654
10655/// The callable declarator of the declaration that covers `start_byte`.
10656///
10657/// A consumer that holds a declaration's recorded byte position rather than its
10658/// syntax node - external header extraction, for instance - uses this to reach
10659/// the same `function_declarator` the declaration walk read.
10660pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
10661    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
10662    loop {
10663        if matches!(
10664            current.kind(),
10665            "declaration" | "field_declaration" | "function_definition"
10666        ) && let Some(declarator) = current
10667            .child_by_field_name("declarator")
10668            .and_then(extract_function_declarator)
10669        {
10670            return Some(declarator);
10671        }
10672        current = current.parent()?;
10673    }
10674}
10675
10676fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
10677    let mut labels = Vec::new();
10678    let mut cursor = parameters_node.walk();
10679    for child in parameters_node.children(&mut cursor) {
10680        match child.kind() {
10681            "parameter_declaration" | "optional_parameter_declaration" => {
10682                if let Some(name_node) = child
10683                    .child_by_field_name("declarator")
10684                    .and_then(cpp_declarator_label_node)
10685                {
10686                    labels.push(name_node);
10687                } else {
10688                    labels.push(child);
10689                }
10690            }
10691            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
10692                labels.push(child);
10693            }
10694            _ => {}
10695        }
10696    }
10697    labels
10698}
10699
10700fn cpp_signature_search_start<'tree>(
10701    signature: &str,
10702    function_declarator: Node<'tree>,
10703    source: &str,
10704    ancestry: &ParentIndex<'tree>,
10705) -> usize {
10706    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
10707        return 0;
10708    };
10709    let raw = node_text(enclosing, source);
10710    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
10711    let offset = function_declarator
10712        .start_byte()
10713        .saturating_sub(enclosing.start_byte())
10714        .saturating_sub(leading_trim_bytes);
10715    offset.min(signature.len())
10716}
10717
10718fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
10719    match node.kind() {
10720        "identifier" | "field_identifier" => Some(node),
10721        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
10722            .child_by_field_name("declarator")
10723            .or_else(|| last_named_child(node))
10724            .and_then(cpp_declarator_label_node),
10725        "array_declarator" => node
10726            .child_by_field_name("declarator")
10727            .and_then(cpp_declarator_label_node),
10728        "function_declarator" => node
10729            .child_by_field_name("declarator")
10730            .or_else(|| node.child_by_field_name("name"))
10731            .or_else(|| last_named_child(node))
10732            .and_then(cpp_declarator_label_node),
10733        _ => None,
10734    }
10735}
10736
10737fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
10738    let base_type = parameter
10739        .child_by_field_name("type")
10740        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
10741        .unwrap_or_default();
10742    let declarator = cpp_parameter_declarator(parameter);
10743    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
10744    // are discarded, so `f(const int)` and `f(int)` declare one function. A
10745    // qualifier written next to the parameter's type is only top-level when
10746    // the declarator adds no indirection; behind a pointer, reference or array
10747    // declarator the same qualifier belongs to the pointee, referent or
10748    // element and keeps distinguishing the type (#1827).
10749    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
10750    let mut cursor = parameter.walk();
10751    let qualifiers = parameter
10752        .named_children(&mut cursor)
10753        .filter(|child| child.kind() == "type_qualifier")
10754        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
10755        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
10756        .collect::<Vec<_>>()
10757        .join(" ");
10758    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
10759        (true, _) => base_type,
10760        (_, true) => qualifiers,
10761        (false, false) => format!("{qualifiers} {base_type}"),
10762    };
10763    let declarator_suffix = declarator
10764        .map(|node| cpp_declarator_suffix_without_name(node, source))
10765        .unwrap_or_default();
10766
10767    let combined = if type_text.is_empty() {
10768        declarator_suffix
10769    } else if declarator_suffix.is_empty() {
10770        type_text
10771    } else {
10772        format!("{type_text} {declarator_suffix}")
10773    };
10774    normalize_cpp_type_text(&combined)
10775}
10776
10777fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
10778    parameter.child_by_field_name("declarator").or_else(|| {
10779        // Some unnamed prototype parameters expose their abstract declarator
10780        // as a direct named child without the grammar's `declarator` field.
10781        // Recover only the structured abstract-declarator node; the parameter's
10782        // type and qualifiers are distinct children and must not be guessed from
10783        // source text.
10784        let mut cursor = parameter.walk();
10785        parameter
10786            .named_children(&mut cursor)
10787            .find(|child| is_cpp_abstract_declarator(child.kind()))
10788    })
10789}
10790
10791/// Whether a parameter's declarator chain adds indirection - a pointer,
10792/// reference, array or function declarator - to the parameter's written type.
10793pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
10794    let mut current = Some(declarator);
10795    while let Some(node) = current {
10796        if matches!(
10797            node.kind(),
10798            "pointer_declarator"
10799                | "abstract_pointer_declarator"
10800                | "reference_declarator"
10801                | "abstract_reference_declarator"
10802                | "array_declarator"
10803                | "abstract_array_declarator"
10804                | "function_declarator"
10805                | "abstract_function_declarator"
10806        ) {
10807            return true;
10808        }
10809        current = cpp_nested_declarator(node);
10810    }
10811    false
10812}
10813
10814fn is_cpp_abstract_declarator(kind: &str) -> bool {
10815    matches!(
10816        kind,
10817        "abstract_pointer_declarator"
10818            | "abstract_reference_declarator"
10819            | "abstract_array_declarator"
10820            | "abstract_function_declarator"
10821            | "abstract_parenthesized_declarator"
10822    )
10823}
10824
10825fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
10826    node.child_by_field_name("declarator").or_else(|| {
10827        if is_cpp_abstract_declarator(node.kind()) {
10828            let mut cursor = node.walk();
10829            node.named_children(&mut cursor)
10830                .find(|child| is_cpp_abstract_declarator(child.kind()))
10831        } else {
10832            // Named declarators historically use their last named child when
10833            // tree-sitter omits the field. Keep that broad fallback for
10834            // attributed, variadic, and recovered named shapes.
10835            last_named_child(node)
10836        }
10837    })
10838}
10839
10840fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
10841    match node.kind() {
10842        "identifier" | "field_identifier" => String::new(),
10843        "pointer_declarator" | "abstract_pointer_declarator" => {
10844            let inner = cpp_nested_declarator(node)
10845                .map(|child| cpp_declarator_suffix_without_name(child, source))
10846                .unwrap_or_default();
10847            format!("*{inner}")
10848        }
10849        "reference_declarator" | "abstract_reference_declarator" => {
10850            let inner = cpp_nested_declarator(node)
10851                .map(|child| cpp_declarator_suffix_without_name(child, source))
10852                .unwrap_or_default();
10853            let reference = node
10854                .children(&mut node.walk())
10855                .find(|child| matches!(child.kind(), "&" | "&&"))
10856                .map(|child| node_text(child, source))
10857                .unwrap_or("&");
10858            format!("{reference}{inner}")
10859        }
10860        "array_declarator" | "abstract_array_declarator" => {
10861            let inner = cpp_nested_declarator(node)
10862                .map(|child| cpp_declarator_suffix_without_name(child, source))
10863                .unwrap_or_default();
10864            let size = node
10865                .child_by_field_name("size")
10866                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
10867                .unwrap_or_default();
10868            format!("{inner}[{size}]")
10869        }
10870        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
10871            let inner = cpp_nested_declarator(node);
10872            inner
10873                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
10874                .unwrap_or_default()
10875        }
10876        "function_declarator" | "abstract_function_declarator" => {
10877            let inner = cpp_nested_declarator(node)
10878                .map(|child| cpp_declarator_suffix_without_name(child, source))
10879                .unwrap_or_default();
10880            let params = node
10881                .child_by_field_name("parameters")
10882                .map(|child| cpp_parameter_signature(child, source))
10883                .unwrap_or_else(|| "()".to_string());
10884            format!("{inner}{params}")
10885        }
10886        _ => {
10887            let text = normalize_cpp_whitespace(node_text(node, source));
10888            let name = extract_declarator_name(node, source);
10889            if name.is_empty() {
10890                text
10891            } else {
10892                text.replace(&name, "").trim().to_string()
10893            }
10894        }
10895    }
10896}
10897
10898fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
10899    collapse_cpp_whitespace(
10900        suffix
10901            .trim()
10902            .trim_start_matches("->")
10903            .trim_start_matches('{')
10904            .trim_end_matches(';'),
10905    )
10906}
10907
10908pub fn normalize_cpp_whitespace(value: &str) -> String {
10909    collapse_cpp_whitespace(value)
10910}
10911
10912fn normalize_cpp_type_text(value: &str) -> String {
10913    collapse_cpp_whitespace(value)
10914        .replace(", ", ",")
10915        .replace(" <", "<")
10916        .replace("< ", "<")
10917        .replace(" >", ">")
10918}
10919
10920fn collapse_cpp_whitespace(value: &str) -> String {
10921    let mut result = String::new();
10922    let mut prev_space = false;
10923    for ch in value.chars() {
10924        if ch.is_whitespace() {
10925            if !prev_space {
10926                result.push(' ');
10927            }
10928            prev_space = true;
10929        } else {
10930            result.push(ch);
10931            prev_space = false;
10932        }
10933    }
10934    result.trim().to_string()
10935}
10936
10937pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
10938    node_source_text(node, source)
10939}
10940
10941pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
10942    walk_named_tree_preorder(node, true, |node| {
10943        match node.kind() {
10944            "type_identifier" | "identifier" | "qualified_identifier" => {
10945                let text = node_text(node, source).trim();
10946                if !text.is_empty() {
10947                    identifiers.insert(text.to_string());
10948                }
10949            }
10950            _ => {}
10951        }
10952        WalkControl::Continue
10953    });
10954}
10955
10956fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
10957    node.child_by_field_name("body").or_else(|| {
10958        let mut cursor = node.walk();
10959        node.named_children(&mut cursor).find(|child| {
10960            matches!(
10961                child.kind(),
10962                "declaration_list" | "field_declaration_list" | "enumerator_list"
10963            )
10964        })
10965    })
10966}
10967
10968/// Return a class body's actual closing brace when the parser supplied one.
10969///
10970/// A malformed namespace sentinel can leave a class node carrying unrelated
10971/// parser errors even though its own class body is complete.  `has_error()` is
10972/// therefore too coarse an admission predicate for sentinel ownership.  The
10973/// body list, however, exposes the opening and closing punctuation directly;
10974/// a real (non-missing) final `}` proves that the class did not borrow the
10975/// enclosing namespace's close.  Requiring the body to end before its parent
10976/// container also rejects a recovered node whose body swallowed that outer
10977/// boundary.
10978fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
10979    if !matches!(
10980        node.kind(),
10981        "class_specifier" | "struct_specifier" | "union_specifier"
10982    ) {
10983        return None;
10984    }
10985    let body = cpp_body_node(node)?;
10986    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
10987        return None;
10988    }
10989    let open = body.child(0)?;
10990    let close = body.child(body.child_count().checked_sub(1)?)?;
10991    if open.kind() != "{"
10992        || open.is_missing()
10993        || close.kind() != "}"
10994        || close.is_missing()
10995        || close.end_byte() != body.end_byte()
10996        || body.end_byte() > node.end_byte()
10997        || node
10998            .parent()
10999            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
11000    {
11001        return None;
11002    }
11003    Some(close)
11004}
11005
11006fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
11007    if node.kind() == "namespace_definition" {
11008        return true;
11009    }
11010    let mut cursor = node.walk();
11011    node.named_children(&mut cursor)
11012        .any(cpp_contains_namespace_definition)
11013}
11014
11015struct CppNestedNamespaceSentinel<'tree> {
11016    function: Node<'tree>,
11017    body: Node<'tree>,
11018    namespace_components: Vec<String>,
11019}
11020
11021/// Owned structural recovery metadata for a namespace-sentinel region.
11022///
11023/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
11024/// instead of the namespace/class scopes that the declaration visitor restores.
11025/// The inverted usage walk has the original CST, so it needs the same ownership
11026/// evidence without borrowing parser nodes across its file scan.  Keep this
11027/// descriptor deliberately source-range based: callers can match a reference
11028/// node by containment and then resolve its structured type spelling in the
11029/// recovered class scope.
11030#[derive(Debug, Clone)]
11031pub struct CppSentinelRecoveredOwner {
11032    pub range: Range,
11033    /// Start of the qualified owner name (`btree<P>::method`).  A leading
11034    /// return type before this byte is looked up from the namespace; parameters,
11035    /// trailing returns, and the body use the member owner scope.
11036    pub owner_name_start_byte: usize,
11037    /// Number of leading components belonging to the namespace rather than
11038    /// the qualified class owner.  A leading return type is looked up before
11039    /// every owner component, not merely before the innermost class.
11040    pub namespace_component_count: usize,
11041    pub scope_components: Vec<String>,
11042}
11043
11044#[derive(Debug, Clone)]
11045pub struct CppSentinelRecoveredClass {
11046    pub namespace_range: Range,
11047    pub namespace_scope_components: Vec<String>,
11048    pub class_range: Range,
11049    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
11050    pub scope_components: Vec<String>,
11051    /// Qualified out-of-line member definitions owned by this class.  Their
11052    /// ranges may extend beyond `class_range` when the malformed sentinel
11053    /// swallowed the namespace close and left definitions as function siblings.
11054    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
11055}
11056
11057/// Resolve the lexical scope restored for a node in a malformed
11058/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
11059/// outrank class spans, which in turn outrank the surviving namespace body.
11060/// The class ancestor suffix is recovered from the original CST so nested
11061/// members keep their complete `Outer::Inner` owner chain.
11062pub fn cpp_sentinel_recovered_scope_for_node(
11063    node: Node<'_>,
11064    source: &str,
11065    recovered_classes: &[CppSentinelRecoveredClass],
11066) -> Option<Vec<String>> {
11067    let contains =
11068        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
11069    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
11070    for recovered in recovered_classes {
11071        for owner in recovered
11072            .owner_ranges
11073            .iter()
11074            .filter(|owner| contains(owner.range))
11075        {
11076            let replace = best_owner.is_none_or(|existing| {
11077                owner.range.end_byte.saturating_sub(owner.range.start_byte)
11078                    < existing
11079                        .range
11080                        .end_byte
11081                        .saturating_sub(existing.range.start_byte)
11082            });
11083            if replace {
11084                best_owner = Some(owner);
11085            }
11086        }
11087    }
11088    if let Some(owner) = best_owner {
11089        let mut scope = owner.scope_components.clone();
11090        if node.start_byte() < owner.owner_name_start_byte {
11091            scope.truncate(owner.namespace_component_count);
11092        }
11093        return Some(scope);
11094    }
11095
11096    let class = recovered_classes
11097        .iter()
11098        .filter(|recovered| contains(recovered.class_range))
11099        .min_by_key(|recovered| {
11100            recovered
11101                .class_range
11102                .end_byte
11103                .saturating_sub(recovered.class_range.start_byte)
11104        });
11105    let class_scope = class.is_some();
11106    let mut scope = if let Some(class) = class {
11107        class.scope_components.clone()
11108    } else {
11109        let namespace = recovered_classes
11110            .iter()
11111            .filter(|recovered| contains(recovered.namespace_range))
11112            .min_by_key(|recovered| {
11113                recovered
11114                    .namespace_range
11115                    .end_byte
11116                    .saturating_sub(recovered.namespace_range.start_byte)
11117            })?;
11118        let mut scope = namespace.namespace_scope_components.clone();
11119        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
11120        let common_prefix = scope
11121            .iter()
11122            .zip(&parser_namespace)
11123            .take_while(|(recovered, parser)| recovered == parser)
11124            .count();
11125        scope.extend(parser_namespace.into_iter().skip(common_prefix));
11126        scope
11127    };
11128    if class_scope {
11129        let mut ancestor_components = Vec::new();
11130        let mut ancestor = node.parent();
11131        while let Some(current) = ancestor {
11132            if matches!(
11133                current.kind(),
11134                "class_specifier" | "struct_specifier" | "union_specifier"
11135            ) && let Some(name) = current.child_by_field_name("name")
11136                && let Some(name_components) = cpp_name_components(name, source)
11137            {
11138                ancestor_components.push(
11139                    name_components
11140                        .into_iter()
11141                        .map(|component| component.name)
11142                        .collect::<Vec<_>>(),
11143                );
11144            }
11145            ancestor = current.parent();
11146        }
11147        ancestor_components.reverse();
11148        let base_len = scope.len();
11149        for component in ancestor_components.into_iter().flatten() {
11150            if scope.len() >= base_len && scope.last() == Some(&component) {
11151                continue;
11152            }
11153            scope.push(component);
11154        }
11155    }
11156    Some(scope)
11157}
11158
11159struct CppSentinelFragmentedClassTail<'tree> {
11160    class_node: Node<'tree>,
11161    template_node: Option<Node<'tree>>,
11162    name: String,
11163    raw_supertypes: Option<Vec<String>>,
11164    fragmented: FragmentedExportBody,
11165    consumed_start: usize,
11166}
11167
11168struct CppSentinelFragmentedClassErrorPrefix<'tree> {
11169    name: String,
11170    open: Node<'tree>,
11171    raw_supertypes: Option<Vec<String>>,
11172}
11173
11174struct CppSentinelDirectBodyClassRegion {
11175    namespace_components: Vec<String>,
11176    class_start: usize,
11177    class_start_line: usize,
11178    class_close_end: usize,
11179    class_close_line: usize,
11180    name: String,
11181}
11182
11183fn cpp_sentinel_body_class_candidate<'tree>(
11184    child: Node<'tree>,
11185) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
11186    if matches!(
11187        child.kind(),
11188        "class_specifier" | "struct_specifier" | "union_specifier"
11189    ) {
11190        return Some((child, None));
11191    }
11192    if child.kind() != "template_declaration" {
11193        if child.kind() == "declaration" {
11194            return Some((first_class_like_child(child)?, None));
11195        }
11196        return None;
11197    }
11198    let mut cursor = child.walk();
11199    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
11200        if matches!(
11201            candidate.kind(),
11202            "class_specifier" | "struct_specifier" | "union_specifier"
11203        ) {
11204            Some(candidate)
11205        } else if candidate.kind() == "declaration" {
11206            first_class_like_child(candidate)
11207        } else {
11208            None
11209        }
11210    })?;
11211    Some((class_node, Some(child)))
11212}
11213
11214/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
11215/// namespace-sentinel body when a later member macro ends the bogus sentinel
11216/// function before the real class close. The anonymous class/open tokens and
11217/// direct identifier are the structural proof; a retained direct close would
11218/// be an ordinary malformed class rather than the fragmented tail handled here.
11219fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
11220    node: Node<'tree>,
11221    source: &str,
11222) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
11223    let name = malformed_class_error_owner_name(node, source)?;
11224    let mut cursor = node.walk();
11225    let children = node.children(&mut cursor).collect::<Vec<_>>();
11226    let keyword = children.first()?;
11227    let open_index = children.iter().position(|child| child.kind() == "{")?;
11228    if children[open_index + 1..]
11229        .iter()
11230        .any(|child| child.kind() == "}")
11231    {
11232        return None;
11233    }
11234    let raw_supertypes =
11235        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
11236    Some(CppSentinelFragmentedClassErrorPrefix {
11237        name,
11238        open: children[open_index],
11239        raw_supertypes,
11240    })
11241}
11242
11243fn cpp_sentinel_direct_body_class_candidate<'tree>(
11244    child: Node<'tree>,
11245) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
11246    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
11247        return Some(candidate);
11248    }
11249    if child.kind() != "template_declaration" {
11250        return None;
11251    }
11252    let mut cursor = child.walk();
11253    let wrapper = child
11254        .named_children(&mut cursor)
11255        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
11256    Some((first_class_like_child(wrapper)?, Some(child)))
11257}
11258
11259fn cpp_sentinel_direct_namespace_components(
11260    function: Node<'_>,
11261    body: Node<'_>,
11262    source: &str,
11263) -> Option<Vec<String>> {
11264    let mut cursor = function.walk();
11265    let children = function
11266        .named_children(&mut cursor)
11267        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
11268        .collect::<Vec<_>>();
11269    let sentinel_index = children.iter().rposition(|child| {
11270        direct_identifier_name(*child, source)
11271            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
11272    })?;
11273    let mut identifiers = Vec::new();
11274    let mut stack = children[sentinel_index + 1..]
11275        .iter()
11276        .rev()
11277        .copied()
11278        .collect::<Vec<_>>();
11279    while let Some(current) = stack.pop() {
11280        if let Some(name) = direct_identifier_name(current, source) {
11281            identifiers.push(name);
11282            continue;
11283        }
11284        let mut cursor = current.walk();
11285        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
11286        stack.extend(children.into_iter().rev());
11287    }
11288    let [keyword, namespace] = identifiers.as_slice() else {
11289        return None;
11290    };
11291    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
11292        .then(|| vec![namespace.clone()])
11293}
11294
11295fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
11296    let mut sibling = class_semicolon.next_named_sibling();
11297    let namespace_close = loop {
11298        let Some(current) = sibling else {
11299            return false;
11300        };
11301        sibling = current.next_named_sibling();
11302        if current.kind() != "comment" {
11303            break current;
11304        }
11305    };
11306    if !cpp_is_stray_close_brace(namespace_close, source) {
11307        return false;
11308    }
11309    loop {
11310        let Some(current) = sibling else {
11311            return false;
11312        };
11313        sibling = current.next_named_sibling();
11314        if current.kind() == "comment" {
11315            continue;
11316        }
11317        return direct_identifier_name(current, source)
11318            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
11319    }
11320}
11321
11322fn cpp_sentinel_macro_body_class_region<'tree>(
11323    node: Node<'tree>,
11324    source: &str,
11325    ancestry: &ParentIndex<'tree>,
11326) -> Option<CppSentinelDirectBodyClassRegion> {
11327    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
11328        return None;
11329    };
11330    if node.kind() != "function_definition" || !node.has_error() {
11331        return None;
11332    }
11333    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
11334    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
11335    let mut cursor = body.walk();
11336    let candidates = body
11337        .named_children(&mut cursor)
11338        .filter_map(cpp_sentinel_direct_body_class_candidate)
11339        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
11340        .collect::<Vec<_>>();
11341    let [(class_node, template_node)] = candidates.as_slice() else {
11342        return None;
11343    };
11344    let original_body = cpp_body_node(*class_node)?;
11345    let name = class_like_name(*class_node, source, ancestry)?;
11346    if name.is_empty() || cpp_export_macro_token(&name) {
11347        return None;
11348    }
11349
11350    let mut sibling = node.next_named_sibling();
11351    let (class_close_start, class_close_end, class_close_line) = loop {
11352        let current = sibling?;
11353        let next = current.next_named_sibling();
11354        if cpp_is_stray_close_brace(current, source)
11355            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
11356        {
11357            let semicolon = next.expect("checked above");
11358            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
11359                return None;
11360            }
11361            break (
11362                current.start_byte(),
11363                semicolon.end_byte(),
11364                semicolon.end_position().row + 1,
11365            );
11366        }
11367        sibling = next;
11368    };
11369    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
11370    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
11371    let root = tree.root_node();
11372    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
11373    // The region reparse is its own tree, so it needs its own parent index;
11374    // the caller's index answers nothing about these nodes.
11375    let reparsed_ancestry = ParentIndex::new(root);
11376    let reparsed =
11377        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
11378    if reparsed.name != name
11379        || reparsed.declaration_node.start_byte() != class_node.start_byte()
11380        || reparsed.body.start_byte() != original_body.start_byte()
11381        || class_close_start <= reparsed.body.end_byte()
11382        || class_close_end <= class_node.end_byte()
11383    {
11384        return None;
11385    }
11386    Some(CppSentinelDirectBodyClassRegion {
11387        namespace_components,
11388        class_start: reparse_start,
11389        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
11390            node.start_position().row + 1
11391        }),
11392        class_close_end,
11393        class_close_line,
11394        name,
11395    })
11396}
11397
11398/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
11399/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
11400///
11401/// The parser puts the namespace opener and the malformed function in one root
11402/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
11403/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
11404/// the malformed function must begin with an all-caps type, then an ERROR whose
11405/// sole identifier is `namespace`, followed by the inner namespace identifier
11406/// and a compound body; and that body must contain a complete named class or a
11407/// structurally fragmented class prefix. A text reparse cannot prove any of
11408/// those ownership boundaries.
11409fn cpp_nested_namespace_sentinel<'tree>(
11410    node: Node<'tree>,
11411    source: &str,
11412    ancestry: &ParentIndex<'tree>,
11413) -> Option<CppNestedNamespaceSentinel<'tree>> {
11414    if !node.has_error() {
11415        return None;
11416    }
11417
11418    let (function, mut namespace_components) = if node.kind() == "ERROR" {
11419        let mut cursor = node.walk();
11420        let functions = node
11421            .named_children(&mut cursor)
11422            .filter(|child| child.kind() == "function_definition")
11423            .collect::<Vec<_>>();
11424        let [function] = functions.as_slice() else {
11425            return None;
11426        };
11427        if !function.has_error() {
11428            return None;
11429        }
11430        let mut cursor = node.walk();
11431        let children = node.children(&mut cursor).collect::<Vec<_>>();
11432        let function_index = children
11433            .iter()
11434            .position(|child| same_node(*child, *function))?;
11435        let [outer_keyword, outer_name, outer_open] =
11436            children.get(function_index.checked_sub(3)?..function_index)?
11437        else {
11438            return None;
11439        };
11440        if outer_keyword.kind() != "namespace"
11441            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
11442            || outer_open.kind() != "{"
11443        {
11444            return None;
11445        }
11446        (
11447            *function,
11448            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
11449        )
11450    } else if node.kind() == "function_definition" {
11451        let declaration_list = node.parent()?;
11452        let namespace = declaration_list.parent()?;
11453        if declaration_list.kind() != "declaration_list"
11454            || namespace.kind() != "namespace_definition"
11455            || namespace.child_by_field_name("body") != Some(declaration_list)
11456        {
11457            return None;
11458        }
11459        (node, Vec::new())
11460    } else {
11461        return None;
11462    };
11463
11464    let mut cursor = function.walk();
11465    let named = function
11466        .named_children(&mut cursor)
11467        .filter(|child| child.kind() != "comment")
11468        .collect::<Vec<_>>();
11469    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
11470        return None;
11471    };
11472    if first_type.kind() != "type_identifier" {
11473        return None;
11474    }
11475    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
11476    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
11477        return None;
11478    }
11479    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
11480        return None;
11481    }
11482    let inner_keyword = inner_error.named_child(0)?;
11483    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
11484        return None;
11485    }
11486    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
11487        return None;
11488    }
11489    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
11490    if inner_name.is_empty() || body.kind() != "compound_statement" {
11491        return None;
11492    }
11493    namespace_components.push(inner_name);
11494
11495    let mut cursor = body.walk();
11496    let has_complete_class = body.named_children(&mut cursor).any(|child| {
11497        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
11498            cpp_body_node(class_node).is_some()
11499                && class_like_name(class_node, source, ancestry)
11500                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
11501        })
11502    });
11503    if !has_complete_class
11504        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
11505    {
11506        return None;
11507    }
11508
11509    Some(CppNestedNamespaceSentinel {
11510        function,
11511        body: *body,
11512        namespace_components,
11513    })
11514}
11515
11516/// Recognize a namespace-begin sentinel directly beneath the translation unit.
11517///
11518/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
11519/// function whose type is the sentinel, whose declarator is the structured
11520/// qualified name `namespace::a::b`, and whose body contains the namespace
11521/// items. Declaration indexing already reparses this bounded region. The
11522/// inverse scanner retains the original tree, so recover the same namespace
11523/// components from the declarator fields for its lexical-scope metadata.
11524fn cpp_root_namespace_sentinel<'tree>(
11525    node: Node<'tree>,
11526    source: &str,
11527    ancestry: &ParentIndex<'tree>,
11528) -> Option<CppNestedNamespaceSentinel<'tree>> {
11529    if node.kind() != "function_definition"
11530        || !node.has_error()
11531        || node.parent()?.kind() != "translation_unit"
11532    {
11533        return None;
11534    }
11535    let first_type = node.child_by_field_name("type")?;
11536    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
11537    if first_type.kind() != "type_identifier"
11538        || sentinel.is_empty()
11539        || !cpp_export_macro_token(&sentinel)
11540    {
11541        return None;
11542    }
11543    let declarator = node.child_by_field_name("declarator")?;
11544    let body = node.child_by_field_name("body")?;
11545    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
11546        return None;
11547    }
11548    let mut cursor = node.walk();
11549    let named = node
11550        .named_children(&mut cursor)
11551        .filter(|child| child.kind() != "comment")
11552        .collect::<Vec<_>>();
11553    let [named_type, named_declarator, named_body] = named.as_slice() else {
11554        return None;
11555    };
11556    if !same_node(*named_type, first_type)
11557        || !same_node(*named_declarator, declarator)
11558        || !same_node(*named_body, body)
11559    {
11560        return None;
11561    }
11562    let mut declarator_components = Vec::new();
11563    let mut valid_components = true;
11564    walk_named_tree_preorder(declarator, true, |component| {
11565        if !matches!(
11566            component.kind(),
11567            "identifier" | "namespace_identifier" | "type_identifier"
11568        ) {
11569            return WalkControl::Continue;
11570        }
11571        let Some(component) = canonical_cpp_qualified_component(component, source) else {
11572            valid_components = false;
11573            return WalkControl::Break;
11574        };
11575        declarator_components.push(component.name);
11576        WalkControl::SkipChildren
11577    });
11578    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
11579        return None;
11580    }
11581    declarator_components.remove(0);
11582    let namespace_components = declarator_components;
11583    if namespace_components.is_empty()
11584        || namespace_components
11585            .iter()
11586            .any(|component| component.is_empty() || cpp_export_macro_token(component))
11587    {
11588        return None;
11589    }
11590
11591    let mut cursor = body.walk();
11592    let has_complete_class = body.named_children(&mut cursor).any(|child| {
11593        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
11594            cpp_body_node(class_node).is_some()
11595                && class_like_name(class_node, source, ancestry)
11596                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
11597        })
11598    });
11599    if !has_complete_class
11600        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
11601    {
11602        return None;
11603    }
11604
11605    Some(CppNestedNamespaceSentinel {
11606        function: node,
11607        body,
11608        namespace_components,
11609    })
11610}
11611
11612/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
11613/// malformed namespace-sentinel function.  The recovery is deliberately
11614/// structural: the class must be a direct body item, its own class node must be
11615/// erroneous and end before a unique anonymous `}` in the enclosing
11616/// declaration-list, and that namespace's next sibling must be a standalone
11617/// `;`.  The complete interior must pass the existing member-shaped reparse
11618/// gate. This avoids source brace scans and does not borrow a close from an
11619/// unrelated later declaration.
11620fn cpp_sentinel_fragmented_class_tail<'tree>(
11621    function: Node<'tree>,
11622    body: Node<'tree>,
11623    source: &str,
11624    ancestry: &ParentIndex<'tree>,
11625) -> Option<CppSentinelFragmentedClassTail<'tree>> {
11626    let mut cursor = body.walk();
11627    let candidates = body
11628        .named_children(&mut cursor)
11629        .filter_map(|child| {
11630            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
11631                let class_body = cpp_body_node(class_node)?;
11632                if !class_node.has_error() {
11633                    return None;
11634                }
11635                let name = class_like_name(class_node, source, ancestry)?;
11636                let raw_supertypes =
11637                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
11638                        .then(|| extract_cpp_supertypes(class_node, source));
11639                return Some((
11640                    class_node,
11641                    template_node,
11642                    name,
11643                    class_body,
11644                    class_body.start_byte().checked_add(1)?,
11645                    raw_supertypes,
11646                ));
11647            }
11648            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
11649            Some((
11650                child,
11651                None,
11652                prefix.name,
11653                prefix.open,
11654                prefix.open.end_byte(),
11655                prefix.raw_supertypes,
11656            ))
11657        })
11658        .collect::<Vec<_>>();
11659    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
11660        candidates.as_slice()
11661    else {
11662        return None;
11663    };
11664    if name.is_empty() || cpp_export_macro_token(name) {
11665        return None;
11666    }
11667
11668    let (close, semicolon) =
11669        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
11670
11671    let reparse_end = close.start_byte();
11672    if *reparse_start >= reparse_end {
11673        return None;
11674    }
11675    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
11676    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
11677        return None;
11678    }
11679    let class_range = Range {
11680        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
11681        end_byte: semicolon.end_byte(),
11682        start_line: template_node.map_or(class_node.start_position().row, |node| {
11683            node.start_position().row
11684        }) + 1,
11685        end_line: semicolon.end_position().row + 1,
11686    };
11687    Some(CppSentinelFragmentedClassTail {
11688        class_node: *class_node,
11689        template_node: *template_node,
11690        name: name.clone(),
11691        raw_supertypes: raw_supertypes.clone(),
11692        fragmented: FragmentedExportBody {
11693            reparse_start: *reparse_start,
11694            reparse_end,
11695            class_range,
11696        },
11697        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
11698    })
11699}
11700
11701/// Recover the class and out-of-line owner scopes from every malformed
11702/// namespace-sentinel region in `root`.
11703///
11704/// This is the shared structural counterpart to
11705/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
11706/// reuses the visitor's sentinel/class admission predicates instead of parsing
11707/// source text a second time.  The returned values own only ranges and names, so
11708/// they can be retained by an inverted usage scan after the tree borrow ends.
11709pub fn cpp_sentinel_recovered_classes(
11710    root: Node<'_>,
11711    source: &str,
11712) -> Vec<CppSentinelRecoveredClass> {
11713    if !root.has_error() {
11714        return Vec::new();
11715    }
11716    // This scan owns its walk of `root`, so it owns the parent index that walk
11717    // asks its ancestor questions through. Built after the error gate: a clean
11718    // tree returns without paying for one.
11719    let ancestry = ParentIndex::new(root);
11720    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
11721    let mut stack = vec![root];
11722    while let Some(current) = stack.pop() {
11723        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
11724            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
11725        {
11726            let namespace_components = cpp_sentinel_recovered_namespace_components(
11727                recovered.function,
11728                &recovered.namespace_components,
11729                source,
11730            );
11731            let fragmented = cpp_sentinel_fragmented_class_tail(
11732                recovered.function,
11733                recovered.body,
11734                source,
11735                &ancestry,
11736            );
11737            let mut class_candidates = Vec::new();
11738            let mut cursor = recovered.body.walk();
11739            for (class_node, template_node) in recovered
11740                .body
11741                .named_children(&mut cursor)
11742                .filter_map(cpp_sentinel_body_class_candidate)
11743            {
11744                let Some(name) = class_like_name(class_node, source, &ancestry) else {
11745                    continue;
11746                };
11747                if name.is_empty() || cpp_export_macro_token(&name) {
11748                    continue;
11749                }
11750                let is_fragmented = fragmented
11751                    .as_ref()
11752                    .is_some_and(|tail| same_node(tail.class_node, class_node));
11753                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
11754                    continue;
11755                }
11756                let class_range = if is_fragmented {
11757                    fragmented
11758                        .as_ref()
11759                        .map(|tail| tail.fragmented.class_range)
11760                        .expect("fragmented class range is present when class matches")
11761                } else {
11762                    cpp_declaration_range(template_node.unwrap_or(class_node))
11763                };
11764                class_candidates.push((class_range, name));
11765            }
11766            if let Some(fragmented) = fragmented
11767                .as_ref()
11768                .filter(|tail| tail.class_node.kind() == "ERROR")
11769            {
11770                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
11771            }
11772
11773            let mut owner_ranges =
11774                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
11775            cpp_sentinel_extend_unique_owner_ranges(
11776                &mut owner_ranges,
11777                cpp_sentinel_recovered_sibling_owner_ranges(
11778                    recovered.function,
11779                    &namespace_components,
11780                    source,
11781                ),
11782            );
11783            for (class_range, name) in class_candidates {
11784                push_cpp_sentinel_recovered_class(
11785                    &mut recovered_classes,
11786                    cpp_declaration_range(recovered.body),
11787                    &namespace_components,
11788                    class_range,
11789                    name,
11790                    &owner_ranges,
11791                );
11792            }
11793
11794            if let Some(declaration_list) = recovered
11795                .function
11796                .parent()
11797                .filter(|parent| parent.kind() == "declaration_list")
11798            {
11799                let outer_namespace =
11800                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
11801                push_cpp_sentinel_sibling_classes(
11802                    &mut recovered_classes,
11803                    declaration_list,
11804                    recovered.function,
11805                    &outer_namespace,
11806                    source,
11807                    &ancestry,
11808                );
11809            }
11810        } else if let Some(region) =
11811            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
11812        {
11813            let namespace_components = cpp_sentinel_recovered_namespace_components(
11814                current,
11815                &region.namespace_components,
11816                source,
11817            );
11818            let owner_container = current
11819                .parent()
11820                .filter(|parent| parent.kind() == "declaration_list")
11821                .unwrap_or(current);
11822            let owner_ranges =
11823                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
11824            push_cpp_sentinel_recovered_class(
11825                &mut recovered_classes,
11826                cpp_declaration_range(owner_container),
11827                &namespace_components,
11828                Range {
11829                    start_byte: region.class_start,
11830                    end_byte: region.class_close_end,
11831                    start_line: region.class_start_line,
11832                    end_line: region.class_close_line,
11833                },
11834                region.name,
11835                &owner_ranges,
11836            );
11837        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
11838            // A generic sentinel-prefixed class can be reduced as a malformed
11839            // function/ERROR without the explicit `namespace X` token pair.
11840            // Reuse the declaration visitor's bounded reparse and retain only
11841            // the recovered class identity/range here.
11842            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
11843                region;
11844            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
11845                continue;
11846            };
11847            let root = tree.root_node();
11848            let template_node = cpp_sentinel_reparsed_leading_template(root);
11849            // A region reparse is its own tree and needs its own parent index.
11850            let reparsed_ancestry = ParentIndex::new(root);
11851            let Some(reparsed_class) =
11852                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
11853            else {
11854                continue;
11855            };
11856            let class_node = reparsed_class.declaration_node;
11857            let name = reparsed_class.name;
11858            let namespace_components =
11859                cpp_sentinel_recovered_namespace_components(current, &[], source);
11860            let owner_container = current
11861                .parent()
11862                .filter(|parent| parent.kind() == "declaration_list")
11863                .unwrap_or(current);
11864            let mut owner_ranges =
11865                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
11866            cpp_sentinel_extend_unique_owner_ranges(
11867                &mut owner_ranges,
11868                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
11869            );
11870            push_cpp_sentinel_recovered_class(
11871                &mut recovered_classes,
11872                cpp_declaration_range(owner_container),
11873                &namespace_components,
11874                Range {
11875                    start_byte: class_start,
11876                    end_byte: close_end,
11877                    start_line: class_node.start_position().row + 1,
11878                    end_line: class_node.end_position().row + 1,
11879                },
11880                name,
11881                &owner_ranges,
11882            );
11883            if owner_container.kind() == "declaration_list" {
11884                push_cpp_sentinel_sibling_classes(
11885                    &mut recovered_classes,
11886                    owner_container,
11887                    current,
11888                    &namespace_components,
11889                    source,
11890                    &ancestry,
11891                );
11892            }
11893        }
11894
11895        let mut cursor = current.walk();
11896        stack.extend(current.named_children(&mut cursor));
11897    }
11898    // A shallower sentinel can expose nested classes as apparent namespace
11899    // siblings even after a deeper sentinel proves that a containing class
11900    // owns their ranges. Drop those shadow descriptors; scope recovery starts
11901    // from the proven containing class and appends parser-visible class
11902    // ancestors, preserving the full `Outer::Inner` chain.
11903    let shadowed = recovered_classes
11904        .iter()
11905        .map(|candidate| {
11906            recovered_classes.iter().any(|container| {
11907                container.class_range.start_byte <= candidate.class_range.start_byte
11908                    && container.class_range.end_byte >= candidate.class_range.end_byte
11909                    && container.class_range != candidate.class_range
11910                    && container.namespace_scope_components.len()
11911                        > candidate.namespace_scope_components.len()
11912                    && container
11913                        .namespace_scope_components
11914                        .starts_with(&candidate.namespace_scope_components)
11915            })
11916        })
11917        .collect::<Vec<_>>();
11918    let mut index = 0usize;
11919    recovered_classes.retain(|_| {
11920        let keep = !shadowed[index];
11921        index += 1;
11922        keep
11923    });
11924    recovered_classes
11925}
11926
11927/// A flat sentinel can swallow the first class while leaving later classes and
11928/// their out-of-line definitions as ordinary declaration-list siblings.  Once
11929/// the malformed class proves the sentinel envelope, retain those structurally
11930/// complete sibling classes under the same surviving namespace so every member
11931/// owner in the region uses one recovery contract.
11932fn push_cpp_sentinel_sibling_classes<'tree>(
11933    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
11934    declaration_list: Node<'tree>,
11935    sentinel_node: Node<'tree>,
11936    namespace_components: &[String],
11937    source: &str,
11938    ancestry: &ParentIndex<'tree>,
11939) {
11940    let owner_ranges =
11941        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
11942    let namespace_range = cpp_declaration_range(declaration_list);
11943    let mut cursor = declaration_list.walk();
11944    for (class_node, template_node) in declaration_list
11945        .named_children(&mut cursor)
11946        .filter(|child| !same_node(*child, sentinel_node))
11947        .filter_map(cpp_sentinel_body_class_candidate)
11948    {
11949        let Some(name) = class_like_name(class_node, source, ancestry) else {
11950            continue;
11951        };
11952        if name.is_empty()
11953            || cpp_export_macro_token(&name)
11954            || cpp_complete_class_body_close(class_node).is_none()
11955        {
11956            continue;
11957        }
11958        push_cpp_sentinel_recovered_class(
11959            recovered_classes,
11960            namespace_range,
11961            namespace_components,
11962            cpp_declaration_range(template_node.unwrap_or(class_node)),
11963            name,
11964            &owner_ranges,
11965        );
11966    }
11967}
11968
11969fn push_cpp_sentinel_recovered_class(
11970    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
11971    namespace_range: Range,
11972    namespace_components: &[String],
11973    class_range: Range,
11974    name: String,
11975    owner_ranges: &[CppSentinelRecoveredOwner],
11976) {
11977    let mut scope_components = namespace_components.to_vec();
11978    scope_components.push(name);
11979    let owner_ranges = owner_ranges
11980        .iter()
11981        .filter(|owner| owner.scope_components.starts_with(&scope_components))
11982        .cloned()
11983        .collect::<Vec<_>>();
11984    if recovered_classes.iter().any(|existing| {
11985        existing.class_range == class_range && existing.scope_components == scope_components
11986    }) {
11987        return;
11988    }
11989    recovered_classes.push(CppSentinelRecoveredClass {
11990        namespace_range,
11991        namespace_scope_components: namespace_components.to_vec(),
11992        class_range,
11993        scope_components,
11994        owner_ranges,
11995    });
11996}
11997
11998fn cpp_sentinel_recovered_namespace_components(
11999    function: Node<'_>,
12000    recovered_components: &[String],
12001    source: &str,
12002) -> Vec<String> {
12003    let mut ancestor_components = Vec::new();
12004    let mut ancestor = function.parent();
12005    while let Some(current) = ancestor {
12006        if current.kind() == "namespace_definition"
12007            && let Some(name_node) = current.child_by_field_name("name")
12008            && let Some(components) = cpp_name_components(name_node, source)
12009        {
12010            ancestor_components.push(
12011                components
12012                    .into_iter()
12013                    .map(|component| component.name)
12014                    .collect::<Vec<_>>(),
12015            );
12016        }
12017        ancestor = current.parent();
12018    }
12019    ancestor_components.reverse();
12020    let mut ancestors = ancestor_components
12021        .into_iter()
12022        .flatten()
12023        .collect::<Vec<_>>();
12024
12025    let overlap = (0..=ancestors.len().min(recovered_components.len()))
12026        .rev()
12027        .find(|length| {
12028            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
12029        })
12030        .unwrap_or(0);
12031    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
12032    ancestors
12033}
12034
12035fn cpp_sentinel_recovered_owner_ranges(
12036    body: Node<'_>,
12037    namespace_components: &[String],
12038    source: &str,
12039) -> Vec<CppSentinelRecoveredOwner> {
12040    let mut owners = Vec::new();
12041    walk_named_tree_preorder(body, true, |node| {
12042        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
12043    });
12044    owners
12045}
12046
12047fn cpp_sentinel_collect_owner_range(
12048    node: Node<'_>,
12049    namespace_components: &[String],
12050    source: &str,
12051    owners: &mut Vec<CppSentinelRecoveredOwner>,
12052) -> WalkControl {
12053    if node.kind() != "function_definition" {
12054        return WalkControl::Continue;
12055    }
12056    let Some(function_declarator) = extract_function_declarator(node) else {
12057        return WalkControl::Continue;
12058    };
12059    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
12060        return WalkControl::Continue;
12061    };
12062    let Some(mut components) = cpp_name_components(name_node, source) else {
12063        return WalkControl::Continue;
12064    };
12065    if components.len() <= 1 {
12066        return WalkControl::Continue;
12067    }
12068    components.pop();
12069    let mut owner_components = components
12070        .into_iter()
12071        .map(|component| component.name)
12072        .collect::<Vec<_>>();
12073    let overlap = (0..=namespace_components.len().min(owner_components.len()))
12074        .rev()
12075        .find(|length| {
12076            owner_components[..*length]
12077                == namespace_components[namespace_components.len().saturating_sub(*length)..]
12078        })
12079        .unwrap_or(0);
12080    let mut scope_components = namespace_components.to_vec();
12081    scope_components.extend(owner_components.drain(overlap..));
12082    if scope_components.len() <= namespace_components.len() {
12083        return WalkControl::Continue;
12084    }
12085    let range = cpp_declaration_range(node);
12086    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
12087        existing.range == range && existing.scope_components == scope_components
12088    }) {
12089        owners.push(CppSentinelRecoveredOwner {
12090            range,
12091            owner_name_start_byte: name_node.start_byte(),
12092            namespace_component_count: namespace_components.len(),
12093            scope_components,
12094        });
12095    }
12096    WalkControl::Continue
12097}
12098
12099fn cpp_sentinel_extend_unique_owner_ranges(
12100    owners: &mut Vec<CppSentinelRecoveredOwner>,
12101    additional: Vec<CppSentinelRecoveredOwner>,
12102) {
12103    for owner in additional {
12104        if !owners.iter().any(|existing| {
12105            existing.range == owner.range && existing.scope_components == owner.scope_components
12106        }) {
12107            owners.push(owner);
12108        }
12109    }
12110}
12111
12112fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
12113    if node.kind() != "ERROR" || node.named_child_count() != 1 {
12114        return false;
12115    }
12116    let Some(end_name) = node.named_child(0) else {
12117        return false;
12118    };
12119    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
12120        return false;
12121    }
12122    let mut cursor = node.walk();
12123    node.children(&mut cursor)
12124        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
12125}
12126
12127/// Collect owner definitions that the malformed sentinel left as later
12128/// declaration-list siblings. Parser-visible namespace siblings are a hard
12129/// boundary: their declarations must keep their own lexical namespace.
12130fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
12131    parent: Node<'_>,
12132    sentinel_node: Node<'_>,
12133    namespace_components: &[String],
12134    source: &str,
12135) -> Vec<CppSentinelRecoveredOwner> {
12136    let mut owners = Vec::new();
12137    let mut after_sentinel = false;
12138    let mut cursor = parent.walk();
12139    for child in parent.named_children(&mut cursor) {
12140        if !after_sentinel {
12141            if same_node(child, sentinel_node) {
12142                after_sentinel = true;
12143            }
12144            continue;
12145        }
12146        walk_named_tree_preorder(child, true, |node| {
12147            if node.kind() == "namespace_definition" {
12148                return WalkControl::SkipChildren;
12149            }
12150            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
12151        });
12152    }
12153    owners
12154}
12155
12156/// Collect owner definitions after a malformed namespace, stopping only at
12157/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
12158/// enclosing container is not trusted to belong to the recovered namespace.
12159fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
12160    parent: Node<'_>,
12161    sentinel_node: Node<'_>,
12162    namespace_components: &[String],
12163    source: &str,
12164) -> Option<Vec<CppSentinelRecoveredOwner>> {
12165    let mut owners = Vec::new();
12166    let mut after_namespace = false;
12167    let mut cursor = parent.walk();
12168    for child in parent.named_children(&mut cursor) {
12169        if !after_namespace {
12170            if same_node(child, sentinel_node) {
12171                after_namespace = true;
12172            }
12173            continue;
12174        }
12175        if cpp_sentinel_namespace_end(child, source) {
12176            return Some(owners);
12177        }
12178        walk_named_tree_preorder(child, true, |node| {
12179            if node.kind() == "namespace_definition" {
12180                return WalkControl::SkipChildren;
12181            }
12182            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
12183        });
12184    }
12185    None
12186}
12187
12188fn cpp_sentinel_recovered_sibling_owner_ranges(
12189    sentinel_node: Node<'_>,
12190    namespace_components: &[String],
12191    source: &str,
12192) -> Vec<CppSentinelRecoveredOwner> {
12193    let Some(declaration_list) = sentinel_node
12194        .parent()
12195        .filter(|parent| parent.kind() == "declaration_list")
12196    else {
12197        return Vec::new();
12198    };
12199    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
12200        declaration_list,
12201        sentinel_node,
12202        namespace_components,
12203        source,
12204    );
12205
12206    let Some(namespace) = declaration_list
12207        .parent()
12208        .filter(|parent| parent.kind() == "namespace_definition")
12209    else {
12210        return owners;
12211    };
12212    let Some(outer_parent) = namespace.parent() else {
12213        return owners;
12214    };
12215    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
12216        outer_parent,
12217        namespace,
12218        namespace_components,
12219        source,
12220    ) {
12221        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
12222    }
12223    owners
12224}
12225
12226fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
12227    let mut current = function_declarator.child_by_field_name("declarator")?;
12228    loop {
12229        if let Some(name) = macro_decorated_unqualified_name(current) {
12230            current = name;
12231            continue;
12232        }
12233        if matches!(
12234            current.kind(),
12235            "qualified_identifier"
12236                | "scoped_identifier"
12237                | "scoped_type_identifier"
12238                | "identifier"
12239                | "field_identifier"
12240                | "operator_name"
12241                | "destructor_name"
12242                | "literal_operator_name"
12243        ) {
12244            return Some(current);
12245        }
12246        current = current
12247            .child_by_field_name("declarator")
12248            .or_else(|| current.child_by_field_name("name"))
12249            .or_else(|| last_named_child(current))?;
12250    }
12251}
12252
12253/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
12254/// `::` between the halves. The scope is an attribute-like macro token, not
12255/// a namespace; `Name` is the declared name.
12256///
12257/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
12258/// tree-sitter joins the attribute macro and the constructor name into one
12259/// `qualified_identifier` and marks the separator it had to invent as MISSING.
12260/// That flag is the grammar's own record that no separator is in the source,
12261/// so read it instead of looking for `::` in the node text. A genuine
12262/// `Outer::Inner` keeps a present separator and is declined here, and so is
12263/// the explicit-global `::name`, whose `::` is present and whose scope is
12264/// absent.
12265fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
12266    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
12267        return None;
12268    }
12269    let mut cursor = node.walk();
12270    if node
12271        .children(&mut cursor)
12272        .any(|child| child.kind() == "::" && !child.is_missing())
12273    {
12274        return None;
12275    }
12276    node.child_by_field_name("name")
12277}
12278
12279fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
12280    if let Some(name) = macro_decorated_unqualified_name(node) {
12281        return cpp_name_components(name, source);
12282    }
12283    match node.kind() {
12284        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12285            let mut components = match node.child_by_field_name("scope") {
12286                Some(scope) => cpp_name_components(scope, source)?,
12287                None => Vec::new(),
12288            };
12289            let name = node.child_by_field_name("name")?;
12290            components.push(canonical_cpp_qualified_component(name, source)?);
12291            Some(components)
12292        }
12293        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
12294    }
12295}
12296
12297fn cpp_sentinel_fragment_boundary<'tree>(
12298    function: Node<'tree>,
12299    class_node: Node<'tree>,
12300    class_body: Node<'tree>,
12301    source: &str,
12302) -> Option<(Node<'tree>, Node<'tree>)> {
12303    let declaration_list = function.parent()?;
12304    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
12305        return None;
12306    }
12307    let namespace = declaration_list.parent()?;
12308    if namespace.kind() != "namespace_definition"
12309        || namespace.child_by_field_name("body") != Some(declaration_list)
12310    {
12311        return None;
12312    }
12313    let mut cursor = declaration_list.walk();
12314    let closes = declaration_list
12315        .children(&mut cursor)
12316        .filter(|child| {
12317            !child.is_named()
12318                && child.kind() == "}"
12319                && child.start_byte() >= function.end_byte()
12320                && child.start_byte() > class_node.end_byte()
12321                && child.start_byte() > class_body.start_byte()
12322        })
12323        .collect::<Vec<_>>();
12324    let [close] = closes.as_slice() else {
12325        return None;
12326    };
12327    let semicolon = namespace.next_named_sibling()?;
12328    if !cpp_is_stray_semicolon(semicolon, source)
12329        || close.end_byte() != namespace.end_byte()
12330        || semicolon.start_byte() < namespace.end_byte()
12331    {
12332        return None;
12333    }
12334    Some((*close, semicolon))
12335}
12336
12337/// Detect the bogus declaration/function tree that tree-sitter recovers for a
12338/// region prefixed by an object-like macro sentinel the parser cannot see
12339/// (issue #941), and return the byte range `[start, end)` of the swallowed
12340/// declaration interior to reparse.
12341///
12342/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
12343/// `function_definition` whose first non-comment named child is the sentinel
12344/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
12345/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
12346/// the real items.
12347/// `start` is the end of the sentinel identifier -- everything after it is the
12348/// genuine source. `end` is the node's end, extended across any trailing empty
12349/// `;` statement the mis-parse displaced past the node (the class/struct closing
12350/// semicolon), so the reparse sees a complete, brace-balanced item.
12351///
12352/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
12353/// node (`has_error`). Unknown annotation/export macros can make a real callable
12354/// error-recovered even though tree-sitter still preserves its declarator, so a
12355/// preserved callable is admitted only when a displaced class keyword precedes
12356/// that declarator. The clean-reparse-to-items gate in
12357/// `cpp_reparsed_items_are_indexable` is the final arbiter.
12358/// Return the reparse start and, when present, the structurally recovered class
12359/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
12360/// separately from the reparse start because an opaque template-declaration
12361/// macro may precede it.
12362fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
12363    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
12364    {
12365        return None;
12366    }
12367    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
12368    // retain a valid function declarator despite the unknown export macro making
12369    // the outer node erroneous. Remember that declarator for the ordering gate
12370    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
12371    // class keyword precedes a spurious callable assembled from a later member.
12372    let mut declarator_cursor = node.walk();
12373    let preserved_callable = node
12374        .children_by_field_name("declarator", &mut declarator_cursor)
12375        .find_map(extract_function_declarator);
12376    // Leading documentation comments are attached to the malformed
12377    // `function_definition` as named children.  They are not part of the
12378    // sentinel prefix, so select the first non-comment child structurally
12379    // rather than requiring the sentinel to be child zero.  This is the shape
12380    // emitted for nlohmann/json's `basic_json`: its class documentation comment
12381    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
12382    // envelope otherwise ends at the first nested union.
12383    let mut cursor = node.walk();
12384    let first = node
12385        .named_children(&mut cursor)
12386        .find(|child| child.kind() != "comment")?;
12387    if first.kind() != "type_identifier" {
12388        return None;
12389    }
12390    let sentinel = normalize_cpp_whitespace(node_text(first, source));
12391    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
12392        return None;
12393    }
12394    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
12395    // makes the trailing sentinel of one region and the leading sentinel of the
12396    // next both land as bare macro-token identifiers ahead of the real content.
12397    // Advance past every leading macro-token identifier so the reparse begins at
12398    // genuine source rather than another sentinel that would re-form the bogus
12399    // shape and fail the reparse gate.
12400    let mut start = first.end_byte();
12401    let mut after_first = false;
12402    let mut cursor = node.walk();
12403    for child in node.named_children(&mut cursor) {
12404        if !after_first {
12405            if same_node(child, first) {
12406                after_first = true;
12407            }
12408            continue;
12409        }
12410        if matches!(child.kind(), "identifier" | "type_identifier")
12411            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
12412        {
12413            start = child.end_byte();
12414        } else {
12415            break;
12416        }
12417    }
12418    // An additional opaque template-declaration macro before a class can be
12419    // folded into the bogus function's qualified declarator.  In that shape
12420    // the macro is not a direct sibling we can skip above; tree-sitter exposes
12421    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
12422    // Reparse from that keyword (or a real preceding `template` keyword) so the
12423    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
12424    // a class nested in a genuine sentinel-wrapped namespace lies after the
12425    // body opening and must not change the established region start.
12426    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
12427    let mut class_start = None;
12428    let mut template_start = None;
12429    let mut stack = vec![node];
12430    while let Some(current) = stack.pop() {
12431        if current.start_byte() >= prefix_end {
12432            continue;
12433        }
12434        if matches!(
12435            current.kind(),
12436            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
12437        ) {
12438            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
12439                "class" | "struct" | "union" | "enum" => {
12440                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
12441                        seen.min(current.start_byte())
12442                    }));
12443                }
12444                "template" => {
12445                    template_start =
12446                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
12447                            seen.min(current.start_byte())
12448                        }));
12449                }
12450                _ => {}
12451            }
12452        }
12453        let mut cursor = current.walk();
12454        stack.extend(current.children(&mut cursor));
12455    }
12456    if preserved_callable.is_some_and(|callable| {
12457        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
12458    }) {
12459        return None;
12460    }
12461    if let Some(class_start) = class_start {
12462        start = template_start
12463            .filter(|template_start| *template_start < class_start)
12464            .unwrap_or(class_start);
12465    }
12466    Some((start, class_start))
12467}
12468
12469/// Locate a sentinel-prefixed class whose malformed declaration was split across
12470/// root-level siblings. The true class close is represented structurally as a
12471/// lone `}` error followed by the class's displaced `;`; nested method/body
12472/// errors are not direct siblings of the sentinel node and therefore cannot
12473/// satisfy this pair.
12474fn cpp_sentinel_macro_class_region<'tree>(
12475    node: Node<'tree>,
12476    source: &str,
12477) -> Option<(usize, usize, usize, usize, usize, usize)> {
12478    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
12479        return None;
12480    };
12481    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
12482        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
12483        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
12484    if class_start >= body_open_start {
12485        return None;
12486    }
12487    let sibling_close = {
12488        let mut sibling = node.next_named_sibling();
12489        let mut found = None;
12490        while let Some(current) = sibling {
12491            let next = current.next_named_sibling();
12492            if cpp_is_stray_close_brace(current, source)
12493                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
12494            {
12495                let semicolon = next.expect("checked above");
12496                found = Some((
12497                    current.start_byte(),
12498                    semicolon.end_byte(),
12499                    semicolon.end_position().row + 1,
12500                ));
12501                break;
12502            }
12503            sibling = next;
12504        }
12505        found
12506    };
12507    // A stray `};` sibling is this class's close only when the bounded reparse
12508    // agrees the first body-bearing class ENDS there. When the malformed
12509    // envelope swallowed the class's true close, the scan can promote a much
12510    // later scope's close instead -- in protobuf-generated headers
12511    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
12512    // `struct TableStruct_*` paired with the first message class's `};`, making
12513    // the recovered "class body" span whole `namespace {}` blocks and minting
12514    // namespace-scope classes as nested members of the recovered class, which
12515    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
12516    // (#2275). On disagreement, fall through to the suffix-reparse boundary
12517    // below, which derives the close from the class node's own balanced body
12518    // range.
12519    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
12520        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
12521            return false;
12522        };
12523        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
12524        // A region reparse is its own tree and needs its own parent index.
12525        let reparsed_ancestry = ParentIndex::new(tree.root_node());
12526        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
12527            tree.root_node(),
12528            template_node,
12529            source,
12530            &reparsed_ancestry,
12531        ) else {
12532            return false;
12533        };
12534        let body = reparsed_class.body;
12535        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
12536    });
12537    let (class_close_start, class_close_end, class_close_line) =
12538        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
12539            (class_close_start, class_close_end, class_close_line)
12540        } else {
12541            // When the malformed envelope itself is an ERROR, tree-sitter can
12542            // leave the class's balanced close in the source while promoting
12543            // all following members to siblings. Reparse the complete suffix
12544            // and use the first body-bearing class node's own field range as
12545            // the partition boundary. This keeps balancing in tree-sitter and
12546            // preserves the source's original byte offsets.
12547            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
12548            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
12549            // A region reparse is its own tree and needs its own parent index.
12550            let reparsed_ancestry = ParentIndex::new(tree.root_node());
12551            let reparsed_class = cpp_sentinel_reparsed_class(
12552                tree.root_node(),
12553                template_node,
12554                source,
12555                &reparsed_ancestry,
12556            )?;
12557            let body = reparsed_class.body;
12558            let class_close_end = body.end_byte();
12559            let class_close_start = class_close_end.checked_sub(1)?;
12560            let class_close_line = body.end_position().row + 1;
12561            (class_close_start, class_close_end, class_close_line)
12562        };
12563    if class_close_start <= class_start {
12564        return None;
12565    }
12566
12567    // Reparse only far enough to expose the class body opening. This is a
12568    // structured check that the candidate really begins with a body-bearing
12569    // class-like item; the original malformed tree cannot provide that node.
12570    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
12571    let class_root = tree.root_node();
12572    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
12573    // A region reparse is its own tree and needs its own parent index.
12574    let reparsed_ancestry = ParentIndex::new(class_root);
12575    let reparsed_class =
12576        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
12577    let body = reparsed_class.body;
12578    // The class body opening must agree with the malformed wrapper's structured
12579    // body field. This rejects an inner nested class while permitting later
12580    // members to remain fragmented as root-level siblings in the bounded parse.
12581    if body.start_byte() != body_open_start {
12582        return None;
12583    }
12584    let body_start = body.start_byte().checked_add(1)?;
12585    (body_start < class_close_start).then_some((
12586        reparse_start,
12587        class_start,
12588        body_start,
12589        class_close_start,
12590        class_close_end,
12591        class_close_line,
12592    ))
12593}
12594
12595/// Find the `{` token immediately following the class/struct/union/enum token
12596/// at `class_start` in the malformed tree. The token is anonymous in the C++
12597/// grammar, so this deliberately walks all children (not only named children)
12598/// and relies on sibling structure rather than source-text searching.
12599fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
12600    let mut stack = vec![node];
12601    while let Some(current) = stack.pop() {
12602        if current.start_byte() == class_start
12603            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
12604        {
12605            let mut sibling = current.next_sibling();
12606            while let Some(candidate) = sibling {
12607                if candidate.kind() == "{" {
12608                    return Some(candidate.start_byte());
12609                }
12610                sibling = candidate.next_sibling();
12611            }
12612        }
12613        let mut cursor = current.walk();
12614        stack.extend(current.children(&mut cursor));
12615    }
12616    None
12617}
12618
12619/// The class body that tree-sitter displaced out of a sentinel-prefixed
12620/// declaration and left as the malformed node's next sibling.
12621///
12622/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
12623/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
12624/// the last child of that `ERROR` and its `{` opens a sibling
12625/// `compound_statement` instead. The body is still the malformed tree's own
12626/// structured token, which is what the caller's `body.start_byte() !=
12627/// body_open_start` agreement check needs; it just is not reachable by walking
12628/// forward from the class token inside the node.
12629fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
12630    node.next_named_sibling()
12631        .filter(|sibling| sibling.kind() == "compound_statement")
12632}
12633
12634fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
12635    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
12636    let mut end = if class_start.is_some() {
12637        cpp_macro_prefixed_class_end(source, start)?
12638    } else {
12639        node.end_byte()
12640    };
12641    if class_start.is_none()
12642        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
12643    {
12644        end = end.max(namespace_end);
12645    }
12646    let mut sibling = node.next_named_sibling();
12647    while let Some(current) = sibling {
12648        if !cpp_is_stray_semicolon(current, source) {
12649            break;
12650        }
12651        end = current.end_byte();
12652        sibling = current.next_named_sibling();
12653    }
12654    (start < end).then_some((start, end))
12655}
12656
12657/// Extend a sentinel reparse through a following namespace that tree-sitter
12658/// flattened into the sentinel node's sibling list.
12659///
12660/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
12661/// unknown macro becomes a false function return type and consumes the first
12662/// namespace body. A second `namespace detail` then loses its enclosing node:
12663/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
12664/// attaches its declarations to the surrounding error tree. Reparse from that
12665/// structured keyword so tree-sitter, rather than a source-text brace scan,
12666/// supplies the complete namespace boundary.
12667fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
12668    let mut sibling = node.next_sibling();
12669    let keyword = loop {
12670        let candidate = sibling?;
12671        sibling = candidate.next_sibling();
12672        if candidate.kind() != "comment" {
12673            break candidate;
12674        }
12675    };
12676    if keyword.kind() != "namespace" {
12677        return None;
12678    }
12679    let name = loop {
12680        let candidate = sibling?;
12681        sibling = candidate.next_sibling();
12682        if candidate.kind() != "comment" {
12683            break candidate;
12684        }
12685    };
12686    if cpp_namespace_name_components(name, source).is_empty() {
12687        return None;
12688    }
12689    let open = loop {
12690        let candidate = sibling?;
12691        sibling = candidate.next_sibling();
12692        if candidate.kind() != "comment" {
12693            break candidate;
12694        }
12695    };
12696    if open.kind() != "{" {
12697        return None;
12698    }
12699
12700    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
12701    let root = tree.root_node();
12702    let mut cursor = root.walk();
12703    let namespace = root
12704        .named_children(&mut cursor)
12705        .find(|candidate| candidate.kind() != "comment")?;
12706    (namespace.kind() == "namespace_definition"
12707        && namespace.start_byte() == keyword.start_byte()
12708        && namespace.child_by_field_name("body").is_some())
12709    .then_some(namespace.end_byte())
12710}
12711
12712/// Parse the source suffix beginning at a structurally recovered class/template
12713/// keyword and return the end of its first body-bearing class item.  The parser,
12714/// rather than a brace scanner, owns nested-body balancing.  This is needed when
12715/// the original error tree truncates the class and scatters later members as
12716/// top-level siblings.
12717fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
12718    let tree = cpp_reparse_region_items(source, start, source.len())?;
12719    let root = tree.root_node();
12720    let mut cursor = root.walk();
12721    for item in root.named_children(&mut cursor) {
12722        if item.end_byte() <= start || item.kind() == "comment" {
12723            continue;
12724        }
12725        let mut stack = vec![item];
12726        while let Some(current) = stack.pop() {
12727            if matches!(
12728                current.kind(),
12729                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
12730            ) && cpp_body_node(current).is_some()
12731            {
12732                return Some(current.end_byte());
12733            }
12734            let mut cursor = current.walk();
12735            stack.extend(current.named_children(&mut cursor));
12736        }
12737        // The recovered prefix is required to begin with the class item.  If
12738        // the first real item is something else, fail closed rather than skip
12739        // arbitrary source looking for a later class.
12740        return None;
12741    }
12742    None
12743}
12744
12745/// An empty `;` statement: the displaced closing semicolon of a struct/class that
12746/// the sentinel mis-parse split off past the bogus function node.
12747fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
12748    node.kind() == "expression_statement"
12749        && node.named_child_count() == 0
12750        && node_text(node, source).trim() == ";"
12751}
12752
12753/// Recover the real field name when a leading object-like annotation macro
12754/// displaces a qualified type into tree-sitter's bit-field recovery shape.
12755///
12756/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
12757/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
12758/// `bitfield_clause` containing an error plus an assignment.  The assignment's
12759/// left field is the only structured declaration name in that malformed tail.
12760/// A real bit-field is excluded by the all-caps macro type and required error.
12761fn recovered_macro_qualified_field_declarators<'tree>(
12762    node: Node<'tree>,
12763    source: &str,
12764) -> Option<Vec<Node<'tree>>> {
12765    if node.kind() != "field_declaration" {
12766        return None;
12767    }
12768    let macro_type = node.child_by_field_name("type")?;
12769    if macro_type.kind() != "type_identifier"
12770        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12771    {
12772        return None;
12773    }
12774    let pseudo_declarator = node.child_by_field_name("declarator")?;
12775    if pseudo_declarator.kind() != "field_identifier" {
12776        return None;
12777    }
12778    let mut cursor = node.walk();
12779    let clause = node
12780        .named_children(&mut cursor)
12781        .find(|child| child.kind() == "bitfield_clause")?;
12782    if !(0..clause.named_child_count()).any(|index| {
12783        clause
12784            .named_child(index)
12785            .is_some_and(|child| child.kind() == "ERROR")
12786    }) {
12787        return None;
12788    }
12789    let mut recovered = Vec::new();
12790    let mut stack = vec![clause];
12791    while let Some(current) = stack.pop() {
12792        if current.kind() == "assignment_expression"
12793            && let Some(left) = current.child_by_field_name("left")
12794            && extract_variable_name(left, source).is_some()
12795        {
12796            recovered.push(left);
12797            break;
12798        }
12799        let mut cursor = current.walk();
12800        stack.extend(current.named_children(&mut cursor));
12801    }
12802    if recovered.is_empty() {
12803        return None;
12804    }
12805    let mut cursor = node.walk();
12806    recovered.extend(
12807        node.children_by_field_name("declarator", &mut cursor)
12808            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
12809    );
12810    Some(recovered)
12811}
12812
12813/// Recover a macro-qualified constructor that tree-sitter represents as one
12814/// field declaration. The constructor call remains inside the direct recovery
12815/// error, while each member initializer becomes a false function declarator.
12816/// The class owner proves the constructor name and lets the caller ignore those
12817/// initializer declarators.
12818fn recovered_macro_qualified_constructor_call<'tree>(
12819    node: Node<'tree>,
12820    class_name: &str,
12821    source: &str,
12822) -> Option<Node<'tree>> {
12823    if node.kind() != "field_declaration" {
12824        return None;
12825    }
12826    let macro_type = node.child_by_field_name("type")?;
12827    if macro_type.kind() != "type_identifier"
12828        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12829    {
12830        return None;
12831    }
12832    let mut cursor = node.walk();
12833    let bitfield = node
12834        .named_children(&mut cursor)
12835        .find(|child| child.kind() == "bitfield_clause")?;
12836    let error = bitfield
12837        .named_child(0)
12838        .filter(|child| child.kind() == "ERROR")?;
12839    let mut stack = vec![error];
12840    while let Some(current) = stack.pop() {
12841        if current.kind() == "call_expression"
12842            && current
12843                .child_by_field_name("function")
12844                .is_some_and(|function| node_text(function, source) == class_name)
12845            && current
12846                .child_by_field_name("arguments")
12847                .is_some_and(|arguments| arguments.kind() == "argument_list")
12848        {
12849            return Some(current);
12850        }
12851        let mut cursor = current.walk();
12852        stack.extend(current.named_children(&mut cursor));
12853    }
12854    None
12855}
12856
12857/// Recover a macro-qualified member function declaration that tree-sitter
12858/// represents as a pseudo-field. An object-like export macro before a qualified
12859/// return type can displace the namespace and type into an ERROR/bitfield
12860/// recovery, leaving the callable as a structured `call_expression`.
12861///
12862/// The caller must route this shape before ordinary declarator classification;
12863/// otherwise the displaced namespace identifier is published as a field.
12864fn recovered_macro_qualified_function_call<'tree>(
12865    node: Node<'tree>,
12866    source: &str,
12867) -> Option<Node<'tree>> {
12868    if node.kind() != "field_declaration" {
12869        return None;
12870    }
12871    let macro_type = node.child_by_field_name("type")?;
12872    if macro_type.kind() != "type_identifier"
12873        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12874    {
12875        return None;
12876    }
12877    let declarator = node.child_by_field_name("declarator")?;
12878    if declarator.kind() != "field_identifier" {
12879        return None;
12880    }
12881    let mut cursor = node.walk();
12882    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12883    if !named.iter().any(|child| {
12884        child.kind() == "storage_class_specifier"
12885            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
12886    }) {
12887        return None;
12888    }
12889    let bitfield = named
12890        .iter()
12891        .find(|child| child.kind() == "bitfield_clause")?;
12892    let mut bitfield_cursor = bitfield.walk();
12893    let payload = bitfield
12894        .named_children(&mut bitfield_cursor)
12895        .collect::<Vec<_>>();
12896    let [displaced_error, call] = payload.as_slice() else {
12897        return None;
12898    };
12899    if displaced_error.kind() != "ERROR"
12900        || displaced_error.named_child_count() != 1
12901        || displaced_error
12902            .named_child(0)
12903            .is_none_or(|child| child.kind() != "identifier")
12904        || call.kind() != "call_expression"
12905        || call
12906            .child_by_field_name("function")
12907            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
12908        || call
12909            .child_by_field_name("arguments")
12910            .is_none_or(|arguments| arguments.kind() != "argument_list")
12911    {
12912        return None;
12913    }
12914    Some(*call)
12915}
12916
12917fn recovered_macro_qualified_function_parameters(
12918    arguments: Node<'_>,
12919    source: &str,
12920) -> Option<(String, Vec<String>)> {
12921    if arguments.kind() != "argument_list" {
12922        return None;
12923    }
12924    let mut cursor = arguments.walk();
12925    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
12926    if named.is_empty() {
12927        return Some(("()".to_string(), Vec::new()));
12928    }
12929    let mut types = Vec::new();
12930    let mut labels = Vec::new();
12931    let mut index = 0;
12932    while index < named.len() {
12933        let parameter_type = named[index];
12934        let parameter_name = named.get(index + 1).copied()?;
12935        if !matches!(
12936            parameter_type.kind(),
12937            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
12938        ) || parameter_name.kind() != "ERROR"
12939            || parameter_name.named_child_count() != 1
12940            || parameter_name
12941                .named_child(0)
12942                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
12943        {
12944            return None;
12945        }
12946        let parameter_name = parameter_name.named_child(0)?;
12947        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
12948        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
12949        index += 2;
12950    }
12951    Some((format!("({})", types.join(", ")), labels))
12952}
12953
12954/// Recognize the phantom field tree-sitter emits for a macro-qualified
12955/// function return type.  For example,
12956/// `static API result_type ThresholdForSmallA() { ... }` can become a
12957/// `field_declaration` (`API` as the type and `result_type` as a field name)
12958/// followed by a clean `function_definition` for `ThresholdForSmallA`.
12959///
12960/// Keep this predicate entirely tied to the CST envelope: the type must be an
12961/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
12962/// the declaration must carry a missing semicolon rather than a real one, and
12963/// the immediate named sibling must expose a function declarator.  A real
12964/// macro-decorated field with an explicit semicolon therefore remains a field.
12965pub fn recovered_macro_return_type_node<'tree>(
12966    node: Node<'tree>,
12967    source: &str,
12968) -> Option<Node<'tree>> {
12969    if node.kind() != "field_declaration" {
12970        return None;
12971    }
12972    let macro_type = node.child_by_field_name("type")?;
12973    if macro_type.kind() != "type_identifier"
12974        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12975    {
12976        return None;
12977    }
12978    let declarator = node.child_by_field_name("declarator")?;
12979    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
12980        return None;
12981    }
12982    let mut has_missing_semicolon = false;
12983    let mut has_real_semicolon = false;
12984    for index in 0..node.child_count() {
12985        let Some(child) = node.child(index) else {
12986            continue;
12987        };
12988        if child.kind() != ";" {
12989            continue;
12990        }
12991        if child.is_missing() {
12992            has_missing_semicolon = true;
12993        } else {
12994            has_real_semicolon = true;
12995        }
12996    }
12997    if !has_missing_semicolon || has_real_semicolon {
12998        return None;
12999    }
13000    let mut next = node.next_named_sibling();
13001    while next.is_some_and(|sibling| sibling.kind() == "comment") {
13002        next = next.and_then(|sibling| sibling.next_named_sibling());
13003    }
13004    let next = next?;
13005    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
13006        return None;
13007    }
13008    let function_declarator = next.child_by_field_name("declarator")?;
13009    extract_function_declarator(function_declarator).map(|_| declarator)
13010}
13011
13012/// Whether `name` is a type parameter of a template declaration that lexically
13013/// encloses `node`. The malformed macro-return field uses the parameter name as
13014/// its pseudo-declarator; preserving that field is necessary to publish a
13015/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
13016/// ancestors instead of interpreting source text so nested templates and
13017/// parser-recovered regions retain their real lexical scopes.
13018pub(crate) fn cpp_active_template_type_parameter<'tree>(
13019    node: Node<'tree>,
13020    name: &str,
13021    source: &str,
13022    ancestry: &ParentIndex<'tree>,
13023) -> bool {
13024    let mut ancestor = ancestry.parent(node);
13025    while let Some(current) = ancestor {
13026        if current.kind() == "template_declaration"
13027            && let Some(parameters) = current.child_by_field_name("parameters")
13028        {
13029            let mut cursor = parameters.walk();
13030            if parameters.named_children(&mut cursor).any(|parameter| {
13031                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
13032                    && cpp_template_parameter_name(parameter, source)
13033                        .is_some_and(|parameter_name| parameter_name == name)
13034            }) {
13035                return true;
13036            }
13037        }
13038        ancestor = ancestry.parent(current);
13039    }
13040    false
13041}
13042
13043/// Reparse the region `[start, end)` of `source` as C++, confined to the region
13044/// via included ranges so every reparsed node keeps its original byte offset and
13045/// line number. The existing visitors read node text from the original source,
13046/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
13047/// `parse_rust_region_tree` technique.
13048fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
13049    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
13050}
13051
13052fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
13053    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
13054        return None;
13055    }
13056    let semicolon = node.next_sibling()?;
13057    if semicolon.kind() != ";" || semicolon.is_missing() {
13058        return None;
13059    }
13060    let row = node.start_position().row;
13061    let mut start = node.start_byte();
13062    let mut sibling = node.prev_sibling();
13063    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
13064        if previous.kind() == ";" {
13065            break;
13066        }
13067        start = previous.start_byte();
13068        sibling = previous.prev_sibling();
13069    }
13070    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
13071}
13072
13073fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
13074    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
13075        return false;
13076    }
13077    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
13078        return false;
13079    }
13080    let Some(declarator) = (if node.kind() == "function_definition" {
13081        node.child_by_field_name("declarator")
13082            .and_then(extract_function_declarator)
13083    } else {
13084        node.named_child(0)
13085            .filter(|child| child.kind() == "function_declarator")
13086    }) else {
13087        return false;
13088    };
13089    let Some(name) = cpp_function_declarator_name_node(declarator) else {
13090        return false;
13091    };
13092    declarator.start_byte() == node.start_byte()
13093        && name.kind() == "identifier"
13094        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
13095}
13096
13097/// Reparse a fragmented class-body interior while preserving its original byte
13098/// and line offsets, confined to the region by tree-sitter included ranges.
13099///
13100/// This used to materialize the region's whole file prefix as whitespace and
13101/// make the lexer walk it, the technique #1309 replaced on the other reparse
13102/// path: O(file) per fragmented-class recovery, on files that are already
13103/// error-recovered and already slow (#2788). Included ranges give the parser
13104/// the same view -- the region's bytes, at their original offsets and
13105/// line/column positions -- without materializing or lexing anything before it.
13106///
13107/// The equality of the two views is the claim, so a debug build parses both and
13108/// asserts the trees agree node for node.
13109fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
13110    let region = cpp_reparse_region_items(source, start, end);
13111
13112    #[cfg(debug_assertions)]
13113    assert_eq!(
13114        region.as_ref().map(cpp_tree_shape),
13115        cpp_reparse_padded_class_body(source, start, end)
13116            .as_ref()
13117            .map(cpp_tree_shape),
13118        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
13119         prefix produces"
13120    );
13121
13122    region
13123}
13124
13125/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
13126/// replaces, kept as the oracle a debug build asserts every region reparse
13127/// against and as the release-mode parity tests' reference (#2788).
13128#[cfg(any(debug_assertions, test))]
13129fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
13130    if start >= end {
13131        // The included-range parser refuses an empty region; the padded one
13132        // returned a tree holding nothing, which every caller read as "no
13133        // members here".
13134        return None;
13135    }
13136    let bytes = source.as_bytes();
13137    let prefix = bytes.get(..start)?;
13138    let interior = bytes.get(start..end)?;
13139    let mut padded = Vec::with_capacity(end);
13140    padded.extend(
13141        prefix
13142            .iter()
13143            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
13144    );
13145    padded.extend_from_slice(interior);
13146    let padded = String::from_utf8(padded).ok()?;
13147    let mut parser = Parser::new();
13148    parser
13149        .set_language(&tree_sitter_cpp::LANGUAGE.into())
13150        .ok()?;
13151    parser.parse(&padded, None)
13152}
13153
13154/// Every node of `tree` in preorder, by kind, span and position, which is what
13155/// two reparses of one region have to agree on for their callers to read the
13156/// same declarations out of either (#2788).
13157#[cfg(any(debug_assertions, test))]
13158fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
13159    let mut shape = Vec::new();
13160    let mut cursor = tree.root_node().walk();
13161    let mut stack = vec![tree.root_node()];
13162    while let Some(node) = stack.pop() {
13163        shape.push((
13164            node.kind(),
13165            node.start_byte(),
13166            node.end_byte(),
13167            node.start_position().row,
13168            node.start_position().column,
13169            node.is_named(),
13170            node.is_missing(),
13171        ));
13172        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
13173        stack.extend(children.into_iter().rev());
13174    }
13175    shape
13176}
13177
13178/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
13179/// reparsed interior is indexed only when every top-level named node is a
13180/// well-formed C++ item (or a comment) and at least one real item is present.
13181/// Expression/statement soup surfaces as a top-level `ERROR` or
13182/// `expression_statement`, neither of which is an item kind, so it is rejected.
13183///
13184/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
13185/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
13186/// swallowed by a preceding dangling sentinel) reparses to a real
13187/// `namespace_definition` whose body still holds a bogus `function_definition`,
13188/// so the subtree legitimately carries an error. Container items are admitted
13189/// even with an internal error; the inner bogus function is recovered recursively
13190/// when `visit_function_definition` walks it. Each recursion strips at least one
13191/// leading sentinel, so the region strictly shrinks and recovery terminates.
13192///
13193/// A top-level `function_definition` is the one place we stay strict: it is
13194/// admitted only when it is clean or is itself a sentinel candidate. A function
13195/// that has an error and is not a sentinel is a real callable with a broken body,
13196/// so we refuse the whole reparse and let the ordinary path handle it (preserving
13197/// its real return type rather than re-deriving an implicit one).
13198fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
13199    let mut cursor = root.walk();
13200    let mut saw_item = false;
13201    for child in root.named_children(&mut cursor) {
13202        match child.kind() {
13203            "comment" => {}
13204            "function_definition" => {
13205                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
13206                    return false;
13207                }
13208                saw_item = true;
13209            }
13210            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
13211            _ => return false,
13212        }
13213    }
13214    saw_item
13215}
13216
13217/// Robustness gate for a reparsed fragmented multiple-base export class body
13218/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
13219/// kinds a class body produces when reparsed at translation-unit scope: the
13220/// access-specifier label preceding the first member surfaces as a
13221/// `labeled_statement` wrapping that member, and members surface as
13222/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
13223/// Statement or expression soup surfaces as other top-level kinds and is rejected,
13224/// so only a genuinely member-shaped body is ever re-owned as members; anything
13225/// ambiguous falls back to indexing the class alone.
13226fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
13227    if node.kind() != "ERROR" {
13228        return false;
13229    }
13230    let mut stack = Vec::new();
13231    let mut saw_function_declarator = false;
13232    let mut cursor = node.walk();
13233    for child in node.named_children(&mut cursor) {
13234        stack.push(child);
13235    }
13236    while let Some(current) = stack.pop() {
13237        match current.kind() {
13238            // Tree-sitter may wrap adjacent copy-control declarations in a
13239            // nested ERROR. Keep descending only through ERROR wrappers; the
13240            // actual declaration payload must be a function_declarator.
13241            "ERROR" => {
13242                let mut cursor = current.walk();
13243                stack.extend(current.named_children(&mut cursor));
13244            }
13245            "function_declarator" => saw_function_declarator = true,
13246            _ => return false,
13247        }
13248    }
13249    saw_function_declarator
13250}
13251
13252fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
13253    if node.kind() != "ERROR" {
13254        return false;
13255    }
13256    let mut cursor = node.walk();
13257    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
13258    let [explicit, constructor_error, destructor] = named.as_slice() else {
13259        return false;
13260    };
13261    let Some(constructor) = constructor_error.named_child(0) else {
13262        return false;
13263    };
13264    let Some(constructor_name) =
13265        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
13266    else {
13267        return false;
13268    };
13269    let Some(destructor_name) =
13270        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
13271    else {
13272        return false;
13273    };
13274    let Some(destroyed_type) = destructor_name.named_child(0) else {
13275        return false;
13276    };
13277    explicit.kind() == "explicit_function_specifier"
13278        && constructor_error.kind() == "ERROR"
13279        && constructor_error.named_child_count() == 1
13280        && constructor.kind() == "function_declarator"
13281        && constructor_name.kind() == "identifier"
13282        && destructor.kind() == "function_declarator"
13283        && destructor_name.kind() == "destructor_name"
13284        && destroyed_type.kind() == "identifier"
13285        && node_text(constructor_name, source) == node_text(destroyed_type, source)
13286}
13287
13288fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
13289    if node.kind() != "compound_statement" {
13290        return false;
13291    }
13292    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
13293        return false;
13294    };
13295    if prefix.kind() == "labeled_statement"
13296        && prefix.named_child(0).is_some_and(|label| {
13297            matches!(
13298                node_text(label, source).trim(),
13299                "public" | "private" | "protected"
13300            )
13301        })
13302    {
13303        return prefix.named_children(&mut prefix.walk()).any(|child| {
13304            child.kind() == "declaration"
13305                && child.has_error()
13306                && child
13307                    .named_children(&mut child.walk())
13308                    .any(cpp_reparsed_member_error_is_indexable)
13309        });
13310    }
13311    // A malformed constructor initializer can be split into a declaration
13312    // followed by its compound body when the class prefix already contains
13313    // realistic members. Keep this admission tied to that exact structured
13314    // declaration/error/body chain rather than accepting arbitrary blocks.
13315    prefix.kind() == "declaration"
13316        && prefix.has_error()
13317        && prefix
13318            .named_children(&mut prefix.walk())
13319            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
13320}
13321
13322fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
13323    if !cpp_reparsed_member_error_is_indexable(node) {
13324        return false;
13325    }
13326    let Some(preproc) = node.next_named_sibling() else {
13327        return false;
13328    };
13329    preproc.kind() == "preproc_if"
13330        && preproc.has_error()
13331        && preproc
13332            .named_children(&mut preproc.walk())
13333            .any(|child| child.kind() == "expression_statement" && child.has_error())
13334        && preproc
13335            .next_named_sibling()
13336            .is_some_and(|body| body.kind() == "compound_statement")
13337}
13338
13339/// Return a function body whose braces and ownership are explicit in the
13340/// reparsed class-member tree. An error below a real function envelope is
13341/// recoverable by the ordinary function visitor; a missing/deferred body is
13342/// not, because accepting it would let statement soup masquerade as a member.
13343fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
13344    if node.kind() != "function_definition" {
13345        return None;
13346    }
13347    let body = node.child_by_field_name("body")?;
13348    if body.kind() != "compound_statement" {
13349        return None;
13350    }
13351    let open = body.child(0)?;
13352    let close = body.child(body.child_count().checked_sub(1)?)?;
13353    if open.kind() != "{"
13354        || open.is_missing()
13355        || close.kind() != "}"
13356        || close.is_missing()
13357        || close.end_byte() != body.end_byte()
13358        || body.end_byte() != node.end_byte()
13359    {
13360        return None;
13361    }
13362    Some(body)
13363}
13364
13365fn cpp_reparsed_member_function_errors_are_in_body(
13366    node: Node<'_>,
13367    body: Node<'_>,
13368    source: &str,
13369) -> bool {
13370    let mut cursor = node.walk();
13371    node.children(&mut cursor).all(|child| {
13372        same_node(child, body)
13373            || cpp_reparsed_member_attribute_error(child, source)
13374            || cpp_reparsed_member_signature_identifier_errors(child)
13375            || (!child.has_error() && !child.is_error() && !child.is_missing())
13376    })
13377}
13378
13379/// A complete callable can still carry parser errors in its signature when a
13380/// project annotation is not part of the C++ grammar (`nonneg int`,
13381/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
13382/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
13383/// leaves inside the already-proven callable envelope; structured statements,
13384/// literals, missing tokens, and other malformed signature payload remain
13385/// rejected.
13386fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
13387    if !node.has_error() && !node.is_error() && !node.is_missing() {
13388        return false;
13389    }
13390    let mut stack = vec![node];
13391    let mut saw_error = false;
13392    while let Some(current) = stack.pop() {
13393        if current.is_missing() {
13394            return false;
13395        }
13396        if current.kind() == "ERROR" {
13397            saw_error = true;
13398            let mut cursor = current.walk();
13399            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
13400            if children
13401                .iter()
13402                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
13403            {
13404                return false;
13405            }
13406            stack.extend(children);
13407            continue;
13408        }
13409        let mut cursor = current.walk();
13410        stack.extend(current.children(&mut cursor));
13411    }
13412    saw_error
13413}
13414
13415fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
13416    node.kind() == "ERROR"
13417        && node.named_child_count() == 1
13418        && node.named_child(0).is_some_and(|attribute| {
13419            attribute.kind() == "identifier"
13420                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
13421        })
13422}
13423
13424/// A C++ attribute placed between a member's declarator and body can make
13425/// tree-sitter expose the callable as
13426/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
13427/// Keep this admission tied to that exact node geometry. In particular, an
13428/// arbitrary ERROR or identifier before a compound statement is not enough.
13429fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
13430    let Some(body) = cpp_reparsed_member_function_body(node) else {
13431        return false;
13432    };
13433    let mut cursor = node.walk();
13434    let named = node
13435        .named_children(&mut cursor)
13436        .filter(|child| child.kind() != "comment")
13437        .collect::<Vec<_>>();
13438    let [type_node, error, attribute, body_node] = named.as_slice() else {
13439        return false;
13440    };
13441    if !same_node(*body_node, body)
13442        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
13443        || attribute.kind() != "identifier"
13444        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
13445        || error.kind() != "ERROR"
13446        || error.named_child_count() != 1
13447    {
13448        return false;
13449    }
13450    error
13451        .named_child(0)
13452        .is_some_and(cpp_reparsed_attribute_callable_declarator)
13453}
13454
13455fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
13456    cpp_structured_type_path(node, source).is_some()
13457        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
13458}
13459
13460fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
13461    let Some(body) = cpp_reparsed_member_function_body(node) else {
13462        return false;
13463    };
13464    let mut cursor = node.walk();
13465    let named = node
13466        .named_children(&mut cursor)
13467        .filter(|child| child.kind() != "comment")
13468        .collect::<Vec<_>>();
13469    let [friend, return_error, declarator, body_node] = named.as_slice() else {
13470        return false;
13471    };
13472    let Some(return_type) = return_error.named_child(0) else {
13473        return false;
13474    };
13475    same_node(*body_node, body)
13476        && friend.kind() == "type_identifier"
13477        && node_text(*friend, source) == "friend"
13478        && return_error.kind() == "ERROR"
13479        && return_error.named_child_count() == 1
13480        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
13481        && extract_function_declarator(*declarator)
13482            .and_then(cpp_function_declarator_name_node)
13483            .is_some()
13484}
13485
13486fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
13487    let Some(body) = cpp_reparsed_member_function_body(node) else {
13488        return false;
13489    };
13490    let mut cursor = node.walk();
13491    let named = node
13492        .named_children(&mut cursor)
13493        .filter(|child| child.kind() != "comment")
13494        .collect::<Vec<_>>();
13495    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
13496        return false;
13497    };
13498    let Some(return_type) = return_error.named_child(0) else {
13499        return false;
13500    };
13501    same_node(*body_node, body)
13502        && prefix
13503            .iter()
13504            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
13505        && attribute.kind() == "type_identifier"
13506        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
13507        && return_error.kind() == "ERROR"
13508        && return_error.named_child_count() == 1
13509        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
13510        && extract_function_declarator(*declarator)
13511            .and_then(cpp_function_declarator_name_node)
13512            .is_some()
13513}
13514
13515/// An included-range reparse that begins inside a malformed class can merge an
13516/// access label and following template member. Tree-sitter then emits the label
13517/// as the `template_type` name, the template parameter list as its arguments,
13518/// an ERROR-wrapped return type, the callable declarator, and its complete
13519/// body. Admit only that exact structured displacement.
13520fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
13521    let Some(body) = cpp_reparsed_member_function_body(node) else {
13522        return false;
13523    };
13524    let mut cursor = node.walk();
13525    let named = node
13526        .named_children(&mut cursor)
13527        .filter(|child| child.kind() != "comment")
13528        .collect::<Vec<_>>();
13529    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
13530        return false;
13531    };
13532    let Some(template_name) = template_type.child_by_field_name("name") else {
13533        return false;
13534    };
13535    let Some(arguments) = template_type.child_by_field_name("arguments") else {
13536        return false;
13537    };
13538    let Some(return_type) = return_error.named_child(0) else {
13539        return false;
13540    };
13541    let mut cursor = template_type.walk();
13542    let template_errors = template_type
13543        .named_children(&mut cursor)
13544        .filter(|child| child.kind() == "ERROR")
13545        .collect::<Vec<_>>();
13546    let [comment_error] = template_errors.as_slice() else {
13547        return false;
13548    };
13549    let mut cursor = comment_error.walk();
13550    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
13551    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
13552        return false;
13553    };
13554    same_node(*body_node, body)
13555        && template_type.kind() == "template_type"
13556        && template_name.kind() == "type_identifier"
13557        && matches!(
13558            node_text(template_name, source).trim(),
13559            "public" | "private" | "protected"
13560        )
13561        && arguments.kind() == "template_argument_list"
13562        && arguments.named_child_count() > 0
13563        && !arguments.has_error()
13564        && !colon.is_named()
13565        && colon.kind() == ":"
13566        && comments.iter().all(|child| child.kind() == "comment")
13567        && !template_keyword.is_named()
13568        && template_keyword.kind() == "template"
13569        && return_error.kind() == "ERROR"
13570        && return_error.named_child_count() == 1
13571        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
13572        && extract_function_declarator(*declarator)
13573            .and_then(cpp_function_declarator_name_node)
13574            .is_some()
13575}
13576
13577/// Return the constructor declaration tree-sitter can merge into an access
13578/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
13579/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
13580/// declaration's apparent type; the callable name must still exactly match the
13581/// recovered class, so unrelated labeled statements are never re-owned.
13582fn cpp_reparsed_preprocessor_constructor<'tree>(
13583    node: Node<'tree>,
13584    class_name: &str,
13585    source: &str,
13586) -> Option<Node<'tree>> {
13587    if node.kind() != "labeled_statement" {
13588        return None;
13589    }
13590    let mut cursor = node.walk();
13591    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
13592    let [label, directive_error, declaration] = named.as_slice() else {
13593        return None;
13594    };
13595    if label.kind() != "statement_identifier"
13596        || !matches!(
13597            node_text(*label, source),
13598            "public" | "private" | "protected"
13599        )
13600        || directive_error.kind() != "ERROR"
13601        || directive_error.child_count() != 1
13602        || directive_error
13603            .child(0)
13604            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
13605        || declaration.kind() != "declaration"
13606        || declaration.named_child_count() != 2
13607    {
13608        return None;
13609    }
13610    let apparent_type = declaration.child_by_field_name("type")?;
13611    if apparent_type.kind() != "type_identifier"
13612        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
13613    {
13614        return None;
13615    }
13616    let declarator = declaration.child_by_field_name("declarator")?;
13617    let function = extract_function_declarator(declarator)?;
13618    let name = cpp_function_declarator_name_node(function)?;
13619    (node_text(name, source) == class_name).then_some(*declaration)
13620}
13621
13622fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
13623    if extract_function_declarator(node)
13624        .and_then(cpp_function_declarator_name_node)
13625        .is_some()
13626    {
13627        return true;
13628    }
13629    node.kind() == "init_declarator"
13630        && node
13631            .child_by_field_name("declarator")
13632            .is_some_and(|declarator| declarator.kind() == "identifier")
13633        && node
13634            .child_by_field_name("value")
13635            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
13636}
13637
13638/// Return true for the constrained/attribute form that tree-sitter splits into
13639/// an ERROR declaration, a preprocessor `requires` clause, and a following
13640/// compound statement. The three nodes must remain immediate named siblings;
13641/// this deliberately does not search source text or skip unrelated statements.
13642fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
13643    if node.kind() != "ERROR" || node.named_child_count() != 3 {
13644        return false;
13645    }
13646    let mut cursor = node.walk();
13647    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
13648    let [type_node, function_declarator, attribute] = named.as_slice() else {
13649        return false;
13650    };
13651    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
13652        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
13653        || attribute.kind() != "identifier"
13654        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
13655    {
13656        return false;
13657    }
13658    let Some(preproc) =
13659        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
13660    else {
13661        return false;
13662    };
13663    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
13664        .filter(|sibling| sibling.kind() == "compound_statement")
13665    else {
13666        return false;
13667    };
13668    let Some(open) = body.child(0) else {
13669        return false;
13670    };
13671    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
13672        return false;
13673    };
13674    let Some(condition) = preproc.child_by_field_name("condition") else {
13675        return false;
13676    };
13677    let mut cursor = preproc.walk();
13678    let payload = preproc
13679        .named_children(&mut cursor)
13680        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
13681        .collect::<Vec<_>>();
13682    let [requires_statement] = payload.as_slice() else {
13683        return false;
13684    };
13685    let requires_clause = requires_statement.named_child(0);
13686
13687    open.kind() == "{"
13688        && !open.is_missing()
13689        && close.kind() == "}"
13690        && !close.is_missing()
13691        && close.end_byte() == body.end_byte()
13692        && requires_statement.kind() == "expression_statement"
13693        && requires_statement.named_child_count() == 1
13694        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
13695}
13696
13697fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
13698    let mut sibling = node.next_named_sibling();
13699    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
13700        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
13701    }
13702    sibling
13703}
13704
13705fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
13706    let mut sibling = node.prev_named_sibling();
13707    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
13708        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
13709    }
13710    sibling
13711}
13712
13713fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
13714    let Some(preproc) =
13715        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
13716    else {
13717        return false;
13718    };
13719    let Some(error) =
13720        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
13721    else {
13722        return false;
13723    };
13724    cpp_reparsed_attribute_requires_error(error, source)
13725}
13726
13727fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
13728    node: Node<'tree>,
13729    source: &str,
13730) -> Option<Node<'tree>> {
13731    if node.kind() != "ERROR" {
13732        return None;
13733    }
13734    let mut cursor = node.walk();
13735    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
13736    let [parameter, macro_name, message] = named.as_slice() else {
13737        return None;
13738    };
13739    let parameter_name = parameter.named_child(0)?;
13740    (parameter.kind() == "type_parameter_declaration"
13741        && parameter_name.kind() == "type_identifier"
13742        && macro_name.kind() == "type_identifier"
13743        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
13744        && message.kind() == "string_literal")
13745        .then_some(parameter_name)
13746}
13747
13748/// Recognize the alternate constraint-macro prefix where tree-sitter retains
13749/// the complete qualified constraint as a fourth child instead of moving it
13750/// into the following function. Keep the gate tied to a two-type template
13751/// constraint that names the declared type parameter.
13752fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
13753    node: Node<'tree>,
13754    source: &str,
13755) -> Option<Node<'tree>> {
13756    if node.kind() != "ERROR" {
13757        return None;
13758    }
13759    let mut cursor = node.walk();
13760    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
13761    let [parameter, macro_name, message, constraint] = named.as_slice() else {
13762        return None;
13763    };
13764    let parameter_name = parameter.named_child(0)?;
13765    let constraint_scope = constraint.child_by_field_name("scope")?;
13766    let constraint_template = constraint.child_by_field_name("name")?;
13767    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
13768    let mut argument_cursor = constraint_arguments.walk();
13769    let constraint_types = constraint_arguments
13770        .named_children(&mut argument_cursor)
13771        .collect::<Vec<_>>();
13772    if parameter.kind() != "type_parameter_declaration"
13773        || parameter_name.kind() != "type_identifier"
13774        || macro_name.kind() != "type_identifier"
13775        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
13776        || message.kind() != "string_literal"
13777        || constraint.kind() != "qualified_identifier"
13778        || constraint_scope.kind() != "namespace_identifier"
13779        || !matches!(
13780            constraint_template.kind(),
13781            "template_function" | "template_type"
13782        )
13783        || !matches!(constraint_types.as_slice(), [left, right]
13784            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
13785        || constraint_arguments.has_error()
13786    {
13787        return None;
13788    }
13789    let parameter_text = node_text(parameter_name, source);
13790    let mut stack = constraint_types;
13791    while let Some(current) = stack.pop() {
13792        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
13793            return Some(parameter_name);
13794        }
13795        let mut cursor = current.walk();
13796        stack.extend(current.named_children(&mut cursor));
13797    }
13798    None
13799}
13800
13801fn cpp_reparsed_template_macro_companion_is_indexable(
13802    node: Node<'_>,
13803    parameter_name: Node<'_>,
13804    source: &str,
13805) -> bool {
13806    let Some(body) = cpp_reparsed_member_function_body(node) else {
13807        return false;
13808    };
13809    let mut cursor = node.walk();
13810    let named = node
13811        .named_children(&mut cursor)
13812        .filter(|child| child.kind() != "comment")
13813        .collect::<Vec<_>>();
13814    let [
13815        constraint,
13816        close_error,
13817        storage,
13818        return_error,
13819        declarator,
13820        body_node,
13821    ] = named.as_slice()
13822    else {
13823        return false;
13824    };
13825    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
13826        return false;
13827    };
13828    let Some(constraint_template) = constraint.child_by_field_name("name") else {
13829        return false;
13830    };
13831    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
13832        return false;
13833    };
13834    let Some(return_type) = return_error.named_child(0) else {
13835        return false;
13836    };
13837    let mut cursor = constraint_arguments.walk();
13838    let constraint_types = constraint_arguments
13839        .named_children(&mut cursor)
13840        .collect::<Vec<_>>();
13841    same_node(*body_node, body)
13842        && constraint.kind() == "qualified_identifier"
13843        && constraint_scope.kind() == "namespace_identifier"
13844        && constraint_template.kind() == "template_type"
13845        && matches!(constraint_types.as_slice(), [left, right]
13846            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
13847        && !constraint_arguments.has_error()
13848        && close_error.kind() == "ERROR"
13849        && close_error.named_child_count() == 0
13850        && storage.kind() == "storage_class_specifier"
13851        && return_error.kind() == "ERROR"
13852        && return_error.named_child_count() == 1
13853        && return_type.kind() == "identifier"
13854        && node_text(return_type, source) == node_text(parameter_name, source)
13855        && extract_function_declarator(*declarator)
13856            .and_then(cpp_function_declarator_name_node)
13857            .is_some()
13858}
13859
13860fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
13861    node: Node<'tree>,
13862    parameter_name: Node<'_>,
13863    source: &str,
13864) -> Option<Node<'tree>> {
13865    let body = cpp_reparsed_member_function_body(node)?;
13866    let constraint = node.child_by_field_name("type")?;
13867    let constraint_template = constraint.child_by_field_name("name")?;
13868    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
13869    let mut argument_cursor = constraint_arguments.walk();
13870    let constraint_types = constraint_arguments
13871        .named_children(&mut argument_cursor)
13872        .collect::<Vec<_>>();
13873    if constraint.kind() != "qualified_identifier"
13874        || constraint_template.kind() != "template_type"
13875        || !matches!(constraint_types.as_slice(), [left, right]
13876            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
13877        || constraint_arguments.has_error()
13878        || node
13879            .child_by_field_name("body")
13880            .is_none_or(|candidate| !same_node(candidate, body))
13881    {
13882        return None;
13883    }
13884
13885    let mut cursor = node.walk();
13886    let recovery_errors = node
13887        .named_children(&mut cursor)
13888        .filter(|child| child.kind() == "ERROR")
13889        .collect::<Vec<_>>();
13890    if !recovery_errors
13891        .iter()
13892        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
13893        || !recovery_errors.iter().all(|error| {
13894            error.named_child_count() == 0
13895                || cpp_reparsed_constraint_macro_error(*error, source)
13896                || (error.named_child_count() == 1
13897                    && error
13898                        .named_child(0)
13899                        .is_some_and(|child| child.kind() == "function_declarator"))
13900        })
13901    {
13902        return None;
13903    }
13904
13905    let parameter_text = node_text(parameter_name, source);
13906    let mut declarators = node
13907        .child_by_field_name("declarator")
13908        .and_then(extract_function_declarator)
13909        .into_iter()
13910        .collect::<Vec<_>>();
13911    for error in recovery_errors {
13912        let mut stack = vec![error];
13913        while let Some(current) = stack.pop() {
13914            if current.kind() == "function_declarator" {
13915                declarators.push(current);
13916            }
13917            let mut cursor = current.walk();
13918            stack.extend(current.named_children(&mut cursor));
13919        }
13920    }
13921    declarators.into_iter().find(|declarator| {
13922        cpp_function_declarator_name_node(*declarator)
13923            .is_some_and(|name| name.kind() == "identifier")
13924            && declarator
13925                .child_by_field_name("parameters")
13926                .is_some_and(|parameters| {
13927                    parameters
13928                        .named_children(&mut parameters.walk())
13929                        .filter_map(|parameter| parameter.child_by_field_name("type"))
13930                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
13931                })
13932    })
13933}
13934
13935fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
13936    node: Node<'_>,
13937    parameter_name: Node<'_>,
13938    source: &str,
13939) -> bool {
13940    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
13941}
13942
13943fn cpp_reparsed_template_macro_function_companion_is_indexable(
13944    node: Node<'_>,
13945    parameter_name: Node<'_>,
13946    source: &str,
13947) -> bool {
13948    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
13949        return false;
13950    }
13951    let Some(return_type) = node.child_by_field_name("type") else {
13952        return false;
13953    };
13954    let Some(function_declarator) = node
13955        .child_by_field_name("declarator")
13956        .and_then(extract_function_declarator)
13957    else {
13958        return false;
13959    };
13960    if cpp_function_declarator_name_node(function_declarator).is_none()
13961        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
13962    {
13963        return false;
13964    }
13965    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
13966        return false;
13967    };
13968    let parameter_text = node_text(parameter_name, source);
13969    parameters
13970        .named_children(&mut parameters.walk())
13971        .any(|parameter| {
13972            parameter
13973                .child_by_field_name("type")
13974                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
13975        })
13976}
13977
13978fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
13979    if node.kind() != "ERROR" {
13980        return false;
13981    }
13982    let mut stack = vec![node];
13983    while let Some(current) = stack.pop() {
13984        let macro_shape = match current.kind() {
13985            "call_expression" => current
13986                .child_by_field_name("function")
13987                .zip(current.child_by_field_name("arguments")),
13988            "init_declarator" => current
13989                .child_by_field_name("declarator")
13990                .zip(current.child_by_field_name("value")),
13991            _ => None,
13992        };
13993        if let Some((name, arguments)) = macro_shape
13994            && name.kind() == "identifier"
13995            && arguments.kind() == "argument_list"
13996            && arguments.named_child_count() >= 2
13997            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
13998        {
13999            return true;
14000        }
14001        let mut cursor = current.walk();
14002        stack.extend(current.named_children(&mut cursor));
14003    }
14004    false
14005}
14006
14007fn cpp_recovered_template_macro_constructor<'tree>(
14008    node: Node<'tree>,
14009    source: &str,
14010) -> Option<(Node<'tree>, Node<'tree>)> {
14011    let mut prefix = node.prev_named_sibling()?;
14012    while prefix.kind() == "comment" {
14013        prefix = prefix.prev_named_sibling()?;
14014    }
14015    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
14016    let parameter = parameter_name
14017        .parent()
14018        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
14019    let declarator =
14020        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
14021    Some((declarator, parameter))
14022}
14023
14024fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
14025    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
14026        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
14027            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
14028                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
14029                    function,
14030                    parameter_name,
14031                    source,
14032                )
14033        });
14034    }
14035    let Some(parameter_name) =
14036        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
14037    else {
14038        return false;
14039    };
14040    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
14041        cpp_reparsed_template_macro_function_companion_is_indexable(
14042            function,
14043            parameter_name,
14044            source,
14045        )
14046    })
14047}
14048
14049fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
14050    let function_name = node
14051        .child_by_field_name("declarator")
14052        .and_then(extract_function_declarator)
14053        .and_then(cpp_function_declarator_name_node);
14054    if let Some(body) = cpp_reparsed_member_function_body(node)
14055        && function_name.is_some()
14056        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
14057    {
14058        return true;
14059    }
14060    cpp_reparsed_attribute_member_function(node, source)
14061        || cpp_reparsed_friend_function_is_indexable(node, source)
14062        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
14063        || cpp_reparsed_access_template_function_is_indexable(node, source)
14064        || cpp_recovered_template_macro_constructor(node, source).is_some()
14065}
14066
14067/// Recognize the three top-level nodes produced when an unknown attribute
14068/// macro separates an inline member's declarator from its body in a reparsed
14069/// class interior: an errorful declaration with a missing semicolon, the macro
14070/// call expression, and the complete compound body. Their adjacency and exact
14071/// structured shapes prove one recoverable member envelope; arbitrary calls or
14072/// blocks do not pass this gate.
14073fn cpp_reparsed_macro_attribute_member_sequence(
14074    children: &[Node<'_>],
14075    index: usize,
14076    source: &str,
14077) -> bool {
14078    let Some(prefix) = children.get(index).copied() else {
14079        return false;
14080    };
14081    let declaration = if prefix.kind() == "labeled_statement" {
14082        prefix
14083            .named_child(prefix.named_child_count().saturating_sub(1))
14084            .filter(|child| child.kind() == "declaration")
14085    } else {
14086        (prefix.kind() == "declaration").then_some(prefix)
14087    };
14088    let Some(declaration) = declaration else {
14089        return false;
14090    };
14091    if !declaration.has_error()
14092        || declaration
14093            .child_by_field_name("declarator")
14094            .and_then(extract_function_declarator)
14095            .and_then(cpp_function_declarator_name_node)
14096            .is_none()
14097    {
14098        return false;
14099    }
14100    let Some(attribute_statement) = children.get(index + 1).copied() else {
14101        return false;
14102    };
14103    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
14104        .then(|| attribute_statement.named_child(0))
14105        .flatten()
14106        .filter(|child| child.kind() == "call_expression")
14107    else {
14108        return false;
14109    };
14110    let Some(attribute_name) = attribute_call
14111        .child_by_field_name("function")
14112        .filter(|function| function.kind() == "identifier")
14113        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
14114    else {
14115        return false;
14116    };
14117    if !cpp_export_macro_token(&attribute_name) {
14118        return false;
14119    }
14120    let Some(body) = children.get(index + 2).copied() else {
14121        return false;
14122    };
14123    body.kind() == "compound_statement"
14124        && body.child(0).is_some_and(|open| open.kind() == "{")
14125        && body
14126            .child(body.child_count().saturating_sub(1))
14127            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
14128        && declaration.end_byte() <= attribute_statement.start_byte()
14129        && attribute_statement.end_byte() <= body.start_byte()
14130}
14131
14132/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
14133/// types and specifiers followed by a declarator, over and over, with nothing
14134/// left over. A string-argument attribute macro is what strands them there, and
14135/// the walk indexes exactly what this reads, so admitting the region is safe
14136/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
14137/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
14138/// the whole class body.
14139fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
14140    if node.kind() != "ERROR" {
14141        return false;
14142    }
14143    let run = stranded_declaration_run(node, source);
14144    run.complete && !run.declarations.is_empty()
14145}
14146
14147fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
14148    let mut cursor = root.walk();
14149    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
14150    let mut saw_member = false;
14151    let mut index = 0;
14152    while index < children.len() {
14153        let child = children[index];
14154        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
14155            saw_member = true;
14156            index += 3;
14157            continue;
14158        }
14159        if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
14160            let Some(tree) = cpp_reparse_fragmented_class_body(
14161                source,
14162                fragmented.reparse_start,
14163                fragmented.reparse_end,
14164            ) else {
14165                return false;
14166            };
14167            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
14168                return false;
14169            }
14170            saw_member = true;
14171            index += 1;
14172            while index < children.len()
14173                && children[index].end_byte() <= fragmented.class_range.end_byte
14174            {
14175                index += 1;
14176            }
14177            continue;
14178        }
14179        match child.kind() {
14180            "comment" => {}
14181            "labeled_statement" => saw_member = true,
14182            "function_definition" => {
14183                if child.has_error()
14184                    && !cpp_reparsed_member_function_is_indexable(child, source)
14185                    && cpp_sentinel_macro_region(child, source).is_none()
14186                {
14187                    return false;
14188                }
14189                saw_member = true;
14190            }
14191            // The attribute a string-argument macro leaves as a call statement
14192            // of its own. It declares nothing; the member it decorated is the
14193            // sibling after it, checked on its own turn.
14194            "expression_statement" if is_string_attribute_macro_statement(child) => {}
14195            "ERROR"
14196                if (cpp_reparsed_member_error_is_indexable(child)
14197                    || cpp_reparsed_adjacent_copy_control_error(child, source)
14198                    || cpp_reparsed_stranded_member_error(child, source))
14199                    && (child
14200                        .next_named_sibling()
14201                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
14202                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
14203            {
14204                saw_member = true;
14205            }
14206            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
14207                saw_member = true;
14208            }
14209            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
14210                saw_member = true;
14211            }
14212            "expression_statement"
14213                if cpp_is_stray_semicolon(child, source)
14214                    && child.prev_named_sibling().is_some_and(|error| {
14215                        cpp_reparsed_member_error_is_indexable(error)
14216                            || cpp_reparsed_adjacent_copy_control_error(error, source)
14217                            || cpp_reparsed_stranded_member_error(error, source)
14218                    }) =>
14219            {
14220                saw_member = true;
14221            }
14222            "compound_statement"
14223                if cpp_reparsed_constructor_body_is_indexable(child, source)
14224                    || cpp_reparsed_attribute_requires_body(child, source) =>
14225            {
14226                saw_member = true;
14227            }
14228            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
14229            _ => return false,
14230        }
14231        index += 1;
14232    }
14233    saw_member
14234}
14235
14236/// Detect the malformed constructor shape that tree-sitter exposes as an
14237/// access-label statement followed by initializer-looking declarations. The
14238/// declarations are not class members: visiting their `location(loc)` and
14239/// `string(s)` function declarators would publish synthetic functions. The
14240/// export-class fallback keeps the original sibling nodes and therefore avoids
14241/// this parser artifact. The returned range identifies the real constructor
14242/// header, which can be reparsed independently as a structured declarator.
14243fn cpp_reparsed_synthetic_initializer_constructor_range(
14244    root: Node<'_>,
14245    class_name: &str,
14246    source: &str,
14247    constructor_end: usize,
14248) -> Option<std::ops::Range<usize>> {
14249    let mut stack = {
14250        let mut cursor = root.walk();
14251        root.named_children(&mut cursor).collect::<Vec<_>>()
14252    };
14253    while let Some(current) = stack.pop() {
14254        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
14255            current,
14256            class_name,
14257            source,
14258            constructor_end,
14259        ) {
14260            return Some(range);
14261        }
14262        if current.kind() == "ERROR" {
14263            let mut cursor = current.walk();
14264            stack.extend(current.named_children(&mut cursor));
14265        }
14266    }
14267    None
14268}
14269
14270/// Recover an inline constructor that a function-like export macro makes
14271/// tree-sitter merge with the following overload. In the reparsed class-body
14272/// region, the access label wraps one declaration whose ERROR contains the
14273/// constructor declarator and its base-initializer/body, while the declaration's
14274/// ordinary declarator is the following overload. Every boundary below comes
14275/// from that CST; no source syntax is reparsed by hand.
14276fn cpp_reparsed_merged_inline_constructor<'tree>(
14277    root: Node<'tree>,
14278    class_name: &str,
14279    source: &str,
14280) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
14281    let mut stack = vec![root];
14282    while let Some(current) = stack.pop() {
14283        if current.kind() != "labeled_statement" {
14284            let mut cursor = current.walk();
14285            stack.extend(current.named_children(&mut cursor));
14286            continue;
14287        }
14288        let declaration = current
14289            .named_children(&mut current.walk())
14290            .find(|child| child.kind() == "declaration")?;
14291        if declaration
14292            .child_by_field_name("type")
14293            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
14294        {
14295            continue;
14296        }
14297        let following = declaration
14298            .child_by_field_name("declarator")
14299            .and_then(extract_function_declarator)
14300            .and_then(cpp_function_declarator_name_node);
14301        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
14302            continue;
14303        }
14304        let mut declaration_cursor = declaration.walk();
14305        let Some(error) = declaration
14306            .named_children(&mut declaration_cursor)
14307            .find(|child| child.kind() == "ERROR")
14308        else {
14309            continue;
14310        };
14311        let mut error_cursor = error.walk();
14312        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
14313        let Some(constructor) = error_children.iter().copied().find(|child| {
14314            child.kind() == "function_declarator"
14315                && cpp_function_declarator_name_node(*child)
14316                    .is_some_and(|name| node_text(name, source).trim() == class_name)
14317        }) else {
14318            continue;
14319        };
14320        let Some(body) = error_children.iter().copied().find_map(|child| {
14321            (child.kind() == "init_declarator")
14322                .then(|| child.child_by_field_name("value"))
14323                .flatten()
14324                .filter(|value| value.kind() == "initializer_list")
14325        }) else {
14326            continue;
14327        };
14328        if constructor.end_byte() > body.start_byte() {
14329            continue;
14330        }
14331        return Some((constructor.start_byte()..body.end_byte(), body));
14332    }
14333    None
14334}
14335
14336fn cpp_reparsed_synthetic_initializer_constructor(
14337    node: Node<'_>,
14338    class_name: &str,
14339    source: &str,
14340    constructor_end: usize,
14341) -> Option<std::ops::Range<usize>> {
14342    if node.kind() != "labeled_statement" {
14343        return None;
14344    }
14345    let mut cursor = node.walk();
14346    let named = node
14347        .named_children(&mut cursor)
14348        .filter(|child| child.kind() != "comment")
14349        .collect::<Vec<_>>();
14350    let label = named.first()?;
14351    if label.kind() != "statement_identifier"
14352        || !matches!(
14353            node_text(*label, source).trim(),
14354            "public" | "private" | "protected"
14355        )
14356    {
14357        return None;
14358    }
14359    let call_error_index = named.iter().position(|child| {
14360        if child.kind() != "ERROR" {
14361            return false;
14362        }
14363        let mut stack = vec![*child];
14364        while let Some(current) = stack.pop() {
14365            if current.kind() == "call_expression"
14366                && current
14367                    .child_by_field_name("function")
14368                    .is_some_and(|function| {
14369                        function.kind() == "identifier"
14370                            && node_text(function, source).trim() == class_name
14371                    })
14372            {
14373                return true;
14374            }
14375            let mut cursor = current.walk();
14376            stack.extend(current.named_children(&mut cursor));
14377        }
14378        false
14379    })?;
14380    let constructor_call = {
14381        let mut stack = vec![named[call_error_index]];
14382        let mut found = None;
14383        while let Some(current) = stack.pop() {
14384            if current.kind() == "call_expression"
14385                && current
14386                    .child_by_field_name("function")
14387                    .is_some_and(|function| {
14388                        function.kind() == "identifier"
14389                            && node_text(function, source).trim() == class_name
14390                    })
14391            {
14392                found = Some(current);
14393                break;
14394            }
14395            let mut cursor = current.walk();
14396            stack.extend(current.named_children(&mut cursor));
14397        }
14398        found
14399    };
14400    let constructor_call = constructor_call?;
14401    named.iter().skip(call_error_index + 1).find(|child| {
14402        child.kind() == "declaration" && child.has_error() && {
14403            let mut cursor = child.walk();
14404            child.named_children(&mut cursor).any(|declarator| {
14405                declarator.kind() == "init_declarator"
14406                    && declarator
14407                        .child_by_field_name("declarator")
14408                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
14409                    && declarator
14410                        .child_by_field_name("value")
14411                        .is_some_and(|value| value.kind() == "initializer_list")
14412            })
14413        }
14414    })?;
14415    Some(constructor_call.start_byte()..constructor_end)
14416}
14417
14418fn cpp_reparsed_exact_constructor_declarator<'tree>(
14419    root: Node<'tree>,
14420    start: usize,
14421    class_name: &str,
14422    source: &str,
14423) -> Option<Node<'tree>> {
14424    let mut candidate = None;
14425    let mut stack = vec![root];
14426    while let Some(current) = stack.pop() {
14427        if current.kind() == "function_declarator"
14428            && current.start_byte() == start
14429            && cpp_function_declarator_name_node(current)
14430                .is_some_and(|name| node_text(name, source).trim() == class_name)
14431        {
14432            if candidate.is_some() {
14433                return None;
14434            }
14435            candidate = Some(current);
14436            continue;
14437        }
14438        let mut cursor = current.walk();
14439        stack.extend(current.named_children(&mut cursor));
14440    }
14441    candidate
14442}
14443
14444fn cpp_is_indexable_item_kind(kind: &str) -> bool {
14445    matches!(
14446        kind,
14447        "namespace_definition"
14448            | "class_specifier"
14449            | "struct_specifier"
14450            | "union_specifier"
14451            | "enum_specifier"
14452            | "function_definition"
14453            | "template_declaration"
14454            | "declaration"
14455            | "field_declaration"
14456            | "alias_declaration"
14457            | "static_assert_declaration"
14458            | "type_definition"
14459            | "using_declaration"
14460            | "linkage_specification"
14461            | "preproc_def"
14462            | "preproc_function_def"
14463            | "preproc_include"
14464            | "preproc_if"
14465            | "preproc_ifdef"
14466            | "preproc_call"
14467    )
14468}
14469
14470#[cfg(test)]
14471mod tests {
14472    use super::*;
14473    use crate::adapter::parse_cpp_file;
14474    use brokk_bifrost_core::analyzer::parsed_file::{
14475        finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
14476        start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
14477    };
14478    use std::fmt::Write;
14479
14480    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
14481        let mut parser = tree_sitter::Parser::new();
14482        parser
14483            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14484            .unwrap();
14485        let tree = parser.parse(source, None).unwrap();
14486        let file = ProjectFile::new(std::env::temp_dir(), name);
14487        parse_cpp_file(&file, source, &tree)
14488    }
14489
14490    #[test]
14491    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
14492        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
14493        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
14494        let mut macros = parsed
14495            .declarations()
14496            .iter()
14497            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
14498            .collect::<Vec<_>>();
14499        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
14500
14501        assert_eq!(macros.len(), 2, "{macros:#?}");
14502        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
14503        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
14504        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
14505        assert_eq!(
14506            parsed.declaration_ranges(macros[1])[0].start_byte,
14507            source.rfind("#define VALUE 2").expect("second definition")
14508        );
14509    }
14510
14511    #[test]
14512    fn identifies_export_macro_class_base_displaced_into_declarator() {
14513        let source = r#"#define PROJECT_API_
14514namespace project {
14515namespace internal {
14516template <typename T>
14517class Base {};
14518}
14519template <typename T>
14520class Wrapper;
14521template <>
14522class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
14523}
14524"#;
14525        let mut parser = tree_sitter::Parser::new();
14526        parser
14527            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14528            .unwrap();
14529        let tree = parser.parse(source, None).unwrap();
14530        let start = source.find("internal::Base<int>").expect("base");
14531        let mut base = tree
14532            .root_node()
14533            .descendant_for_byte_range(start, start + 8)
14534            .expect("base syntax");
14535        while base.kind() != "qualified_identifier" {
14536            base = base.parent().expect("qualified base ancestor");
14537        }
14538        assert!(
14539            is_recovered_exported_class_base_type_node(base, source),
14540            "{}",
14541            tree.root_node().to_sexp()
14542        );
14543    }
14544
14545    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
14546        let mut identities = parsed
14547            .declarations()
14548            .iter()
14549            .filter(|unit| unit.is_function())
14550            .map(|unit| {
14551                (
14552                    unit.fq_name(),
14553                    unit.signature().unwrap_or_default().to_string(),
14554                )
14555            })
14556            .collect::<Vec<_>>();
14557        identities.sort();
14558        identities
14559    }
14560
14561    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
14562    /// between `explicit` and a constructor name onto the name, producing a
14563    /// `qualified_identifier` whose `::` it had to invent. The declared member
14564    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
14565    /// macro in type position, was already recovered and is the control that
14566    /// both spellings agree.
14567    #[test]
14568    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
14569        let source = r#"class SIMD_4x26 final {
14570   public:
14571      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
14572      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
14573      int m_v;
14574};
14575"#;
14576        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
14577        assert_eq!(
14578            function_identities(&parsed),
14579            vec![
14580                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
14581                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
14582            ],
14583            "{:#?}",
14584            parsed.declarations()
14585        );
14586    }
14587
14588    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
14589    /// `ERROR` holding the wrapped declaration and every declaration after it
14590    /// until the parser recovers. All of them must be indexed, each with the
14591    /// byte range that spells it.
14592    #[test]
14593    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
14594        let source = r#"#include <cstdint>
14595struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
14596    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
14597                     struct llama_model * model,
14598              struct llama_context_params   params),
14599            "use llama_init_from_model instead");
14600    LLAMA_API int32_t llama_tokenize(
14601        const struct llama_vocab * vocab,
14602                      const char * text,
14603                            bool   parse_special);
14604    LLAMA_API int32_t llama_other(int a);
14605"#;
14606        let parsed = parse_cpp_declarations(source, "llama.h");
14607        assert_eq!(
14608            function_identities(&parsed),
14609            vec![
14610                (
14611                    "llama_new_context_with_model".to_string(),
14612                    "(struct llama_model *, struct llama_context_params)".to_string()
14613                ),
14614                ("llama_other".to_string(), "(int)".to_string()),
14615                (
14616                    "llama_tokenize".to_string(),
14617                    "(const struct llama_vocab *, const char *, bool)".to_string()
14618                ),
14619            ],
14620            "{:#?}",
14621            parsed.declarations()
14622        );
14623
14624        // Each recovered declaration owns the source that spells it, so
14625        // navigation lands on the declaration and not on the macro envelope.
14626        for (name, expected) in [
14627            (
14628                "llama_new_context_with_model",
14629                "LLAMA_API struct llama_context * llama_new_context_with_model(",
14630            ),
14631            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
14632            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
14633        ] {
14634            let unit = parsed
14635                .declarations()
14636                .iter()
14637                .find(|unit| unit.is_function() && unit.fq_name() == name)
14638                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
14639            let [range] = parsed.declaration_ranges(unit) else {
14640                panic!("{name} must have exactly one range");
14641            };
14642            let text = &source[range.start_byte..range.end_byte];
14643            assert!(
14644                text.starts_with(expected),
14645                "{name} range is {text:?}, expected it to start with {expected:?}"
14646            );
14647            assert!(
14648                text.ends_with(')') || text.ends_with(';'),
14649                "{name}: {text:?}"
14650            );
14651        }
14652    }
14653
14654    /// Negative controls for the same recovery: a macro invocation whose
14655    /// arguments are not a declaration recovers nothing, whether the parser
14656    /// keeps it clean, reads the arguments as a type, or reads them as a bare
14657    /// declarator.
14658    #[test]
14659    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
14660        for source in [
14661            "int before;\nFOO(1, 2);\nint after;\n",
14662            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
14663            "int before;\nMACRO(int a, int b);\nint after;\n",
14664            "DECLARE_HANDLE(HWND);\nint after;\n",
14665        ] {
14666            let parsed = parse_cpp_declarations(source, "macro-call.h");
14667            assert_eq!(
14668                function_identities(&parsed),
14669                Vec::new(),
14670                "{source:?} must declare no function: {:#?}",
14671                parsed.declarations()
14672            );
14673        }
14674    }
14675
14676    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
14677    /// makes tree-sitter read the macro as the member's type and its argument
14678    /// list as a parenthesized declarator, which then swallows the attributed
14679    /// member and the member written after it. Both are members.
14680    #[test]
14681    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
14682        let source = r#"#include <string_view>
14683namespace Botan {
14684class DL_Group final {
14685   public:
14686      DL_Group() = default;
14687      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
14688      DL_Group(std::string_view pem, int format);
14689      size_t get_p() const;
14690};
14691}
14692"#;
14693        let parsed = parse_cpp_declarations(source, "dl_group.h");
14694        assert_eq!(
14695            function_identities(&parsed),
14696            vec![
14697                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
14698                (
14699                    "Botan.DL_Group.DL_Group".to_string(),
14700                    "(std::string_view)".to_string()
14701                ),
14702                (
14703                    "Botan.DL_Group.DL_Group".to_string(),
14704                    "(std::string_view, int)".to_string()
14705                ),
14706                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
14707            ],
14708            "{:#?}",
14709            parsed.declarations()
14710        );
14711    }
14712
14713    /// #2552 shape 3, export-macro class. The class head macro makes the body a
14714    /// `compound_statement`, so members come from the region reparse, and one
14715    /// string-attribute member used to make that reparse unindexable -- which
14716    /// left the class with no members at all.
14717    #[test]
14718    fn an_export_macro_class_keeps_its_string_attribute_members() {
14719        let source = r#"#include <string_view>
14720namespace Botan {
14721class BOTAN_PUBLIC_API(2, 0) DL_Group final {
14722   public:
14723      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
14724      DL_Group(std::string_view pem, int format);
14725      size_t get_p() const;
14726};
14727}
14728"#;
14729        let parsed = parse_cpp_declarations(source, "dl_group.h");
14730        assert!(
14731            parsed
14732                .declarations()
14733                .iter()
14734                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
14735            "{:#?}",
14736            parsed.declarations()
14737        );
14738        assert_eq!(
14739            function_identities(&parsed),
14740            vec![
14741                (
14742                    "Botan.DL_Group.DL_Group".to_string(),
14743                    "(std::string_view)".to_string()
14744                ),
14745                (
14746                    "Botan.DL_Group.DL_Group".to_string(),
14747                    "(std::string_view, int)".to_string()
14748                ),
14749                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
14750            ],
14751            "{:#?}",
14752            parsed.declarations()
14753        );
14754    }
14755
14756    /// #2552 shape 3, the `= default` variant plus the access-label
14757    /// constructor. The attributed defaulted constructor separates cleanly, but
14758    /// it strands the next member in a bare `ERROR`, and a constructor written
14759    /// with a member-initializer list under `private:` dissolves into
14760    /// expression soup that the reparse recovers from its own source range.
14761    #[test]
14762    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
14763        let source = r#"namespace Botan {
14764class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
14765   public:
14766      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
14767      XMSS_Parameters(int oid, int len);
14768      size_t len() const;
14769
14770   private:
14771      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
14772            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
14773
14774      int m_oid;
14775      int m_wots_oid;
14776      size_t m_element_size;
14777      size_t m_tree_height;
14778};
14779}
14780"#;
14781        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
14782        let constructors = function_identities(&parsed)
14783            .into_iter()
14784            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
14785            .map(|(_, signature)| signature)
14786            .collect::<Vec<_>>();
14787        assert_eq!(
14788            constructors,
14789            vec![
14790                "()".to_string(),
14791                "(int, int)".to_string(),
14792                "(int, int, size_t, size_t)".to_string(),
14793            ],
14794            "{:#?}",
14795            parsed.declarations()
14796        );
14797    }
14798
14799    /// Negative control for the same rule: a real qualified name spells its
14800    /// `::` in the source, so the separator is present rather than MISSING and
14801    /// the out-of-line definition keeps its owner.
14802    #[test]
14803    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
14804        let source = r#"namespace shell {
14805struct Outer {
14806   struct Inner {
14807      Inner(int v);
14808      void run(int v);
14809   };
14810};
14811Outer::Inner::Inner(int v) {}
14812void Outer::Inner::run(int v) {}
14813}
14814"#;
14815        let parsed = parse_cpp_declarations(source, "outer.cpp");
14816        let names = function_identities(&parsed)
14817            .into_iter()
14818            .map(|(fq_name, _)| fq_name)
14819            .collect::<Vec<_>>();
14820        assert!(
14821            names
14822                .iter()
14823                .all(|name| name.starts_with("shell.Outer$Inner.")),
14824            "{names:#?}"
14825        );
14826    }
14827
14828    #[test]
14829    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
14830        let source = r#"namespace control {
14831template <typename T>
14832class AnySpan;
14833template <typename T>
14834class ABSL_ATTRIBUTE_VIEW AnySpan {
14835 public:
14836  int begin() const;
14837};
14838}
14839
14840namespace absl {
14841ABSL_NAMESPACE_BEGIN
14842template <typename T>
14843class ABSL_ATTRIBUTE_VIEW Span {
14844 public:
14845  int begin() const;
14846  int back() const;
14847};
14848
14849int begin();
14850int back();
14851}
14852"#;
14853        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
14854        let declarations = parsed.declarations();
14855        assert!(
14856            declarations
14857                .iter()
14858                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
14859        );
14860        for method in ["begin", "back"] {
14861            assert!(declarations.iter().any(|unit| {
14862                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
14863            }));
14864            assert!(
14865                declarations.iter().any(|unit| {
14866                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
14867                })
14868            );
14869        }
14870        assert!(
14871            declarations
14872                .iter()
14873                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
14874        );
14875        assert!(
14876            declarations
14877                .iter()
14878                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
14879        );
14880        assert!(
14881            declarations
14882                .iter()
14883                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
14884        );
14885    }
14886
14887    #[test]
14888    fn explicit_global_member_definition_has_canonical_package_boundary() {
14889        let source = r#"
14890namespace arangodb::aql {
14891class ExecutionPlan {
14892 public:
14893  template<class... Args> Node* createNode(Args&&... args);
14894};
14895}
14896
14897template<class... Args>
14898Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
14899"#;
14900        let parsed = parse_cpp_declarations(source, "global-member.cpp");
14901
14902        assert!(parsed.declarations().iter().any(|unit| {
14903            unit.is_function()
14904                && unit.package_name() == "arangodb::aql"
14905                && unit.short_name() == "ExecutionPlan.createNode"
14906                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
14907        }));
14908    }
14909
14910    #[test]
14911    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
14912        let source = r#"
14913#ifndef TINYXML2_INCLUDED
14914#define TINYXML2_INCLUDED
14915namespace tinyxml2 {
14916class TINYXML2_LIB XMLUtil {
14917 public:
14918  static const char* SkipWhiteSpace(const char* p) {
14919    while (*p) {
14920      if (*p == ' ') {
14921        ++p;
14922      }
14923    }
14924    return p;
14925  }
14926  static bool StringEqual(const char* p, const char* q) {
14927    return p == q;
14928  }
14929  class TINYXML2_LIB Helper {
14930   public:
14931    void Touch();
14932  };
14933  static void ToStr(int value, char* buffer);
14934 private:
14935  static const char* writeBoolTrue;
14936};
14937
14938class TINYXML2_LIB XMLNode {
14939 public:
14940  virtual XMLNode* ShallowClone() const = 0;
14941  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
14942};
14943}
14944#endif
14945"#;
14946        let mut parser = tree_sitter::Parser::new();
14947        parser
14948            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14949            .unwrap();
14950        let tree = parser.parse(source, None).unwrap();
14951        let mut boundary_found = false;
14952        walk_named_tree_preorder(tree.root_node(), true, |node| {
14953            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
14954                && name == "XMLUtil"
14955            {
14956                boundary_found = fragmented_export_sibling_class_boundary(node, source)
14957                    .and_then(|boundary| {
14958                        recover_exported_class_function_definition(boundary, source)
14959                    })
14960                    .is_some_and(|(_, name, _)| name == "XMLNode");
14961            }
14962            WalkControl::Continue
14963        });
14964        assert!(
14965            boundary_found,
14966            "fixture must exercise the recovered sibling boundary"
14967        );
14968
14969        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
14970        assert!(
14971            parsed
14972                .declarations()
14973                .iter()
14974                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
14975            "{:#?}",
14976            parsed.declarations()
14977        );
14978        assert!(
14979            parsed
14980                .declarations()
14981                .iter()
14982                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
14983            "{:#?}",
14984            parsed.declarations()
14985        );
14986        assert!(parsed.declarations().iter().any(|unit| {
14987            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
14988        }));
14989        assert!(
14990            parsed
14991                .declarations()
14992                .iter()
14993                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
14994        );
14995        assert!(
14996            parsed
14997                .declarations()
14998                .iter()
14999                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
15000        );
15001    }
15002
15003    #[test]
15004    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
15005        // Clang's diagnostic suite intentionally contains this ill-formed
15006        // spelling. The analyzer must retain the parser's explicit-global AST
15007        // boundary instead of constructing `cwg311::::cwg311::X`.
15008        let parsed = parse_cpp_declarations(
15009            r#"
15010namespace cwg311 {
15011namespace X { namespace Y {} }
15012namespace ::cwg311::X {}
15013}
15014"#,
15015            "explicit-global-namespace.cpp",
15016        );
15017
15018        assert!(parsed.declarations().iter().any(|unit| {
15019            unit.kind() == CodeUnitType::Module
15020                && unit.short_name() == "cwg311::X"
15021                && unit.fq_name() == "cwg311::X"
15022        }));
15023        assert!(
15024            parsed
15025                .declarations()
15026                .iter()
15027                .all(|unit| !unit.short_name().contains("::::")),
15028            "recovered namespace names must not retain empty scope components: {:#?}",
15029            parsed.declarations()
15030        );
15031    }
15032
15033    #[test]
15034    fn repeated_scope_separator_does_not_create_empty_function_owner() {
15035        let scope = ScopeInfo {
15036            package_name: "X".to_string(),
15037            module: None,
15038            class_unit: None,
15039            template_signature: None,
15040            template_metadata: None,
15041            declarations_are_fields: false,
15042            recovered_specialization_member_scope: false,
15043            visible_using_namespaces: Vec::new(),
15044        };
15045
15046        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
15047
15048        assert!(owner.is_none());
15049        assert_eq!(name, "doit");
15050        assert_eq!(package, "X");
15051    }
15052
15053    #[test]
15054    fn trailing_decltype_expression_is_not_a_function_declarator() {
15055        let source = r#"
15056namespace boost { namespace detail {
15057#if ! defined(BOOST_NO_SFINAE_EXPR) && \
15058    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
15059    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
15060#define BOOST_THREAD_PROVIDES_INVOKE
15061#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
15062template <class Fp, class A0, class ...Args>
15063inline auto
15064invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
15065       BOOST_THREAD_RV_REF(Args) ...args)
15066    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
15067{
15068    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
15069}
15070#endif
15071#endif
15072}}
15073"#;
15074        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
15075
15076        assert!(
15077            parsed
15078                .declarations()
15079                .iter()
15080                .all(|unit| unit.short_name() != ".*f")
15081        );
15082    }
15083
15084    fn find_class_named<'tree>(
15085        root: Node<'tree>,
15086        source: &str,
15087        expected_name: &str,
15088    ) -> Option<Node<'tree>> {
15089        let mut stack = vec![root];
15090        while let Some(node) = stack.pop() {
15091            if node.kind() == "class_specifier"
15092                && node
15093                    .child_by_field_name("name")
15094                    .is_some_and(|name| node_text(name, source) == expected_name)
15095            {
15096                return Some(node);
15097            }
15098            let mut cursor = node.walk();
15099            stack.extend(node.named_children(&mut cursor));
15100        }
15101        None
15102    }
15103
15104    #[test]
15105    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
15106        let source = r#"EXPORT void definition(struct Value value) {}
15107EXPORT void prototype(struct Value value);
15108"#;
15109        let mut parser = tree_sitter::Parser::new();
15110        parser
15111            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15112            .unwrap();
15113        let tree = parser.parse(source, None).unwrap();
15114        let root = tree.root_node();
15115        let mut cursor = root.walk();
15116        let callables = root
15117            .named_children(&mut cursor)
15118            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
15119            .collect::<Vec<_>>();
15120
15121        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
15122        for callable in callables {
15123            assert!(callable.has_error(), "fixture must exercise error recovery");
15124            assert!(
15125                cpp_sentinel_macro_parts(callable, source).is_none(),
15126                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
15127            );
15128        }
15129    }
15130
15131    #[test]
15132    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
15133        let source = r#"namespace absl {
15134ABSL_NAMESPACE_BEGIN
15135// Generate a floating-point variate conforming to a Beta distribution:
15136template <typename RealType = double>
15137class beta_distribution {
15138 public:
15139  using result_type = RealType;
15140
15141
15142  beta_distribution() : beta_distribution(1) {}
15143
15144  explicit beta_distribution(result_type alpha, result_type beta = 1)
15145      : param_(alpha, beta) {}
15146
15147  explicit beta_distribution(const param_type& p) : param_(p) {}
15148
15149  void reset() {}
15150
15151  // Generating functions
15152  template <typename URBG>
15153  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
15154    return (*this)(g, param_);
15155  }
15156
15157};
15158ABSL_NAMESPACE_END
15159}  // namespace absl
15160"#;
15161        let mut parser = tree_sitter::Parser::new();
15162        parser
15163            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15164            .unwrap();
15165        let tree = parser.parse(source, None).unwrap();
15166        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
15167        let body = namespace
15168            .child_by_field_name("body")
15169            .expect("fixture namespace body");
15170        let sentinel = body.named_child(0).expect("sentinel envelope");
15171        let callable = sentinel
15172            .child_by_field_name("declarator")
15173            .and_then(extract_function_declarator)
15174            .and_then(cpp_function_declarator_name_node)
15175            .expect("preserved callable name");
15176
15177        assert_eq!(sentinel.kind(), "function_definition");
15178        assert_eq!(callable.kind(), "operator_name");
15179        assert!(
15180            cpp_sentinel_macro_parts(sentinel, source).is_some(),
15181            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
15182        );
15183    }
15184
15185    #[test]
15186    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
15187        let source = r#"namespace absl {
15188ABSL_NAMESPACE_BEGIN
15189// absl::discrete_distribution
15190//
15191// A discrete distribution produces random integers i, where 0 <= i < n
15192template <typename IntType = int>
15193class discrete_distribution {
15194 public:
15195  using result_type = IntType;
15196  class param_type {
15197   public:
15198    param_type() { init(); }
15199    template <typename InputIterator>
15200    explicit param_type(InputIterator begin, InputIterator end)
15201        : p_(begin, end) {
15202      init();
15203    }
15204  };
15205  discrete_distribution() : param_() {}
15206  explicit discrete_distribution(const param_type& p) : param_(p) {}
15207};
15208ABSL_NAMESPACE_END
15209}  // namespace absl
15210"#;
15211        let mut parser = tree_sitter::Parser::new();
15212        parser
15213            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15214            .unwrap();
15215        let tree = parser.parse(source, None).unwrap();
15216        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
15217        let body = namespace
15218            .child_by_field_name("body")
15219            .expect("fixture namespace body");
15220        let sentinel = body.named_child(0).expect("sentinel envelope");
15221        let callable = sentinel
15222            .child_by_field_name("declarator")
15223            .and_then(extract_function_declarator)
15224            .and_then(cpp_function_declarator_name_node)
15225            .expect("preserved callable name");
15226
15227        assert_eq!(sentinel.kind(), "function_definition");
15228        assert_eq!(callable.kind(), "identifier");
15229        assert!(
15230            cpp_sentinel_macro_parts(sentinel, source).is_some(),
15231            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
15232        );
15233    }
15234
15235    #[test]
15236    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
15237        let source = r#"
15238#define CPPCHECKLIB
15239class Library {
15240    struct Container {
15241        CPPCHECKLIB static std::string toString(Yield yield);
15242        CPPCHECKLIB static std::string toString(Action action);
15243    };
15244};
15245"#;
15246        let mut parser = tree_sitter::Parser::new();
15247        parser
15248            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15249            .unwrap();
15250        let tree = parser.parse(source, None).unwrap();
15251        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
15252        let parsed = parse_cpp_file(&file, source, &tree);
15253        assert!(
15254            parsed
15255                .declarations()
15256                .iter()
15257                .all(|unit| unit.fq_name() != "Library$Container.std"),
15258            "the qualified return-type namespace must not become a field: {:#?}",
15259            parsed.declarations()
15260        );
15261        for expected in ["(Yield)", "(Action)"] {
15262            assert!(
15263                parsed.declarations().iter().any(|unit| {
15264                    unit.is_function()
15265                        && unit.fq_name() == "Library$Container.toString"
15266                        && unit.signature() == Some(expected)
15267                }),
15268                "recovered toString overload {expected} is missing: {:#?}",
15269                parsed.declarations()
15270            );
15271        }
15272    }
15273
15274    #[test]
15275    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
15276        let source = r#"
15277#define SIMPLECPP_LIB
15278namespace simplecpp {
15279using TokenString = std::string;
15280struct Location { int line{}; };
15281class SIMPLECPP_LIB Token {
15282  TokenString prefix;
15283  void prefix_method() {}
15284 public:
15285  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
15286      whitespaceahead(wsahead), location(loc), string(s)
15287      // The comment must not hide the constructor body from recovery.
15288      {
15289      flags();
15290  }
15291  TokenString string;
15292  bool whitespaceahead;
15293  Location location;
15294  Token *previous{};
15295 private:
15296  void flags() {
15297      whitespaceahead = true;
15298  }
15299};
15300}
15301"#;
15302        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
15303
15304        let location_fields = parsed
15305            .declarations()
15306            .iter()
15307            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
15308            .collect::<Vec<_>>();
15309        assert_eq!(
15310            location_fields.len(),
15311            1,
15312            "location should have one class-owned declaration: {:#?}",
15313            parsed.declarations()
15314        );
15315        assert!(
15316            location_fields[0].is_field(),
15317            "location has wrong kind: {:#?}",
15318            parsed.declarations()
15319        );
15320        assert!(
15321            parsed.declarations().iter().all(|unit| {
15322                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
15323            })
15324        );
15325        assert!(
15326            parsed.declarations().iter().all(|unit| {
15327                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
15328            })
15329        );
15330        assert!(
15331            parsed
15332                .declarations()
15333                .iter()
15334                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
15335        );
15336        assert!(
15337            parsed
15338                .declarations()
15339                .iter()
15340                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
15341            "the recovered class must retain its constructor: {:#?}",
15342            parsed.declarations()
15343        );
15344        assert!(
15345            parsed
15346                .declarations()
15347                .iter()
15348                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
15349        );
15350        assert!(parsed.declarations().iter().any(|unit| {
15351            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
15352        }));
15353        let constructor = parsed
15354            .declarations()
15355            .iter()
15356            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
15357            .expect("recovered constructor");
15358        let constructor_start = source.find("Token(const").expect("constructor start");
15359        let constructor_end = source
15360            .get(
15361                ..source
15362                    .find("  TokenString string;")
15363                    .expect("constructor end"),
15364            )
15365            .expect("constructor slice")
15366            .trim_end()
15367            .len();
15368        assert!(
15369            parsed
15370                .navigation_ranges
15371                .get(constructor)
15372                .is_some_and(|ranges| {
15373                    ranges.iter().any(|range| {
15374                        range.start_byte == constructor_start && range.end_byte == constructor_end
15375                    })
15376                }),
15377            "constructor navigation must span the full body: {:#?}",
15378            parsed.navigation_ranges
15379        );
15380        assert_eq!(
15381            parsed
15382                .signature_metadata
15383                .get(constructor)
15384                .and_then(|metadata| metadata.first())
15385                .and_then(SignatureMetadata::callable_linkage),
15386            Some(CallableLinkage::External)
15387        );
15388        let token_class = parsed
15389            .declarations()
15390            .iter()
15391            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
15392            .expect("recovered Token class");
15393        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
15394        assert!(
15395            parsed
15396                .navigation_ranges
15397                .get(token_class)
15398                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
15399            "class navigation must include the terminating semicolon: {:#?}",
15400            parsed.navigation_ranges
15401        );
15402    }
15403
15404    #[test]
15405    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
15406        let source = r#"
15407#define SIMPLECPP_LIB
15408namespace simplecpp {
15409using TokenString = std::string;
15410class Macro;
15411struct Location {
15412  unsigned int fileIndex{};
15413  unsigned int line{};
15414  unsigned int col{};
15415};
15416struct Output {
15417  int type;
15418};
15419class SIMPLECPP_LIB Token {
15420 public:
15421  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
15422      whitespaceahead(wsahead), location(loc), string(s) {
15423      flags();
15424  }
15425  Token(const Token &tok) :
15426      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
15427      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
15428      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
15429  Token &operator=(const Token &tok) = delete;
15430  const TokenString& str() const { return string; }
15431  void setstr(const std::string &s) { string = s; flags(); }
15432  bool isOneOf(const char ops[]) const;
15433  TokenString macro;
15434  char op;
15435  bool comment;
15436  bool name;
15437  bool number;
15438  bool whitespaceahead;
15439  Location location;
15440  Token *previous{};
15441  Token *next{};
15442 private:
15443  void flags() {
15444      name = !string.empty();
15445      comment = false;
15446      number = false;
15447      op = 0;
15448  }
15449  TokenString string;
15450};
15451}
15452struct Following {
15453  int type;
15454};
15455class SIMPLECPP_LIB Later {
15456 public:
15457  Later(int value) : value(value) {}
15458  int value;
15459};
15460"#;
15461        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
15462        assert!(
15463            parsed
15464                .declarations()
15465                .iter()
15466                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
15467        );
15468        assert!(
15469            !parsed
15470                .declarations()
15471                .iter()
15472                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
15473        );
15474        assert!(
15475            parsed
15476                .declarations()
15477                .iter()
15478                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
15479        );
15480        assert!(
15481            !parsed
15482                .declarations()
15483                .iter()
15484                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
15485        );
15486        assert!(
15487            parsed
15488                .declarations()
15489                .iter()
15490                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
15491        );
15492        assert!(
15493            parsed
15494                .declarations()
15495                .iter()
15496                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
15497        );
15498        assert!(
15499            parsed
15500                .declarations()
15501                .iter()
15502                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
15503        );
15504        assert!(
15505            parsed
15506                .declarations()
15507                .iter()
15508                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
15509        );
15510        assert!(
15511            parsed
15512                .declarations()
15513                .iter()
15514                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
15515        );
15516        assert!(
15517            parsed
15518                .declarations()
15519                .iter()
15520                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
15521        );
15522        assert!(parsed.declarations().iter().all(|unit| {
15523            !matches!(
15524                unit.fq_name().as_str(),
15525                "simplecpp.Token.Following" | "simplecpp.Token.Later"
15526            )
15527        }));
15528        assert!(
15529            !parsed
15530                .declarations()
15531                .iter()
15532                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
15533            "the following struct must remain outside the recovered Token class"
15534        );
15535    }
15536
15537    #[test]
15538    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
15539        let source = r#"
15540#define SIMPLECPP_LIB
15541namespace {
15542namespace simplecpp {
15543using TokenString = std::string;
15544struct Location { int line{}; };
15545class SIMPLECPP_LIB HiddenToken {
15546 public:
15547  HiddenToken(const TokenString &s, const Location &loc) :
15548      location(loc), string(s) {
15549      flags();
15550  }
15551  TokenString string;
15552  Location location;
15553  HiddenToken *previous{};
15554 private:
15555  void flags() {}
15556};
15557}
15558}
15559"#;
15560        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
15561        let constructor = parsed
15562            .declarations()
15563            .iter()
15564            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
15565            .expect("recovered anonymous-namespace constructor");
15566        assert_eq!(
15567            parsed
15568                .signature_metadata
15569                .get(constructor)
15570                .and_then(|metadata| metadata.first())
15571                .and_then(SignatureMetadata::callable_linkage),
15572            Some(CallableLinkage::Internal)
15573        );
15574    }
15575
15576    #[test]
15577    fn macro_qualified_static_field_keeps_real_declarator() {
15578        let source = r#"#define JSON_INLINE_VARIABLE
15579struct Reader {
15580static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
15581static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
15582static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
15583};"#;
15584        let mut parser = tree_sitter::Parser::new();
15585        parser
15586            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15587            .unwrap();
15588        let tree = parser.parse(source, None).unwrap();
15589        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
15590        let parsed = parse_cpp_file(&file, source, &tree);
15591        for expected in [
15592            "Reader.npos",
15593            "Reader.other",
15594            "Reader.pointer",
15595            "Reader.reference",
15596        ] {
15597            assert!(
15598                parsed
15599                    .declarations()
15600                    .iter()
15601                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
15602                "real macro-decorated field {expected} is missing: {:#?}",
15603                parsed.declarations()
15604            );
15605        }
15606        assert!(
15607            parsed
15608                .declarations()
15609                .iter()
15610                .all(|unit| unit.fq_name() != "Reader.std"),
15611            "qualified type prefix became a pseudo-field: {:#?}",
15612            parsed.declarations()
15613        );
15614        let root = tree.root_node();
15615        let mut stack = vec![root];
15616        let mut signatures = Vec::new();
15617        while let Some(current) = stack.pop() {
15618            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
15619            {
15620                signatures.extend(
15621                    declarators
15622                        .into_iter()
15623                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
15624                );
15625            }
15626            let mut cursor = current.walk();
15627            stack.extend(current.named_children(&mut cursor));
15628        }
15629        signatures.sort();
15630        assert_eq!(
15631            signatures,
15632            [
15633                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
15634                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
15635                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
15636                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
15637            ]
15638        );
15639    }
15640
15641    fn member_function_linkage(source: &str) -> CallableLinkage {
15642        let mut parser = tree_sitter::Parser::new();
15643        parser
15644            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15645            .unwrap();
15646        let tree = parser.parse(source, None).unwrap();
15647        let ancestry = ParentIndex::new(tree.root_node());
15648        let mut stack = vec![tree.root_node()];
15649        while let Some(node) = stack.pop() {
15650            if node.kind() == "function_definition" {
15651                let mut current = node.parent();
15652                while let Some(parent) = current {
15653                    if matches!(
15654                        parent.kind(),
15655                        "class_specifier" | "struct_specifier" | "union_specifier"
15656                    ) {
15657                        return cpp_callable_linkage(node, source, &ancestry);
15658                    }
15659                    current = parent.parent();
15660                }
15661            }
15662            let mut cursor = node.walk();
15663            stack.extend(node.named_children(&mut cursor));
15664        }
15665        panic!("fixture has no member function definition");
15666    }
15667
15668    #[test]
15669    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
15670        assert_eq!(
15671            member_function_linkage("struct Named { int method() { return 1; } };"),
15672            CallableLinkage::External
15673        );
15674        assert_eq!(
15675            member_function_linkage(
15676                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
15677            ),
15678            CallableLinkage::Internal
15679        );
15680        assert_eq!(
15681            member_function_linkage("struct { int method() { return 1; } } instance;"),
15682            CallableLinkage::Internal
15683        );
15684        assert_eq!(
15685            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
15686            CallableLinkage::Internal
15687        );
15688    }
15689
15690    #[test]
15691    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
15692        let source = r#"
15693#ifndef PROTON_VALUE_HPP
15694#define PROTON_VALUE_HPP
15695namespace proton {
15696namespace internal {
15697class value_base {
15698  protected:
15699    internal::data& data();
15700    internal::data data_;
15701  friend class codec::encoder;
15702  friend class codec::decoder;
15703};
15704}
15705class value : public internal::value_base, private internal::comparable<value> {
15706  private:
15707    template<class T, class U=void> struct assignable :
15708        public std::enable_if<codec::is_encodable<T>::value, U> {};
15709    template<class U> struct assignable<value, U> {};
15710  public:
15711    PN_CPP_EXTERN value();
15712    PN_CPP_EXTERN value(const value&);
15713    PN_CPP_EXTERN value& operator=(const value&);
15714    PN_CPP_EXTERN value(value&&);
15715    PN_CPP_EXTERN value& operator=(value&&);
15716    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
15717    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
15718        codec::encoder e(*this);
15719        e << x;
15720        return *this;
15721    }
15722    PN_CPP_EXTERN type_id type() const;
15723    PN_CPP_EXTERN bool empty() const;
15724    PN_CPP_EXTERN void clear();
15725    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
15726    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
15727  friend PN_CPP_EXTERN void swap(value&, value&);
15728  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
15729  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
15730  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
15731    value(pn_data_t* d);
15732    void reset(pn_data_t* d = 0);
15733};
15734}
15735#endif
15736"#;
15737        let mut parser = tree_sitter::Parser::new();
15738        parser
15739            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15740            .unwrap();
15741        let tree = parser.parse(source, None).unwrap();
15742        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
15743        let parsed = parse_cpp_file(&file, source, &tree);
15744        let macro_constructors = parsed
15745            .signature_metadata
15746            .iter()
15747            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
15748            .flat_map(|(_, metadata)| metadata)
15749            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
15750            .collect::<Vec<_>>();
15751
15752        assert_eq!(
15753            macro_constructors.len(),
15754            3,
15755            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
15756            parsed.declarations()
15757        );
15758        assert!(
15759            macro_constructors.iter().all(|metadata| {
15760                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
15761            }),
15762            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
15763        );
15764    }
15765
15766    #[test]
15767    fn recovered_export_class_typedef_uses_displaced_alias_name() {
15768        let source = r#"
15769namespace spi {
15770class Filter {
15771public:
15772    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
15773};
15774}
15775namespace filter {
15776class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
15777{
15778public:
15779    typedef spi::Filter BASE_CLASS;
15780    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
15781    BEGIN_LOG4CXX_CAST_MAP()
15782    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
15783    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
15784    END_LOG4CXX_CAST_MAP()
15785    FilterDecision decide() const;
15786};
15787}
15788"#;
15789        let mut parser = tree_sitter::Parser::new();
15790        parser
15791            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15792            .unwrap();
15793        let tree = parser.parse(source, None).unwrap();
15794        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
15795        let parsed = parse_cpp_file(&file, source, &tree);
15796        assert!(
15797            parsed.declarations().iter().any(|unit| {
15798                unit.is_class()
15799                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
15800                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
15801            }),
15802            "the displaced typedef alias must retain its declared name: {:#?}",
15803            parsed.declarations()
15804        );
15805        assert!(
15806            parsed
15807                .declarations()
15808                .iter()
15809                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
15810            "the qualified underlying type must not become a false nested alias: {:#?}",
15811            parsed.declarations()
15812        );
15813    }
15814
15815    #[test]
15816    fn exported_single_base_recovery_uses_displaced_class_name() {
15817        let source = r#"
15818class CORE_EXPORT QgsPoint : public AbstractGeometry
15819{
15820    Q_GADGET
15821
15822    Q_PROPERTY( double x READ x WRITE setX )
15823    Q_PROPERTY( double y READ y WRITE setY )
15824    Q_PROPERTY( double z READ z WRITE setZ )
15825    Q_PROPERTY( double m READ m WRITE setM )
15826
15827  public:
15828#ifndef SIP_RUN
15829    QgsPoint(
15830      double x = std::numeric_limits<double>::quiet_NaN(),
15831      double y = std::numeric_limits<double>::quiet_NaN(),
15832      double z = std::numeric_limits<double>::quiet_NaN(),
15833      double m = std::numeric_limits<double>::quiet_NaN(),
15834      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
15835    );
15836#else
15837    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 )];
15838    % MethodCode
15839    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
15840    {
15841      int state;
15842      sipIsErr = 0;
15843      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
15844      if ( !sipIsErr )
15845      {
15846        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
15847      }
15848      sipReleaseType( p, sipType_QgsPointXY, state );
15849    }
15850    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
15851    {
15852      int state;
15853      sipIsErr = 0;
15854
15855      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
15856      if ( !sipIsErr )
15857      {
15858        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
15859      }
15860      sipReleaseType( p, sipType_QPointF, state );
15861    }
15862    else if (
15863      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
15864      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
15865      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
15866      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
15867    {
15868      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
15869      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
15870      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
15871      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
15872      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
15873      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
15874    }
15875    else // Invalid ctor arguments
15876    {
15877      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
15878      sipIsErr = 1;
15879    }
15880    % End
15881#endif
15882
15883    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
15884    explicit QgsPoint( QPointF p ) SIP_SKIP;
15885    explicit QgsPoint(
15886      Qgis::WkbType wkbType,
15887      double x = std::numeric_limits<double>::quiet_NaN(),
15888      double y = std::numeric_limits<double>::quiet_NaN(),
15889      double z = std::numeric_limits<double>::quiet_NaN(),
15890      double m = std::numeric_limits<double>::quiet_NaN()
15891    ) SIP_SKIP;
15892    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
15893    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
15894    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
15895#ifndef SIP_RUN
15896  private:
15897    bool fuzzyHelper(
15898      double epsilon,
15899      const AbstractGeometry &other,
15900      bool is3DFlag,
15901      bool isMeasureFlag
15902    ) const
15903    {
15904      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
15905    }
15906#endif
15907};
15908class Ordinary : public Base { public: Ordinary(); };
15909class API_EXPORT Plain { public: Plain(); };
15910class API_EXPORT : public Base {};
15911class
15912PN_CPP_CLASS_EXTERN Sender : public Link {
15913    Sender();
15914};
15915class thread_ctx_t {};
15916class ctx_t ZMQ_FINAL : public thread_ctx_t {
15917    bool start();
15918};
15919"#;
15920        let mut parser = tree_sitter::Parser::new();
15921        parser
15922            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15923            .unwrap();
15924        let tree = parser.parse(source, None).unwrap();
15925        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
15926        let parsed = parse_cpp_file(&file, source, &tree);
15927        let declarations = parsed.declarations();
15928
15929        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
15930            assert!(
15931                declarations
15932                    .iter()
15933                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
15934                "missing recovered class {expected}: {declarations:#?}"
15935            );
15936        }
15937        let qgs_point = declarations
15938            .iter()
15939            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
15940            .expect("recovered QgsPoint class");
15941        assert_eq!(
15942            parsed.raw_supertypes.get(qgs_point),
15943            Some(&vec!["AbstractGeometry".to_string()]),
15944            "single-base export recovery must retain its displaced base"
15945        );
15946        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
15947        assert!(
15948            parsed
15949                .navigation_ranges
15950                .get(qgs_point)
15951                .is_some_and(|ranges| {
15952                    !ranges.is_empty()
15953                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
15954                }),
15955            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
15956            parsed.navigation_ranges.get(qgs_point)
15957        );
15958        let sender = declarations
15959            .iter()
15960            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
15961            .expect("recovered Sender class");
15962        assert_eq!(
15963            parsed.raw_supertypes.get(sender),
15964            Some(&vec!["Link".to_string()]),
15965            "post-declarator export recovery must retain its displaced base"
15966        );
15967        let ctx = declarations
15968            .iter()
15969            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
15970            .expect("recovered ctx_t class");
15971        assert_eq!(
15972            parsed.raw_supertypes.get(ctx),
15973            Some(&vec!["thread_ctx_t".to_string()]),
15974            "postfix export-macro recovery must retain its displaced base"
15975        );
15976        assert!(
15977            declarations.iter().any(|unit| {
15978                unit.is_function()
15979                    && unit.fq_name() == "QgsPoint.QgsPoint"
15980                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
15981            }),
15982            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
15983        );
15984        assert!(
15985            declarations.iter().all(|unit| {
15986                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
15987            }),
15988            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
15989        );
15990    }
15991
15992    #[test]
15993    fn function_like_export_macro_classes_keep_names_and_base_edges() {
15994        // Every sibling shape tree-sitter produces after the `class MACRO(2, 0)`
15995        // error: a plain body, a single base, `final` without a base, `final`
15996        // with one base, and `final` with a comma-separated base list.
15997        let source = r#"
15998namespace api {
15999class PROJECT_PUBLIC_API(2, 0) Prelude {
16000  public:
16001    Prelude();
16002};
16003class PROJECT_PUBLIC_API(2, 0) Base {
16004  public:
16005    Base(int value);
16006};
16007class PROJECT_PUBLIC_API(2, 0) Mixin {
16008  public:
16009    Mixin();
16010};
16011class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
16012  public:
16013    Adopted(int value);
16014};
16015class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
16016  public:
16017    Derived(int value);
16018};
16019class PROJECT_PUBLIC_API(2, 0) Solo final {
16020  public:
16021    Solo();
16022};
16023class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
16024  public:
16025    Blended(int value);
16026};
16027class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
16028  public:
16029    Woven(int value);
16030};
16031} // namespace api
16032"#;
16033        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
16034        let declarations = parsed.declarations();
16035        let class_named = |name: &str| {
16036            declarations
16037                .iter()
16038                .find(|unit| unit.is_class() && unit.fq_name() == name)
16039                .unwrap_or_else(|| {
16040                    panic!("missing function-like export macro class {name}: {declarations:#?}")
16041                })
16042        };
16043        let base = class_named("api.Base");
16044        class_named("api.Prelude");
16045        class_named("api.Mixin");
16046
16047        assert_eq!(
16048            parsed.raw_supertypes.get(class_named("api.Adopted")),
16049            Some(&vec!["Base".to_string()])
16050        );
16051        assert_eq!(
16052            parsed.raw_supertypes.get(class_named("api.Derived")),
16053            Some(&vec!["Base".to_string()])
16054        );
16055        assert_eq!(
16056            parsed.raw_supertypes.get(class_named("api.Solo")),
16057            None,
16058            "a final class without a base list must not invent a supertype"
16059        );
16060        assert_eq!(
16061            parsed.raw_supertypes.get(class_named("api.Blended")),
16062            Some(&vec!["Base".to_string(), "Mixin".to_string()])
16063        );
16064        assert_eq!(
16065            parsed.raw_supertypes.get(class_named("api.Woven")),
16066            Some(&vec!["Base".to_string(), "Mixin".to_string()])
16067        );
16068        assert!(
16069            declarations
16070                .iter()
16071                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
16072            "the export macro must not become a declaration: {declarations:#?}"
16073        );
16074        assert!(
16075            declarations.iter().all(|unit| !matches!(
16076                unit.identifier(),
16077                "final" | "public" | "protected" | "private"
16078            )),
16079            "the head specifiers must not become declarations: {declarations:#?}"
16080        );
16081        assert!(
16082            parsed
16083                .navigation_ranges
16084                .get(base)
16085                .is_some_and(|ranges| !ranges.is_empty()),
16086            "the recovered base must retain a navigable declaration range"
16087        );
16088    }
16089
16090    #[test]
16091    fn function_like_export_class_survives_a_preceding_malformed_body() {
16092        let source = r#"
16093namespace api {
16094class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
16095   public:
16096      /** Return a descriptive string. */
16097      const char* what() const noexcept override { return m_msg.c_str(); }
16098
16099      /** Return the type of error. */
16100      virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
16101
16102      /** Return an associated error code. */
16103      virtual int error_code() const noexcept { return 0; }
16104
16105      /** Avoid throwing the base directly. */
16106      explicit Exception(std::string_view msg);
16107
16108      /** Avoid throwing the base directly. */
16109      Exception(const char* prefix, std::string_view msg);
16110
16111      /** Avoid throwing the base directly. */
16112      Exception(std::string_view msg, const std::exception& e);
16113
16114   private:
16115      std::string m_msg;
16116};
16117
16118class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
16119   public:
16120      explicit Invalid_Argument(std::string_view msg);
16121
16122      explicit Invalid_Argument(std::string_view msg, std::string_view where);
16123
16124      Invalid_Argument(std::string_view msg, const std::exception& e);
16125
16126      ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
16127};
16128} // namespace api
16129"#;
16130        let mut parser = Parser::new();
16131        parser
16132            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16133            .expect("set C++ grammar");
16134        let tree = parser.parse(source, None).expect("parse fixture");
16135        let mut stack = vec![tree.root_node()];
16136        let mut saw_embedded_shape = false;
16137        while let Some(node) = stack.pop() {
16138            saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
16139                .iter()
16140                .any(|recovered| recovered.name == "Invalid_Argument");
16141            let mut cursor = node.walk();
16142            stack.extend(node.named_children(&mut cursor));
16143        }
16144        assert!(
16145            saw_embedded_shape,
16146            "fixture must retain the embedded error geometry: {}",
16147            tree.root_node().to_sexp()
16148        );
16149
16150        let parsed = parse_cpp_file(
16151            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
16152            source,
16153            &tree,
16154        );
16155        let declarations = parsed.declarations();
16156        let exception = declarations
16157            .iter()
16158            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
16159            .expect("qualified-base export class");
16160        let invalid = declarations
16161            .iter()
16162            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
16163            .expect("class embedded in the preceding malformed body");
16164
16165        assert_eq!(
16166            parsed.raw_supertypes.get(exception),
16167            Some(&vec!["std::exception".to_string()])
16168        );
16169        assert_eq!(
16170            parsed.raw_supertypes.get(invalid),
16171            Some(&vec!["Exception".to_string()])
16172        );
16173        assert!(
16174            parsed.materialization_records.iter().any(|record| matches!(
16175                record,
16176                MaterializationRecord::RecoveredDeclaration { unit, .. }
16177                    if unit == invalid
16178            )),
16179            "the embedded class must retain recovery provenance: {:#?}",
16180            parsed.materialization_records
16181        );
16182    }
16183
16184    #[test]
16185    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
16186        let source = r#"
16187public:
16188   explicit Lookup_Error(std::string_view err) : Exception(err) {}
16189
16190   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
16191"#;
16192        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
16193            .expect("reparse merged constructor body");
16194        let (range, body) =
16195            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
16196                .unwrap_or_else(|| {
16197                    panic!(
16198                        "the merged constructor must retain its structured declarator/body: {}",
16199                        tree.root_node().to_sexp()
16200                    )
16201                });
16202        assert_eq!(
16203            source.get(range).expect("constructor range"),
16204            "Lookup_Error(std::string_view err) : Exception(err) {}"
16205        );
16206        assert_eq!(node_text(body, source), "{}");
16207    }
16208
16209    #[test]
16210    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
16211        let positive_source =
16212            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
16213        let mut parser = tree_sitter::Parser::new();
16214        parser
16215            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16216            .unwrap();
16217        let positive_tree = parser.parse(positive_source, None).unwrap();
16218        assert!(cpp_reparsed_members_are_indexable(
16219            positive_tree.root_node(),
16220            positive_source
16221        ));
16222
16223        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
16224        let negative_tree = parser.parse(negative_source, None).unwrap();
16225        assert!(!cpp_reparsed_members_are_indexable(
16226            negative_tree.root_node(),
16227            negative_source
16228        ));
16229    }
16230
16231    #[test]
16232    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
16233        let copy_control_source = r#"
16234public:
16235    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
16236    explicit Token(const Token* tok);
16237    ~Token();
16238    Token* astOperand1() { return nullptr; }
16239"#;
16240        let constraint_source = r#"
16241private:
16242    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
16243    static T *tokAtImpl(T *tok, int index) {
16244        return tok;
16245    }
16246
16247    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
16248    static T *linkAtImpl(T *tok, int index) {
16249        return tok;
16250    }
16251
16252public:
16253    int late() const { return 1; }
16254"#;
16255        let mut parser = tree_sitter::Parser::new();
16256        parser
16257            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16258            .unwrap();
16259        let copy_control_tree = parser
16260            .parse(copy_control_source, None)
16261            .expect("parse copy-control fixture");
16262        assert!(
16263            copy_control_tree.root_node().has_error(),
16264            "fixture must exercise adjacent copy-control recovery"
16265        );
16266        assert!(
16267            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
16268            "a complete late getter must remain recoverable after adjacent copy-control declarations"
16269        );
16270        let mut cursor = copy_control_tree.root_node().walk();
16271        assert!(
16272            copy_control_tree
16273                .root_node()
16274                .named_children(&mut cursor)
16275                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
16276            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
16277            copy_control_tree.root_node().to_sexp()
16278        );
16279        let constraint_tree = parser
16280            .parse(constraint_source, None)
16281            .expect("parse constraint-macro fixture");
16282        assert!(constraint_tree.root_node().has_error());
16283        assert!(
16284            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
16285            "complete constraint-macro members must not hide a later ordinary member"
16286        );
16287        let mut cursor = constraint_tree.root_node().walk();
16288        assert!(
16289            constraint_tree
16290                .root_node()
16291                .named_children(&mut cursor)
16292                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
16293                    child,
16294                    constraint_source
16295                )),
16296            "fixture must retain the split constraint-macro prefix/function geometry"
16297        );
16298    }
16299
16300    #[test]
16301    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
16302        let source = r#"
16303struct Analyzer {
16304    struct Action {
16305        Action() = default;
16306        Action(const Action&) = default;
16307        Action& operator=(const Action& rhs) & = default;
16308
16309        template<class T,
16310                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
16311                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
16312        // NOLINTNEXTLINE(google-explicit-constructor)
16313        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
16314        {}
16315
16316        enum : std::uint16_t { None = 0, Read = (1 << 0) };
16317        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
16318
16319    private:
16320        unsigned int mFlag{};
16321    };
16322
16323    enum class Direction : unsigned char { Forward, Reverse };
16324    virtual Action analyze(Direction d) const = 0;
16325};
16326"#;
16327        let mut parser = tree_sitter::Parser::new();
16328        parser
16329            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16330            .unwrap();
16331        let tree = parser.parse(source, None).unwrap();
16332        assert!(tree.root_node().has_error());
16333        let root = tree.root_node();
16334        let outer = root
16335            .named_children(&mut root.walk())
16336            .find(|child| child.kind() == "ERROR")
16337            .expect("fragmented Analyzer prefix");
16338        let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
16339            .expect("structured Analyzer fragment boundary");
16340        assert_eq!(outer_name, "Analyzer");
16341        let outer_tree = cpp_reparse_fragmented_class_body(
16342            source,
16343            outer_fragment.reparse_start,
16344            outer_fragment.reparse_end,
16345        )
16346        .expect("reparse Analyzer body");
16347        let outer_root = outer_tree.root_node();
16348        let action_prefix = outer_root
16349            .named_children(&mut outer_root.walk())
16350            .find(|child| child.kind() == "ERROR")
16351            .expect("fragmented Action prefix");
16352        let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
16353            .expect("structured Action fragment boundary");
16354        assert_eq!(action_name, "Action");
16355        let action_tree = cpp_reparse_fragmented_class_body(
16356            source,
16357            action_fragment.reparse_start,
16358            action_fragment.reparse_end,
16359        )
16360        .expect("reparse Action body");
16361        let action_root = action_tree.root_node();
16362        let macro_prefix = action_root
16363            .named_children(&mut action_root.walk())
16364            .find(|child| child.kind() == "ERROR")
16365            .expect("constraint macro prefix");
16366        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
16367            .expect("structured template macro prefix");
16368        let macro_companion =
16369            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
16370        assert!(
16371            cpp_reparsed_template_macro_constructor_companion_is_indexable(
16372                macro_companion,
16373                macro_parameter,
16374                source,
16375            ),
16376            "split constrained constructor must be admitted: {}",
16377            macro_companion.to_sexp()
16378        );
16379        assert!(
16380            cpp_reparsed_members_are_indexable(action_root, source),
16381            "complete Action body must pass the recovery gate: {}",
16382            action_tree.root_node().to_sexp()
16383        );
16384        assert!(
16385            cpp_reparsed_members_are_indexable(outer_root, source),
16386            "complete Analyzer body must pass the recovery gate: {}",
16387            outer_tree.root_node().to_sexp()
16388        );
16389        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
16390        let parsed = parse_cpp_file(&file, source, &tree);
16391        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
16392            assert!(
16393                parsed
16394                    .declarations()
16395                    .iter()
16396                    .any(|unit| unit.fq_name() == expected),
16397                "missing recovered declaration {expected}: {:#?}",
16398                parsed.declarations()
16399            );
16400        }
16401        assert!(
16402            parsed
16403                .declarations()
16404                .iter()
16405                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
16406            "nested members must not remain flattened: {:#?}",
16407            parsed.declarations()
16408        );
16409    }
16410
16411    #[test]
16412    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
16413        let source = r#"
16414raw_hash_set& operator=(raw_hash_set&& that) {
16415  return move_assign(
16416      std::move(that),
16417      typename AllocTraits::propagate_on_container_move_assignment());
16418}
16419
16420iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
16421  return {};
16422}
16423
16424void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
16425
16426iterator insert(const_iterator hint, value_type&& value)
16427    ABSL_ATTRIBUTE_LIFETIME_BOUND {
16428  return {};
16429}
16430
16431friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
16432  return left.size() == right.size();
16433}
16434
16435static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
16436  return static_cast<slot_type*>(buffer);
16437}
16438
16439protected:
16440// Included-range recovery can attach this comment to the template prefix.
16441template <class K>
16442void AssertOnFind([[maybe_unused]] const K& key) {
16443  Check(key);
16444}
16445"#;
16446        let mut parser = tree_sitter::Parser::new();
16447        parser
16448            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16449            .unwrap();
16450        let tree = parser.parse(source, None).unwrap();
16451        assert!(
16452            tree.root_node().has_error(),
16453            "the fixture must exercise tree-sitter's errorful member shapes"
16454        );
16455        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
16456
16457        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
16458        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
16459        assert!(!cpp_reparsed_members_are_indexable(
16460            incomplete_tree.root_node(),
16461            incomplete_source
16462        ));
16463
16464        let outside_error_source = "int foo() stray_attribute {}\n";
16465        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
16466        assert!(outside_error_tree.root_node().has_error());
16467        assert!(!cpp_reparsed_members_are_indexable(
16468            outside_error_tree.root_node(),
16469            outside_error_source
16470        ));
16471
16472        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
16473        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
16474        assert!(!cpp_reparsed_members_are_indexable(
16475            variable_initializer_tree.root_node(),
16476            variable_initializer_source
16477        ));
16478    }
16479
16480    #[test]
16481    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
16482        let positive_source = r#"
16483std::pair<iterator, bool> insert(init_type&& value)
16484    ABSL_ATTRIBUTE_LIFETIME_BOUND
16485#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
16486  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
16487#endif
16488{
16489  return emplace(std::move(value));
16490}
16491"#;
16492        let mut parser = tree_sitter::Parser::new();
16493        parser
16494            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16495            .unwrap();
16496        let positive_tree = parser.parse(positive_source, None).unwrap();
16497        assert!(
16498            positive_tree.root_node().has_error(),
16499            "the fixture must exercise the split attribute/requires shape"
16500        );
16501        assert!(cpp_reparsed_members_are_indexable(
16502            positive_tree.root_node(),
16503            positive_source
16504        ));
16505
16506        let template_return_source = r#"
16507pair<int> insert(init_type&& value)
16508    ABSL_ATTRIBUTE_LIFETIME_BOUND
16509#if LANGUAGE_LEVEL >= 202002L
16510  requires(!Predicate<init_type>::value)
16511#endif
16512// Attributes and the function body may be separated by comments.
16513{
16514  return {};
16515}
16516"#;
16517        let template_return_tree = parser.parse(template_return_source, None).unwrap();
16518        assert!(
16519            cpp_reparsed_members_are_indexable(
16520                template_return_tree.root_node(),
16521                template_return_source
16522            ),
16523            "template-return attribute/requires tree: {}",
16524            template_return_tree.root_node().to_sexp()
16525        );
16526
16527        let no_body_source = r#"
16528std::pair<iterator, bool> insert(init_type&& value)
16529    ABSL_ATTRIBUTE_LIFETIME_BOUND
16530#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
16531  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
16532#endif
16533+ 0;
16534"#;
16535        let no_body_tree = parser.parse(no_body_source, None).unwrap();
16536        assert!(!cpp_reparsed_members_are_indexable(
16537            no_body_tree.root_node(),
16538            no_body_source
16539        ));
16540
16541        let extra_payload_source = r#"
16542pair<int> insert(init_type&& value)
16543    ABSL_ATTRIBUTE_LIFETIME_BOUND
16544#if LANGUAGE_LEVEL >= 202002L
16545  int unrelated;
16546  requires(Predicate<init_type>::value)
16547#endif
16548{
16549  return {};
16550}
16551"#;
16552        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
16553        assert!(!cpp_reparsed_members_are_indexable(
16554            extra_payload_tree.root_node(),
16555            extra_payload_source
16556        ));
16557
16558        let variable_initializer_source = r#"
16559int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
16560#if LANGUAGE_LEVEL >= 202002L
16561  requires(true)
16562#endif
16563{
16564  bad;
16565}
16566"#;
16567        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
16568        assert!(!cpp_reparsed_members_are_indexable(
16569            variable_initializer_tree.root_node(),
16570            variable_initializer_source
16571        ));
16572    }
16573
16574    #[test]
16575    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
16576        let source = r#"namespace absl {
16577ABSL_NAMESPACE_BEGIN namespace container_internal {
16578
16579class raw_hash_set : public Base {
16580 public:
16581  using value_type = int;
16582
16583  template <class U,
16584            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
16585  void insert(U value) { (void)value; }
16586
16587  struct InsertSlot {
16588    raw_hash_set& s;
16589  };
16590};
16591
16592}
16593ABSL_NAMESPACE_END
16594}"#;
16595        let mut parser = tree_sitter::Parser::new();
16596        parser
16597            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16598            .unwrap();
16599        let tree = parser.parse(source, None).unwrap();
16600        let root = tree.root_node();
16601        let outer_namespace = root
16602            .named_children(&mut root.walk())
16603            .find(|child| child.kind() == "namespace_definition")
16604            .expect("outer absl namespace");
16605        let declaration_list = outer_namespace
16606            .child_by_field_name("body")
16607            .expect("outer namespace body");
16608        let sentinel_function = declaration_list
16609            .named_children(&mut declaration_list.walk())
16610            .find(|child| child.kind() == "function_definition")
16611            .expect("malformed namespace sentinel function");
16612        let ancestry = ParentIndex::new(root);
16613        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
16614            .expect("structured nested namespace sentinel");
16615        let fragmented =
16616            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
16617                .expect("fragmented raw_hash_set class");
16618        assert_eq!(fragmented.class_node.kind(), "ERROR");
16619        assert_eq!(fragmented.name, "raw_hash_set");
16620        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
16621
16622        let outer_scope =
16623            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
16624        let mut outer_siblings = Vec::new();
16625        push_cpp_sentinel_sibling_classes(
16626            &mut outer_siblings,
16627            declaration_list,
16628            sentinel.function,
16629            &outer_scope,
16630            source,
16631            &ancestry,
16632        );
16633        let [outer_shadow] = outer_siblings.as_slice() else {
16634            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
16635        };
16636        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
16637        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
16638
16639        let field = "    raw_hash_set& s;";
16640        let start = source.find(field).expect("InsertSlot field") + 4;
16641        let node = root
16642            .descendant_for_byte_range(start, start + "raw_hash_set".len())
16643            .expect("raw_hash_set type node");
16644        let recovered = cpp_sentinel_recovered_classes(root, source);
16645        let [deep_class] = recovered.as_slice() else {
16646            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
16647        };
16648        assert_eq!(
16649            deep_class.namespace_scope_components,
16650            vec!["absl", "container_internal"]
16651        );
16652        assert_eq!(
16653            deep_class.scope_components,
16654            vec!["absl", "container_internal", "raw_hash_set"]
16655        );
16656        assert!(
16657            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
16658                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
16659        );
16660
16661        assert_eq!(
16662            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
16663            Some(vec![
16664                "absl".to_string(),
16665                "container_internal".to_string(),
16666                "raw_hash_set".to_string(),
16667                "InsertSlot".to_string(),
16668            ])
16669        );
16670
16671        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
16672        let parsed = parse_cpp_file(&file, source, &tree);
16673        let raw_hash_set = parsed
16674            .declarations()
16675            .iter()
16676            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
16677            .expect("recovered raw_hash_set class");
16678        assert_eq!(
16679            raw_hash_set.fq_name(),
16680            "absl::container_internal.raw_hash_set",
16681            "the recovered declaration must publish under the deeper sentinel namespace"
16682        );
16683        assert_eq!(
16684            parsed.raw_supertypes.get(raw_hash_set),
16685            Some(&vec!["Base".to_string()]),
16686            "the structured base clause on the fragmented ERROR prefix must survive publication"
16687        );
16688        assert!(
16689            parsed.materialization_records.iter().any(|record| matches!(
16690                record,
16691                MaterializationRecord::RecoveredDeclaration { recovery, unit }
16692                    if unit == raw_hash_set && *recovery == deep_class.class_range
16693            )),
16694            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
16695            parsed.materialization_records
16696        );
16697    }
16698
16699    /// Issue #2358: recording an aggregate definition must not walk the whole
16700    /// file.
16701    ///
16702    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
16703    /// class-like shape that has a body, so the removal step runs once per
16704    /// aggregate. It used to `retain` over `top_level_declarations` and over
16705    /// *every* child list in the file on each of those calls, comparing whole
16706    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
16707    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
16708    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
16709    /// tens of thousands of times over and the C forward differential never
16710    /// finished.
16711    ///
16712    /// A definition the file has not already declared removes nothing, so the
16713    /// honest cost is zero regardless of how many other aggregates surround it.
16714    /// Two sizes an order of magnitude apart pin that the count is not merely
16715    /// small but independent of the file.
16716    ///
16717    /// The declaration walk answers every ancestor question from a
16718    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
16719    /// the root for each one (#2361). Substituting the index is only safe
16720    /// because it answers the identical question, so pin that on the shapes
16721    /// this file's recovery paths care about: anonymous and named aggregates,
16722    /// nested namespaces, templates, macro-displaced declarations and the
16723    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
16724    /// too -- `Node::parent` walks the visible tree, not the named one.
16725    #[test]
16726    fn the_parent_index_answers_what_tree_sitter_answers() {
16727        const SHAPES: [&str; 5] = [
16728            "namespace outer { namespace inner { struct Tag { int field; }; } }",
16729            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
16730            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
16731            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
16732            "class API Broken : public First, public Second {\n  void member();\n",
16733        ];
16734        for source in SHAPES {
16735            let mut parser = tree_sitter::Parser::new();
16736            parser
16737                .set_language(&tree_sitter_cpp::LANGUAGE.into())
16738                .unwrap();
16739            let tree = parser.parse(source, None).unwrap();
16740            let root = tree.root_node();
16741            let ancestry = ParentIndex::new(root);
16742            let mut nodes = 0usize;
16743            let mut stack = vec![root];
16744            while let Some(node) = stack.pop() {
16745                nodes += 1;
16746                assert_eq!(
16747                    node.parent().map(|parent| parent.id()),
16748                    ancestry.parent(node).map(|parent| parent.id()),
16749                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
16750                );
16751                let mut cursor = node.walk();
16752                stack.extend(node.children(&mut cursor));
16753            }
16754            assert!(nodes > 1, "{source:?} produced no tree to compare");
16755        }
16756    }
16757
16758    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
16759    /// index for every ancestor edge. Asking tree-sitter directly makes each
16760    /// edge re-descend from the root, turning declaration extraction on a
16761    /// deeply nested generated header from quadratic output work into a cubic
16762    /// tree walk. Exact query counts pin the route without a machine-dependent
16763    /// wall-clock ceiling.
16764    #[test]
16765    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
16766        const DEPTH: usize = 64;
16767        let mut source = String::new();
16768        for level in 0..DEPTH {
16769            writeln!(source, "namespace n{level} {{").unwrap();
16770        }
16771        source.push_str("int deepest(int value);\n");
16772        for _ in 0..DEPTH {
16773            source.push_str("}\n");
16774        }
16775
16776        let mut parser = tree_sitter::Parser::new();
16777        parser
16778            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16779            .unwrap();
16780        let tree = parser.parse(&source, None).unwrap();
16781        let root = tree.root_node();
16782        let ancestry = ParentIndex::new(root);
16783        let mut function_declarator = None;
16784        walk_named_tree_preorder(root, true, |node| {
16785            if node.kind() == "function_declarator" {
16786                function_declarator = Some(node);
16787                WalkControl::Break
16788            } else {
16789                WalkControl::Continue
16790            }
16791        });
16792        let function_declarator = function_declarator.expect("deepest function declarator");
16793        let ancestor_count =
16794            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
16795
16796        ancestry.reset_parent_query_count_for_test();
16797        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
16798        assert_eq!(DEPTH, lexical_scope.len());
16799        assert_eq!(
16800            ancestor_count + 1,
16801            ancestry.parent_query_count_for_test(),
16802            "lexical-scope ancestry bypassed the parent index"
16803        );
16804
16805        ancestry.reset_parent_query_count_for_test();
16806        assert_eq!(
16807            DispatchExtensibility::Closed,
16808            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
16809        );
16810        assert_eq!(
16811            ancestor_count,
16812            ancestry.parent_query_count_for_test(),
16813            "dispatch ancestry bypassed the parent index"
16814        );
16815
16816        ancestry.reset_parent_query_count_for_test();
16817        assert_eq!(
16818            CallableLinkage::External,
16819            cpp_callable_linkage(function_declarator, &source, &ancestry)
16820        );
16821        assert_eq!(
16822            ancestor_count + 1,
16823            ancestry.parent_query_count_for_test(),
16824            "linkage ancestry bypassed the parent index"
16825        );
16826
16827        ancestry.reset_parent_query_count_for_test();
16828        assert!(!cpp_callable_is_structural_constructor(
16829            function_declarator,
16830            &source,
16831            &ancestry
16832        ));
16833        assert_eq!(
16834            ancestor_count + 1,
16835            ancestry.parent_query_count_for_test(),
16836            "constructor ancestry bypassed the parent index"
16837        );
16838    }
16839
16840    /// Forward declarations followed by definitions are compacted as one
16841    /// batch, without rescanning the shared namespace/top-level lists for each
16842    /// tag. Definitions are intentionally visited in reverse order so the
16843    /// assertion also pins eager remove-and-reappend ordering.
16844    #[test]
16845    fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
16846        for aggregates in [64usize, 512] {
16847            let mut source =
16848                String::from("typedef unsigned long long u64;\nnamespace generated {\n");
16849            for index in 0..aggregates {
16850                writeln!(source, "struct tag{index};").unwrap();
16851            }
16852            for index in (0..aggregates).rev() {
16853                writeln!(
16854                    source,
16855                    "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
16856                )
16857                .unwrap();
16858            }
16859            source.push_str("}\n");
16860
16861            start_code_unit_removal_scan_probe();
16862            let parsed = parse_cpp_declarations(&source, "vmlinux.h");
16863            let scanned = finish_code_unit_removal_scan_probe();
16864
16865            let expected_names: Vec<String> = (0..aggregates)
16866                .rev()
16867                .map(|index| format!("tag{index}"))
16868                .collect();
16869            let top_level_names: Vec<String> = parsed
16870                .top_level_declarations
16871                .iter()
16872                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
16873                .map(|unit| unit.short_name().to_string())
16874                .collect();
16875            let namespace = parsed
16876                .declarations()
16877                .iter()
16878                .find(|unit| {
16879                    unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
16880                })
16881                .expect("generated namespace should be declared");
16882            let child_names: Vec<String> = parsed.children[namespace]
16883                .iter()
16884                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
16885                .map(|unit| unit.short_name().to_string())
16886                .collect();
16887            assert_eq!(
16888                aggregates,
16889                parsed
16890                    .declarations()
16891                    .iter()
16892                    .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
16893                    .count(),
16894                "every aggregate must still be declared at {aggregates} aggregates"
16895            );
16896            assert_eq!(expected_names, top_level_names);
16897            assert_eq!(expected_names, child_names);
16898            assert_eq!(
16899                0, scanned,
16900                "replacing {aggregates} forward declarations must compact their shared lists once"
16901            );
16902        }
16903    }
16904
16905    #[test]
16906    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
16907        const DISTINCT_PER_KIND: usize = 64;
16908        let mut source = String::new();
16909        for index in 0..DISTINCT_PER_KIND {
16910            writeln!(source, "typedef int Alias{index};").unwrap();
16911        }
16912        writeln!(source, "typedef long Alias0;").unwrap();
16913        for index in 0..DISTINCT_PER_KIND {
16914            writeln!(source, "#define MACRO_{index} {index}").unwrap();
16915        }
16916        writeln!(source, "#define MACRO_0 duplicate").unwrap();
16917        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
16918
16919        let mut parser = tree_sitter::Parser::new();
16920        parser
16921            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16922            .unwrap();
16923        let tree = parser.parse(&source, None).unwrap();
16924        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
16925
16926        start_declaration_identity_comparison_probe();
16927        let parsed = parse_cpp_file(&file, &source, &tree);
16928        let comparisons = finish_declaration_identity_comparison_probe();
16929
16930        assert_eq!(
16931            DISTINCT_PER_KIND + 1,
16932            parsed
16933                .declarations()
16934                .iter()
16935                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
16936                .count(),
16937            "every physical typedef alias declaration must be retained so \
16938             conditional branch guards stay available to the resolver"
16939        );
16940        assert_eq!(
16941            DISTINCT_PER_KIND + 1,
16942            parsed
16943                .declarations()
16944                .iter()
16945                .filter(|unit| {
16946                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
16947                })
16948                .count(),
16949            "distinct macro redefinitions must remain available to temporal lookup"
16950        );
16951        assert_eq!(
16952            2,
16953            parsed
16954                .declarations()
16955                .iter()
16956                .filter(|unit| {
16957                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
16958                })
16959                .count(),
16960            "function overloads must remain distinct"
16961        );
16962
16963        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
16964        assert!(
16965            comparisons <= dedup_inputs * 4,
16966            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
16967        );
16968    }
16969
16970    #[test]
16971    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
16972        let source = r#"namespace absl {
16973ABSL_NAMESPACE_BEGIN namespace container_internal {
16974template <typename T>
16975class broken {
16976 public:
16977  using value_type = T;
16978  T operator->() const { return &operator*(); }
16979  using alias = value_type;
16980};
16981}
16982}
16983"#;
16984        let mut parser = tree_sitter::Parser::new();
16985        parser
16986            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16987            .unwrap();
16988        let tree = parser.parse(source, None).unwrap();
16989        let broken = find_class_named(tree.root_node(), source, "broken")
16990            .expect("the positive fixture must expose the broken class node");
16991        assert!(
16992            broken.has_error(),
16993            "the positive fixture must retain an internal parser error"
16994        );
16995        assert!(
16996            cpp_complete_class_body_close(broken).is_some(),
16997            "the positive fixture must expose a real class body close"
16998        );
16999        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
17000        assert!(
17001            recovered.iter().any(|class| {
17002                class.scope_components == ["absl", "container_internal", "broken"]
17003            }),
17004            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
17005        );
17006    }
17007
17008    #[test]
17009    fn sentinel_recovery_keeps_members_after_nested_body_close() {
17010        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
17011NLOHMANN_BASIC_JSON_TPL_DECLARATION
17012class basic_json {
17013 private:
17014  union storage {
17015    int value;
17016  } data;
17017 public:
17018  using late_alias = int;
17019  late_alias value() const;
17020};
17021NLOHMANN_JSON_NAMESPACE_END
17022"#;
17023        let mut parser = tree_sitter::Parser::new();
17024        parser
17025            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17026            .unwrap();
17027        let tree = parser.parse(source, None).unwrap();
17028        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
17029        let basic_json = recovered
17030            .iter()
17031            .find(|class| {
17032                class
17033                    .scope_components
17034                    .last()
17035                    .is_some_and(|name| name == "basic_json")
17036            })
17037            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
17038        let late_alias = source
17039            .find("late_alias value")
17040            .expect("late alias reference");
17041        assert!(
17042            basic_json.class_range.start_byte < late_alias
17043                && late_alias < basic_json.class_range.end_byte,
17044            "the recovered class range must include members after a nested close: {basic_json:#?}"
17045        );
17046    }
17047
17048    #[test]
17049    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
17050        let source = r#"namespace absl {
17051ABSL_NAMESPACE_BEGIN namespace container_internal {
17052template <typename T>
17053class broken {
17054 public:
17055  using value_type = T;
17056  T operator->() const { return &operator*(); }
17057}
17058}
17059"#;
17060        let mut parser = tree_sitter::Parser::new();
17061        parser
17062            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17063            .unwrap();
17064        let tree = parser.parse(source, None).unwrap();
17065        let broken = find_class_named(tree.root_node(), source, "broken")
17066            .expect("the negative fixture must expose the malformed class node");
17067        assert!(
17068            broken.has_error(),
17069            "the negative fixture must retain a parser error"
17070        );
17071        assert!(
17072            cpp_complete_class_body_close(broken).is_none(),
17073            "the malformed class must not expose a real body close"
17074        );
17075        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
17076        assert!(
17077            recovered
17078                .iter()
17079                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
17080            "an incomplete class must not borrow the namespace close: {recovered:#?}"
17081        );
17082    }
17083
17084    #[test]
17085    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
17086        let source = r#"namespace absl {
17087ABSL_NAMESPACE_BEGIN namespace container_internal {
17088template <typename T>
17089struct broken {
17090  using value_type = T;
17091};
17092}
17093
17094#ifdef OWNER_DEF
17095template <typename T>
17096typename broken<T>::value_type broken<T>::method() {
17097  value_type value{};
17098  return value;
17099}
17100#endif
17101
17102namespace sibling {
17103template <typename T>
17104typename broken<T>::value_type broken<T>::other() {
17105  value_type value{};
17106  return value;
17107}
17108}
17109
17110ABSL_NAMESPACE_END
17111}
17112"#;
17113        let mut parser = tree_sitter::Parser::new();
17114        parser
17115            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17116            .unwrap();
17117        let tree = parser.parse(source, None).unwrap();
17118        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
17119        let broken = recovered
17120            .iter()
17121            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
17122            .expect("the sentinel class must be recovered");
17123        let method_start = source
17124            .find("typename broken<T>::value_type broken<T>::method()")
17125            .expect("guarded sibling owner");
17126        let method_end = source[method_start..]
17127            .find("\n}")
17128            .map(|offset| method_start + offset + 2)
17129            .expect("guarded sibling owner close");
17130        assert!(
17131            broken
17132                .owner_ranges
17133                .iter()
17134                .any(|owner| owner.range.start_byte <= method_start
17135                    && method_end <= owner.range.end_byte),
17136            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
17137        );
17138        let sibling_start = source
17139            .find("typename broken<T>::value_type broken<T>::other()")
17140            .expect("nested namespace sibling owner");
17141        assert!(
17142            broken
17143                .owner_ranges
17144                .iter()
17145                .all(|owner| owner.range.start_byte > sibling_start
17146                    || owner.range.end_byte <= sibling_start),
17147            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
17148        );
17149    }
17150
17151    #[test]
17152    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
17153        let source = r#"#ifdef OUTER
17154namespace absl {
17155ABSL_NAMESPACE_BEGIN namespace container_internal {
17156template <typename T>
17157struct broken {
17158  using value_type = T;
17159};
17160}
17161}
17162
17163#ifdef OWNER_DEF
17164template <typename T>
17165typename broken<T>::value_type broken<T>::method() {
17166  value_type value{};
17167  return value;
17168}
17169#endif
17170#endif
17171"#;
17172        let mut parser = tree_sitter::Parser::new();
17173        parser
17174            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17175            .unwrap();
17176        let tree = parser.parse(source, None).unwrap();
17177        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
17178        let broken = recovered
17179            .iter()
17180            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
17181            .expect("the sentinel class must be recovered");
17182        let method_start = source
17183            .find("typename broken<T>::value_type broken<T>::method()")
17184            .expect("outer sibling owner");
17185        assert!(
17186            broken
17187                .owner_ranges
17188                .iter()
17189                .all(|owner| owner.range.start_byte > method_start
17190                    || owner.range.end_byte <= method_start),
17191            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
17192        );
17193    }
17194
17195    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
17196    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
17197        let mut signatures = parsed
17198            .declarations()
17199            .iter()
17200            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
17201            .filter_map(|unit| unit.signature().map(str::to_string))
17202            .collect::<Vec<_>>();
17203        signatures.sort();
17204        signatures.dedup();
17205        signatures
17206    }
17207
17208    #[test]
17209    fn callable_parameter_types_come_from_the_ast_parameter_list() {
17210        let source = r#"
17211template <typename T, ENABLE_BYTES(T)>
17212Vec256<T> DupOdd(Vec256<T> value) { return value; }
17213
17214struct Visitor {
17215  void fail(this auto const& self) {}
17216};
17217"#;
17218        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
17219        let dup_odd = parsed
17220            .declarations()
17221            .iter()
17222            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
17223            .expect("DupOdd declaration");
17224        assert_eq!(
17225            dup_odd.signature(),
17226            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
17227        );
17228        assert_eq!(
17229            parsed
17230                .signature_metadata
17231                .get(dup_odd)
17232                .and_then(|metadata| metadata.first())
17233                .and_then(SignatureMetadata::callable_parameter_types),
17234            Some(["Vec256<T>".to_string()].as_slice())
17235        );
17236
17237        let fail = parsed
17238            .declarations()
17239            .iter()
17240            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
17241            .expect("explicit-object member");
17242        assert_eq!(fail.signature(), Some("(const this auto &)"));
17243        let metadata = parsed
17244            .signature_metadata
17245            .get(fail)
17246            .and_then(|metadata| metadata.first())
17247            .expect("explicit-object signature metadata");
17248        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
17249        assert!(
17250            metadata
17251                .callable_arity()
17252                .is_some_and(|arity| arity.accepts(0))
17253        );
17254    }
17255
17256    #[test]
17257    fn trailing_qualifiers_survive_parameter_list_whitespace() {
17258        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
17259        // declarator's structure, so an out-of-line definition that spells its
17260        // parameter list with different whitespace than the declaration must
17261        // still carry it.
17262        let source = r#"
17263struct Widget {
17264  bool multiline(int settings, int supprs) const;
17265  bool doublespace(int settings, int supprs) const;
17266  bool noexcept_multiline(int settings, int supprs) noexcept;
17267  bool ref_multiline(int settings, int supprs) &&;
17268};
17269bool
17270Widget::multiline (int settings,
17271                   int supprs) const
17272{ return settings + supprs > 0; }
17273bool Widget::doublespace(int settings,  int supprs) const { return true; }
17274bool Widget::noexcept_multiline(int settings,
17275                                int supprs) noexcept { return true; }
17276bool Widget::ref_multiline(int settings,
17277                           int supprs) && { return true; }
17278"#;
17279        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
17280        assert_eq!(
17281            vec!["(int, int) const".to_string()],
17282            identity_signatures(&parsed, "Widget.multiline")
17283        );
17284        assert_eq!(
17285            vec!["(int, int) const".to_string()],
17286            identity_signatures(&parsed, "Widget.doublespace")
17287        );
17288        assert_eq!(
17289            vec!["(int, int) noexcept".to_string()],
17290            identity_signatures(&parsed, "Widget.noexcept_multiline")
17291        );
17292        assert_eq!(
17293            vec!["(int, int) &&".to_string()],
17294            identity_signatures(&parsed, "Widget.ref_multiline")
17295        );
17296    }
17297
17298    #[test]
17299    fn macro_fragmented_plain_class_keeps_following_member_signature() {
17300        let source = r#"
17301struct CString {};
17302class CMessage {
17303public:
17304  CString GetParams(unsigned int index, unsigned int length = -1) const
17305      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
17306    return GetParamsColon(index, length);
17307  }
17308  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
17309};
17310CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
17311  return {};
17312}
17313"#;
17314        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
17315        assert_eq!(
17316            vec!["(unsigned int, unsigned int) const".to_string()],
17317            identity_signatures(&parsed, "CMessage.GetParamsColon")
17318        );
17319    }
17320
17321    #[test]
17322    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
17323        let source = r#"
17324#pragma once
17325#define DEMO_DEPRECATED(message)
17326namespace demo {
17327struct Base {
17328    static int aligned(int value) { return value; }
17329    int legacy(int value) const
17330        DEMO_DEPRECATED("use replacement()") { return value; }
17331    int replacement() const;
17332    void run(int value);
17333};
17334struct OtherBase {
17335    void run(int value);
17336    static int aligned(int value) { return value; }
17337};
17338struct Derived : Base {};
17339struct Override : Base {
17340    void run(int value);
17341    static int aligned(int value) { return value; }
17342};
17343struct RecoveredOverride : Base {
17344    int legacy(int value) const
17345        DEMO_DEPRECATED("use replacement()") { return value; }
17346    void run(int value);
17347};
17348struct Hidden : Base {
17349    void run(int first, int second);
17350    static int aligned(int first, int second) { return first + second; }
17351};
17352struct Ambiguous : Base, OtherBase {};
17353}
17354struct Global {};
17355"#;
17356        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
17357        let declarations = parsed.declarations();
17358        let fq_names = declarations
17359            .iter()
17360            .map(|unit| unit.fq_name())
17361            .collect::<std::collections::BTreeSet<_>>();
17362
17363        for expected in [
17364            "demo.Base",
17365            "demo.Base.aligned",
17366            "demo.Base.legacy",
17367            "demo.Base.replacement",
17368            "demo.Base.run",
17369            "demo.Derived",
17370            "demo.OtherBase",
17371            "demo.Override",
17372            "demo.RecoveredOverride",
17373            "demo.Hidden",
17374            "demo.Ambiguous",
17375            "Global",
17376        ] {
17377            assert!(
17378                fq_names.contains(expected),
17379                "missing {expected} from namespaced macro fragment: {declarations:#?}"
17380            );
17381        }
17382        assert!(
17383            !fq_names.contains("Derived"),
17384            "following class escaped its namespace: {declarations:#?}"
17385        );
17386        assert!(
17387            !fq_names.contains("demo.Global"),
17388            "global class crossed the recovered namespace boundary: {declarations:#?}"
17389        );
17390    }
17391
17392    #[test]
17393    fn trailing_qualifiers_still_separate_genuine_overloads() {
17394        // The qualifier must keep distinguishing the real C++ overload sets it
17395        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
17396        let source = r#"
17397struct Widget {
17398  int* slot(int index);
17399  const int* slot(int index) const;
17400  int log(int severity) &;
17401  int log(int severity) &&;
17402};
17403"#;
17404        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
17405        assert_eq!(
17406            vec!["(int)".to_string(), "(int) const".to_string()],
17407            identity_signatures(&parsed, "Widget.slot")
17408        );
17409        assert_eq!(
17410            vec!["(int) &".to_string(), "(int) &&".to_string()],
17411            identity_signatures(&parsed, "Widget.log")
17412        );
17413    }
17414
17415    #[test]
17416    fn virtual_specifier_is_not_part_of_the_identity_signature() {
17417        // `override` never appears on the out-of-line definition, and C++ does
17418        // not make it part of the signature, so it must not split the identity.
17419        let source = r#"
17420struct Base {
17421  virtual void run(int value) const;
17422};
17423struct Widget : Base {
17424  void run(int value) const override;
17425};
17426void Widget::run(int value) const {}
17427"#;
17428        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
17429        assert_eq!(
17430            vec!["(int) const".to_string()],
17431            identity_signatures(&parsed, "Widget.run")
17432        );
17433    }
17434
17435    #[test]
17436    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
17437        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
17438        // the function type, so a declaration that spells `const int` and a
17439        // definition that spells `int` are one entity.
17440        let source = r#"
17441struct Widget {
17442  bool value_params(const int settings, const int supprs);
17443  void pointee_const(const int* p);
17444  void pointer_const(int* const p);
17445  void both_const(const int* const p);
17446  void reference_const(const int& p);
17447  void array_const(const int values[4]);
17448};
17449bool Widget::value_params(int settings, int supprs) { return true; }
17450void Widget::pointer_const(int* p) {}
17451void Widget::both_const(const int* p) {}
17452"#;
17453        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
17454        assert_eq!(
17455            vec!["(int, int)".to_string()],
17456            identity_signatures(&parsed, "Widget.value_params")
17457        );
17458        assert_eq!(
17459            vec!["(int *)".to_string()],
17460            identity_signatures(&parsed, "Widget.pointer_const")
17461        );
17462        assert_eq!(
17463            vec!["(const int *)".to_string()],
17464            identity_signatures(&parsed, "Widget.both_const")
17465        );
17466        // The const that is not top-level still distinguishes the type.
17467        assert_eq!(
17468            vec!["(const int *)".to_string()],
17469            identity_signatures(&parsed, "Widget.pointee_const")
17470        );
17471        assert_eq!(
17472            vec!["(const int &)".to_string()],
17473            identity_signatures(&parsed, "Widget.reference_const")
17474        );
17475        assert_eq!(
17476            vec!["(const int [4])".to_string()],
17477            identity_signatures(&parsed, "Widget.array_const")
17478        );
17479    }
17480
17481    #[test]
17482    fn top_level_parameter_const_still_separates_pointee_overloads() {
17483        let source = r#"
17484struct Widget {
17485  void take(const int* p);
17486  void take(int* p);
17487};
17488"#;
17489        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
17490        assert_eq!(
17491            vec!["(const int *)".to_string(), "(int *)".to_string()],
17492            identity_signatures(&parsed, "Widget.take")
17493        );
17494    }
17495
17496    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
17497        let mut parser = tree_sitter::Parser::new();
17498        parser
17499            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17500            .unwrap();
17501        let tree = parser.parse(source, None).unwrap();
17502        let start = source.find(callable_name).expect("callable declaration");
17503        let declarator =
17504            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
17505        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
17506    }
17507
17508    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
17509        let mut shapes = comparable_shapes(source, callable_name);
17510        assert_eq!(1, shapes.len(), "{shapes:?}");
17511        match shapes.remove(0) {
17512            CppComparableSlot::Shape(shape) => shape,
17513            other => panic!("expected a comparable shape, got {other:?}"),
17514        }
17515    }
17516
17517    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
17518        let mut current = shape.root();
17519        loop {
17520            match shape.node(current) {
17521                CppComparableNode::Named { .. } => return shape.node(current),
17522                CppComparableNode::Pointer { inner, .. }
17523                | CppComparableNode::Reference { inner }
17524                | CppComparableNode::Array { inner } => current = *inner,
17525                CppComparableNode::Generic { base, .. } => current = *base,
17526            }
17527        }
17528    }
17529
17530    #[test]
17531    fn comparable_shape_keeps_pointee_const() {
17532        assert_ne!(
17533            sole_comparable_shape("void f(const char* p);", "f("),
17534            sole_comparable_shape("void f(char* p);", "f(")
17535        );
17536    }
17537
17538    #[test]
17539    fn comparable_shape_keeps_inner_pointer_const() {
17540        assert_ne!(
17541            sole_comparable_shape("void f(int** p);", "f("),
17542            sole_comparable_shape("void f(int* const* p);", "f(")
17543        );
17544    }
17545
17546    #[test]
17547    fn comparable_shape_drops_top_level_pointer_const() {
17548        assert_eq!(
17549            sole_comparable_shape("void f(int* const p);", "f("),
17550            sole_comparable_shape("void f(int* p);", "f(")
17551        );
17552    }
17553
17554    #[test]
17555    fn comparable_shape_drops_top_level_base_const() {
17556        assert_eq!(
17557            sole_comparable_shape("void f(const int p);", "f("),
17558            sole_comparable_shape("void f(int p);", "f(")
17559        );
17560    }
17561
17562    #[test]
17563    fn comparable_shape_decays_top_level_array_to_pointer() {
17564        assert_eq!(
17565            sole_comparable_shape("void f(int a[3]);", "f("),
17566            sole_comparable_shape("void f(int* a);", "f(")
17567        );
17568        assert_eq!(
17569            sole_comparable_shape("void f(int* a[3]);", "f("),
17570            sole_comparable_shape("void f(int** a);", "f(")
17571        );
17572    }
17573
17574    #[test]
17575    fn comparable_shape_keeps_array_behind_pointer() {
17576        assert_ne!(
17577            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
17578            sole_comparable_shape("struct S { void f(int** a); };", "f(")
17579        );
17580    }
17581
17582    #[test]
17583    fn comparable_shape_records_written_name_and_lexical_scope() {
17584        let declared =
17585            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
17586        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
17587        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
17588            panic!("named leaf");
17589        };
17590        assert_eq!(["Msg".to_string()].as_slice(), name.path());
17591        assert_eq!(
17592            ["ns".to_string(), "S".to_string()].as_slice(),
17593            name.lexical_scope()
17594        );
17595        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
17596            panic!("named leaf");
17597        };
17598        assert_eq!(
17599            ["ns".to_string(), "Msg".to_string()].as_slice(),
17600            name.path()
17601        );
17602        assert!(name.lexical_scope().is_empty());
17603        assert_ne!(declared, defined);
17604    }
17605
17606    #[test]
17607    fn comparable_shape_marks_sized_primitive_leaf() {
17608        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
17609        let CppComparableNode::Named {
17610            name, primitive, ..
17611        } = comparable_named_leaf(&shape)
17612        else {
17613            panic!("named leaf");
17614        };
17615        assert!(primitive);
17616        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
17617        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
17618    }
17619
17620    #[test]
17621    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
17622        assert_eq!(
17623            vec![CppComparableSlot::Unstructured],
17624            comparable_shapes("void f(void (*cb)(int));", "f(")
17625        );
17626    }
17627
17628    #[test]
17629    fn comparable_shape_reports_ellipsis_slot() {
17630        let shapes = comparable_shapes("void f(int a, ...);", "f(");
17631        assert_eq!(2, shapes.len(), "{shapes:?}");
17632        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
17633    }
17634
17635    #[test]
17636    fn comparable_shape_keeps_template_argument_const() {
17637        assert_ne!(
17638            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
17639            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
17640        );
17641    }
17642
17643    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
17644    /// file-scope tag that a later `struct inner *` at file scope may name.
17645    #[test]
17646    fn c_file_mints_aggregate_member_tag_at_file_scope() {
17647        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
17648        let parsed = parse_cpp_declarations(source, "x.c");
17649        let declarations = parsed.declarations();
17650
17651        assert!(
17652            declarations
17653                .iter()
17654                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
17655            "expected a file-scope inner tag, got {declarations:?}"
17656        );
17657        assert!(
17658            declarations
17659                .iter()
17660                .all(|unit| unit.fq_name() != "outer$inner"),
17661            "expected no nested identity, got {declarations:?}"
17662        );
17663        assert!(
17664            declarations
17665                .iter()
17666                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
17667        );
17668        // Members still belong to their own aggregate.
17669        assert!(
17670            declarations
17671                .iter()
17672                .any(|unit| unit.fq_name() == "inner.value")
17673        );
17674        assert!(
17675            declarations
17676                .iter()
17677                .any(|unit| unit.fq_name() == "outer.item")
17678        );
17679
17680        let outer = declarations
17681            .iter()
17682            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
17683            .expect("outer");
17684        assert!(
17685            parsed
17686                .children
17687                .get(outer)
17688                .into_iter()
17689                .flatten()
17690                .all(|child| child.fq_name() != "inner"),
17691            "the tag must not hang off the aggregate it is written inside: {:?}",
17692            parsed.children
17693        );
17694    }
17695
17696    /// A header carries no compilation language of its own, and a `.cpp`
17697    /// translation unit really does declare a nested class. Both keep exactly
17698    /// the C++ extraction they had before the C dialect existed.
17699    #[test]
17700    fn header_and_cpp_files_keep_nested_tag_identity() {
17701        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
17702        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
17703            let parsed = parse_cpp_declarations(source, name);
17704            let declarations = parsed.declarations();
17705            assert!(
17706                declarations
17707                    .iter()
17708                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
17709                "{name} must keep the nested identity, got {declarations:?}"
17710            );
17711            assert!(
17712                declarations.iter().all(|unit| unit.fq_name() != "inner"),
17713                "{name} must not mint a file-scope tag, got {declarations:?}"
17714            );
17715            assert!(
17716                declarations
17717                    .iter()
17718                    .any(|unit| unit.fq_name() == "outer$inner.value")
17719            );
17720        }
17721    }
17722
17723    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
17724    #[test]
17725    fn uppercase_c_extension_keeps_cpp_tag_scope() {
17726        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
17727        let parsed = parse_cpp_declarations(source, "x.C");
17728        assert!(
17729            parsed
17730                .declarations()
17731                .iter()
17732                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
17733        );
17734    }
17735
17736    /// There is no such thing as a partially nested tag in C: every level of a
17737    /// nested aggregate chain lands at the same enclosing scope.
17738    #[test]
17739    fn c_file_mints_every_nesting_level_at_file_scope() {
17740        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
17741        let parsed = parse_cpp_declarations(source, "z.c");
17742        let declarations = parsed.declarations();
17743
17744        for tag in ["a", "b", "c"] {
17745            assert!(
17746                declarations
17747                    .iter()
17748                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
17749                "expected a file-scope {tag}, got {declarations:?}"
17750            );
17751        }
17752        assert!(
17753            declarations
17754                .iter()
17755                .all(|unit| !unit.fq_name().contains('$')),
17756            "no level may keep a nested identity, got {declarations:?}"
17757        );
17758        // Each member still belongs to the aggregate that declares it.
17759        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
17760        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
17761        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
17762    }
17763
17764    /// An enum tag is a tag; its enumerators stay members of the enum, which is
17765    /// what makes them ordinary identifiers at the enum's own (file) scope.
17766    #[test]
17767    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
17768        let source = "struct outer { enum color { RED, GREEN } c; };\n";
17769        let parsed = parse_cpp_declarations(source, "e.c");
17770        let declarations = parsed.declarations();
17771
17772        let color = declarations
17773            .iter()
17774            .find(|unit| unit.is_class() && unit.fq_name() == "color")
17775            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
17776        assert!(
17777            declarations
17778                .iter()
17779                .all(|unit| unit.fq_name() != "outer$color")
17780        );
17781        for enumerator in ["color.RED", "color.GREEN"] {
17782            assert!(
17783                declarations.iter().any(|unit| unit.fq_name() == enumerator),
17784                "expected {enumerator}, got {declarations:?}"
17785            );
17786        }
17787        let children = parsed
17788            .children
17789            .get(color)
17790            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
17791        assert!(
17792            ["color.RED", "color.GREEN"]
17793                .iter()
17794                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
17795            "enumerators must hang off their enum: {children:?}"
17796        );
17797    }
17798
17799    #[test]
17800    fn c_file_mints_member_list_union_at_file_scope() {
17801        let source = "struct outer { union inner { int a; float b; } item; };\n";
17802        let parsed = parse_cpp_declarations(source, "u.c");
17803        let declarations = parsed.declarations();
17804        assert!(
17805            declarations
17806                .iter()
17807                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
17808            "expected a file-scope inner union, got {declarations:?}"
17809        );
17810        assert!(
17811            declarations
17812                .iter()
17813                .all(|unit| unit.fq_name() != "outer$inner")
17814        );
17815        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
17816        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
17817    }
17818
17819    /// A tag declared in a namespace member list is not a file-scope tag: the
17820    /// nearest enclosing non-aggregate scope is the namespace.
17821    #[test]
17822    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
17823        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
17824        let parsed = parse_cpp_declarations(source, "n.c");
17825        let declarations = parsed.declarations();
17826        let inner = declarations
17827            .iter()
17828            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
17829            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
17830        assert_eq!(inner.package_name(), "ns");
17831        assert!(
17832            declarations
17833                .iter()
17834                .all(|unit| unit.fq_name() != "ns.outer$inner")
17835        );
17836    }
17837
17838    /// Pins today's treatment of a tag declared inside a function body: the
17839    /// declaration walk does not descend into statement bodies, so no unit is
17840    /// minted for it in either dialect. C block scope is out of scope for the
17841    /// dialect change, and this test proves the change did not disturb it.
17842    #[test]
17843    fn function_local_tags_are_unchanged_in_both_dialects() {
17844        let source =
17845            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
17846        for name in ["y.c", "y.cpp"] {
17847            let parsed = parse_cpp_declarations(source, name);
17848            let declarations = parsed.declarations();
17849            assert!(
17850                declarations
17851                    .iter()
17852                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
17853                "{name}: {declarations:?}"
17854            );
17855            for tag in ["localtag", "deeper", "localtag$deeper"] {
17856                assert!(
17857                    declarations.iter().all(|unit| unit.fq_name() != tag),
17858                    "{name} must not mint {tag}, got {declarations:?}"
17859                );
17860            }
17861        }
17862    }
17863
17864    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
17865    /// re-scope: the typedef name is identical in both dialects.
17866    #[test]
17867    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
17868        let source = "typedef struct { int v; } T;\n";
17869        for name in ["t.c", "t.cpp"] {
17870            let parsed = parse_cpp_declarations(source, name);
17871            let declarations = parsed.declarations();
17872            assert!(
17873                declarations
17874                    .iter()
17875                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
17876                "{name}: {declarations:?}"
17877            );
17878        }
17879    }
17880
17881    #[test]
17882    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
17883        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
17884        let parsed = parse_cpp_declarations(source, "socket.c");
17885        let declarations = parsed.declarations();
17886        assert_eq!(
17887            declarations
17888                .iter()
17889                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
17890                .count(),
17891            1,
17892            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
17893        );
17894        for expected in [
17895            "PAL_HANDLE",
17896            "PAL_HANDLE.sock",
17897            "PAL_HANDLE$sock",
17898            "PAL_HANDLE$sock.ops",
17899        ] {
17900            assert!(
17901                declarations.iter().any(|unit| unit.fq_name() == expected),
17902                "expected {expected}, got {declarations:?}"
17903            );
17904        }
17905    }
17906
17907    /// `class` is not C. Source that spells one in a `.c` file is not C code,
17908    /// so it keeps the C++ reading rather than acquiring a half-C identity.
17909    #[test]
17910    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
17911        let source = "class outer { class inner { int v; }; };\n";
17912        let c_parsed = parse_cpp_declarations(source, "k.c");
17913        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
17914        let c_declarations = c_parsed.declarations();
17915        let cpp_declarations = cpp_parsed.declarations();
17916        assert!(
17917            c_declarations
17918                .iter()
17919                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
17920            "{c_declarations:?}"
17921        );
17922        assert_eq!(
17923            c_declarations
17924                .iter()
17925                .map(|unit| unit.fq_name())
17926                .collect::<std::collections::BTreeSet<_>>(),
17927            cpp_declarations
17928                .iter()
17929                .map(|unit| unit.fq_name())
17930                .collect::<std::collections::BTreeSet<_>>()
17931        );
17932    }
17933
17934    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
17935    /// every (node, class-like name) pair a tree offers, and require the same
17936    /// answer from both.
17937    ///
17938    /// The release build has no `debug_assertions` agreement check, so this is
17939    /// what pins the two together there.  Both query orders are exercised:
17940    /// document order is what the walk does, and reverse order proves that a
17941    /// question about an earlier byte than one already answered is still
17942    /// filtered back to its own prefix rather than answered from the wider
17943    /// fold.
17944    ///
17945    /// Returns how many questions were answered with a namespace, so a fixture
17946    /// can assert it actually reached the path (#2754).
17947    fn namespace_forward_scan_agreement(source: &str) -> usize {
17948        let mut parser = tree_sitter::Parser::new();
17949        parser
17950            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17951            .unwrap();
17952        let tree = parser.parse(source, None).unwrap();
17953        let root = tree.root_node();
17954        let ancestry = ParentIndex::new(root);
17955
17956        let mut nodes = Vec::new();
17957        let mut names = std::collections::BTreeSet::new();
17958        let mut cursor = root.walk();
17959        let mut stack = vec![root];
17960        while let Some(node) = stack.pop() {
17961            if matches!(
17962                node.kind(),
17963                "class_specifier" | "struct_specifier" | "union_specifier"
17964            ) && let Some(name) = class_like_name(node, source, &ancestry)
17965            {
17966                names.insert(name);
17967            }
17968            nodes.push(node);
17969            stack.extend(node.named_children(&mut cursor));
17970        }
17971        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
17972        assert!(!names.is_empty(), "fixture declares no class-like name");
17973
17974        let mut answered = 0usize;
17975        for reversed in [false, true] {
17976            let mut scan = CppNamespaceForwardScan::default();
17977            let ordered: Vec<_> = if reversed {
17978                nodes.iter().rev().copied().collect()
17979            } else {
17980                nodes.clone()
17981            };
17982            answered = 0;
17983            for node in ordered {
17984                for name in &names {
17985                    scan.advance_to(root, node.start_byte(), source, &ancestry);
17986                    let carried = scan.unique_earlier_forward(name, node);
17987                    assert_eq!(
17988                        carried,
17989                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
17990                        "carried-forward scan and prefix scan disagree about {name} at \
17991                         {} node starting at byte {} (reversed order: {reversed})",
17992                        node.kind(),
17993                        node.start_byte()
17994                    );
17995                    answered += usize::from(carried.is_some());
17996                }
17997            }
17998        }
17999        answered
18000    }
18001
18002    /// A malformed namespace whose forward declarations are the only identity
18003    /// signal left for the class definitions tree-sitter pushed out to file
18004    /// scope.  Both recovered classes open the guard; only the first one is
18005    /// separated from the namespace by nothing but recovery trivia, so only the
18006    /// first one borrows.  The carried-forward scan has to reproduce both
18007    /// answers.
18008    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
18009namespace ns {
18010class Widget;
18011class Gadget;
18012int x = ;
18013}
18014class API Widget {
18015public:
18016    void first();
18017};
18018class API Gadget {
18019public:
18020    void second();
18021};
18022"#;
18023
18024    #[test]
18025    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
18026        assert!(
18027            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
18028            "the fixture must actually reach the namespace-borrow path"
18029        );
18030
18031        // Nothing here may be answered, and the two paths have to agree about
18032        // that too: a clean namespace is not an identity proof, two forwards of
18033        // one name are ambiguous rather than a guess, and a forward inside a
18034        // function body is not at namespace scope.
18035        for source in [
18036            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
18037            r#"#define API
18038namespace ns {
18039class Widget;
18040class Widget;
18041int x = ;
18042}
18043class API Widget {
18044public:
18045    void method();
18046};
18047"#,
18048            r#"#define API
18049namespace ns {
18050void host() {
18051    class Widget;
18052}
18053int x = ;
18054}
18055class API Widget {
18056public:
18057    void method();
18058};
18059"#,
18060        ] {
18061            assert_eq!(
18062                namespace_forward_scan_agreement(source),
18063                0,
18064                "no borrow is justified here: {source}"
18065            );
18066        }
18067    }
18068
18069    /// The fold is incremental, so a walk that asks about steadily later bytes
18070    /// must never re-fold a node an earlier question already folded, and must
18071    /// never skip one that lies between two questions.
18072    #[test]
18073    fn carried_forward_namespace_scan_folds_each_node_once() {
18074        let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
18075        let mut parser = tree_sitter::Parser::new();
18076        parser
18077            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18078            .unwrap();
18079        let tree = parser.parse(source, None).unwrap();
18080        let root = tree.root_node();
18081        let ancestry = ParentIndex::new(root);
18082
18083        let mut incremental = CppNamespaceForwardScan::default();
18084        for cutoff in 0..=source.len() {
18085            incremental.advance_to(root, cutoff, source, &ancestry);
18086        }
18087        let mut whole = CppNamespaceForwardScan::default();
18088        whole.advance_to(root, source.len(), source, &ancestry);
18089
18090        let mut incremental_shape: Vec<_> = incremental
18091            .forwards
18092            .iter()
18093            .map(|(name, forwards)| {
18094                (
18095                    name.clone(),
18096                    forwards
18097                        .iter()
18098                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
18099                        .collect::<Vec<_>>(),
18100                )
18101            })
18102            .collect();
18103        let mut whole_shape: Vec<_> = whole
18104            .forwards
18105            .iter()
18106            .map(|(name, forwards)| {
18107                (
18108                    name.clone(),
18109                    forwards
18110                        .iter()
18111                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
18112                        .collect::<Vec<_>>(),
18113                )
18114            })
18115            .collect();
18116        incremental_shape.sort();
18117        whole_shape.sort();
18118        for (_, forwards) in &mut incremental_shape {
18119            forwards.sort();
18120        }
18121        for (_, forwards) in &mut whole_shape {
18122            forwards.sort();
18123        }
18124
18125        assert!(!whole_shape.is_empty(), "fixture folds no forward");
18126        assert_eq!(
18127            incremental_shape, whole_shape,
18128            "one byte at a time must fold exactly what one whole pass folds"
18129        );
18130    }
18131
18132    /// Drive the region reparse and the whitespace-padded reparse it replaced
18133    /// over the same region and require identical trees.
18134    ///
18135    /// The release build has no `debug_assertions` agreement check, so this is
18136    /// what pins the two together there. Each body is reparsed at its own
18137    /// offset and again after a long prefix, because the prefix is the whole
18138    /// difference between the two techniques: the padded parse lexes it as
18139    /// whitespace, the included-range parse never sees it, and the tree has to
18140    /// come out the same either way (#2788).
18141    fn fragmented_class_reparse_agreement(body: &str) {
18142        for prefix in [
18143            String::new(),
18144            "// leading comment\n".to_string(),
18145            // A body starts just after its class head's `{`, which is normally
18146            // mid-line: the padded parse then has spaces before the region on
18147            // the region's own line, and the included-range parse has nothing
18148            // at all before it.
18149            "class Widget : public Base { ".to_string(),
18150            "namespace filler {\n".to_string()
18151                + &"struct Filler { int member; };\n".repeat(200)
18152                + "}\n",
18153            "namespace filler {\n".to_string()
18154                + &"struct Filler { int member; };\n".repeat(200)
18155                + "}\nclass Widget : public Base { ",
18156        ] {
18157            let source = format!("{prefix}{body}");
18158            let start = prefix.len();
18159            let end = source.len();
18160            let region = cpp_reparse_fragmented_class_body(&source, start, end)
18161                .expect("the region reparse must produce a tree");
18162            let padded = cpp_reparse_padded_class_body(&source, start, end)
18163                .expect("the padded reparse must produce a tree");
18164            assert_eq!(
18165                cpp_tree_shape(&region),
18166                cpp_tree_shape(&padded),
18167                "region and padded reparse disagree at offset {start} of {end} bytes"
18168            );
18169            assert_eq!(
18170                region.root_node().start_byte(),
18171                start,
18172                "the reparsed region keeps its original offsets"
18173            );
18174        }
18175    }
18176
18177    #[test]
18178    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
18179        // A conditional immediately after an access label: the shape the padded
18180        // technique was kept for, because the directive and its macro name
18181        // become an ERROR plus the following declaration's apparent type.
18182        fragmented_class_reparse_agreement(
18183            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
18184        );
18185        fragmented_class_reparse_agreement(
18186            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
18187        );
18188        // The merged inline constructor and the nested fragmented bodies the
18189        // #938 recovery reads out of a reparse.
18190        fragmented_class_reparse_agreement(
18191            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
18192        );
18193        fragmented_class_reparse_agreement(
18194            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
18195        );
18196        // A body that is not member-shaped at all still has to reparse the same
18197        // way, because the admission gate reads the tree to reject it.
18198        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
18199    }
18200
18201    /// A header shaped like the generated ones this walk is slow on: many
18202    /// enums, classes whose members share the enums' names, nested enums, an
18203    /// ownerless enumerator, and namespaced repeats of all of it.
18204    fn many_enums_and_mixed_declarations() -> String {
18205        let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
18206        for index in 0..40 {
18207            let _ = write!(
18208                source,
18209                "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
18210                 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
18211                 class Color{index}Like {{ public: int member{index}; }};\n"
18212            );
18213        }
18214        source.push_str("namespace outer {\n");
18215        for index in 0..20 {
18216            let _ = write!(
18217                source,
18218                "enum Shade{index} {{ DARK{index} }};\n\
18219                 struct Shade{index}Holder {{ int field{index}; }};\n"
18220            );
18221        }
18222        source.push_str("}\n");
18223        source
18224    }
18225
18226    /// Drive [`CppFieldOwnerIndex`] and the declaration scan it replaced over
18227    /// every question a fixture's declarations can ask, and require the same
18228    /// answer from both.
18229    ///
18230    /// The release build has no `debug_assertions` agreement check, so this is
18231    /// what pins the two together there. The index is fed one declaration at a
18232    /// time and every question is re-asked after each one, which is what proves
18233    /// the incremental record agrees -- a whole-set rebuild would pass a weaker
18234    /// test. Each of the fixture's own fields is also fed in restated as a
18235    /// declaration of another file: the scan ignores those because it asks
18236    /// about the asking unit's own source, and the index has to ignore them for
18237    /// the same reason.
18238    ///
18239    /// Returns how many questions the fixture answered `true`, so a caller can
18240    /// assert that it actually reached the path (#2786).
18241    fn field_owner_index_agreement(source: &str, name: &str) -> usize {
18242        let parsed = parse_cpp_declarations(source, name);
18243        let file = ProjectFile::new(std::env::temp_dir(), name);
18244        let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
18245
18246        let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
18247        declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
18248
18249        let foreign: Vec<CodeUnit> = declarations
18250            .iter()
18251            .filter(|unit| unit.kind() == CodeUnitType::Field)
18252            .map(|unit| {
18253                CodeUnit::new_fq(
18254                    elsewhere.clone(),
18255                    unit.kind(),
18256                    unit.package_name().to_string(),
18257                    unit.short_name().to_string(),
18258                    unit.fq().clone(),
18259                )
18260            })
18261            .collect();
18262
18263        // One owner chain deeper than any C++ short name reaches today
18264        // (`cpp_member_fq`: at most one `.`, separating the owner chain from
18265        // the member). The scan asks `starts_with("owner.")`, so a field like
18266        // this answers for every owner in its chain, and the index has to
18267        // record every one of them rather than only the innermost.
18268        let mut packages: Vec<String> = declarations
18269            .iter()
18270            .map(|unit| unit.package_name().to_string())
18271            .collect();
18272        packages.push(String::new());
18273        packages.sort();
18274        packages.dedup();
18275        let deeper: Vec<CodeUnit> = packages
18276            .iter()
18277            .map(|package_name| {
18278                CodeUnit::new_fq(
18279                    file.clone(),
18280                    CodeUnitType::Field,
18281                    package_name.clone(),
18282                    "SynthOwner.middle.leaf".to_string(),
18283                    cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
18284                )
18285            })
18286            .collect();
18287
18288        // Every (package, owner) pair anything could ask about: each unit's own
18289        // short name, each dotted prefix of it, and the empty owner an
18290        // anonymous enum asks with (#2140).
18291        let mut questions: Vec<(String, String)> = Vec::new();
18292        for unit in declarations.iter().chain(deeper.iter()) {
18293            let package_name = unit.package_name().to_string();
18294            let short_name = unit.short_name();
18295            questions.push((package_name.clone(), short_name.to_string()));
18296            questions.push((package_name.clone(), String::new()));
18297            for (offset, _) in short_name.match_indices('.') {
18298                questions.push((package_name.clone(), short_name[..offset].to_string()));
18299            }
18300        }
18301        questions.sort();
18302        questions.dedup();
18303
18304        let mut index = CppFieldOwnerIndex::default();
18305        let mut recorded: Vec<&CodeUnit> = Vec::new();
18306        let mut answered = 0usize;
18307        for unit in foreign
18308            .iter()
18309            .chain(declarations.iter())
18310            .chain(deeper.iter())
18311        {
18312            index.record(unit, &file);
18313            recorded.push(unit);
18314            for (package_name, owner_short_name) in &questions {
18315                let carried = index.owns_fields(package_name, owner_short_name);
18316                assert_eq!(
18317                    carried,
18318                    cpp_declarations_hold_owned_fields(
18319                        recorded.iter().copied(),
18320                        &file,
18321                        package_name,
18322                        owner_short_name
18323                    ),
18324                    "the carried field index and the declaration scan disagree about \
18325                     {package_name:?}/{owner_short_name:?} after recording {}",
18326                    unit.fq_name()
18327                );
18328                answered += usize::from(carried);
18329            }
18330        }
18331
18332        // The whole-set build the first question performs must land on the same
18333        // index the incremental record built.
18334        let rebuilt = CppFieldOwnerIndex::of(
18335            foreign
18336                .iter()
18337                .chain(declarations.iter())
18338                .chain(deeper.iter()),
18339            &file,
18340        );
18341        for (package_name, owner_short_name) in &questions {
18342            assert_eq!(
18343                rebuilt.owns_fields(package_name, owner_short_name),
18344                index.owns_fields(package_name, owner_short_name),
18345                "a rebuilt index must answer what the incremental one answers for \
18346                 {package_name:?}/{owner_short_name:?}"
18347            );
18348        }
18349        answered
18350    }
18351
18352    /// The one thing the field index cannot absorb by addition: a deferred
18353    /// replacement of a declaration that owns children removes those children.
18354    ///
18355    /// The first enum builds the index, the struct records `Color.RED` into it,
18356    /// the body-less second `Color` replaces the first and takes `Color.RED`
18357    /// with it, and the last enum then asks about owner `Color`. An index that
18358    /// survived that removal answers `true` where the declarations say `false`,
18359    /// which is exactly what the in-walk agreement assertion catches (#2786).
18360    #[test]
18361    fn a_replacement_that_removes_children_drops_the_field_index() {
18362        let source =
18363            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
18364        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
18365        let mut names: Vec<_> = parsed
18366            .declarations()
18367            .iter()
18368            .map(|unit| unit.fq_name())
18369            .collect();
18370        names.sort();
18371        assert_eq!(
18372            names,
18373            vec![
18374                "Color".to_string(),
18375                "First".to_string(),
18376                "First.A".to_string()
18377            ],
18378            "the replaced Color owns no field any more"
18379        );
18380    }
18381
18382    /// A recovery that re-declares what the file already declared mints
18383    /// nothing.
18384    ///
18385    /// The reparse walk replaces the outer `Widget`, which removes its method,
18386    /// and then re-creates that method from the region. Creation alone would
18387    /// call the method recovered; it was there before the recovery opened, so
18388    /// the recovered set is empty and only the region's own reparse window is
18389    /// recorded (#2787).
18390    #[test]
18391    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
18392        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
18393                      BEGIN_NS\n\
18394                      namespace demo { struct Widget { void doWork(); }; }\n\
18395                      END_NS\n";
18396        let parsed = parse_cpp_declarations(source, "restored.cpp");
18397        let recovered: Vec<String> = parsed
18398            .materialization_records
18399            .iter()
18400            .filter_map(|record| match record {
18401                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
18402                _ => None,
18403            })
18404            .collect();
18405        assert!(
18406            recovered.is_empty(),
18407            "the region declares nothing the file did not already declare: {recovered:?}"
18408        );
18409        let mut names: Vec<String> = parsed
18410            .declarations()
18411            .iter()
18412            .map(|unit| unit.fq_name())
18413            .collect();
18414        names.sort();
18415        assert_eq!(
18416            names,
18417            vec![
18418                "demo".to_string(),
18419                "demo.Widget".to_string(),
18420                "demo.Widget.doWork".to_string(),
18421            ]
18422        );
18423    }
18424
18425    /// Four macro-sentinel recoveries in one file (#941). Each one must record
18426    /// exactly the declarations it minted -- not the ones an earlier recovery
18427    /// minted, and not the file's other declarations -- in start-byte order,
18428    /// and each record must carry its own reparse window (#2787).
18429    #[test]
18430    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
18431        let mut source = String::new();
18432        for index in 0..4 {
18433            let _ = write!(
18434                source,
18435                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
18436            );
18437        }
18438        source.push_str("void outside() {}\n");
18439        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
18440
18441        let recovered: Vec<(String, (usize, usize))> = parsed
18442            .materialization_records
18443            .iter()
18444            .filter_map(|record| match record {
18445                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
18446                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
18447                }
18448                _ => None,
18449            })
18450            .collect();
18451
18452        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
18453        for index in 0..4 {
18454            // The window the reparse covers: everything after the opening
18455            // sentinel token up to the newline before the closing one.
18456            let region = format!("namespace demo{index}");
18457            let region_start = source.find(&region).expect("each region is in the source");
18458            let start = source[..region_start]
18459                .rfind("BEGIN_NS")
18460                .expect("each region opens with a sentinel")
18461                + "BEGIN_NS".len();
18462            let end = start
18463                + source[start..]
18464                    .find("END_NS")
18465                    .expect("each region closes with a sentinel")
18466                - 1;
18467            let window = (start, end);
18468            for name in [
18469                format!("demo{index}"),
18470                format!("demo{index}.Widget{index}"),
18471                format!("demo{index}.Widget{index}.doWork{index}"),
18472            ] {
18473                expected.push((name, window));
18474            }
18475        }
18476        assert_eq!(
18477            recovered, expected,
18478            "each recovery records its own minted declarations, in order"
18479        );
18480        assert!(
18481            parsed
18482                .declarations()
18483                .iter()
18484                .any(|unit| unit.fq_name() == "outside"),
18485            "the declaration outside every region stays parsed and unrecovered"
18486        );
18487    }
18488
18489    #[test]
18490    fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
18491        assert!(
18492            field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
18493            "the fixture must actually own fields"
18494        );
18495
18496        // The shapes that make the two implementations diverge if the index
18497        // records the wrong keys: a nested enum's owner is its whole dotted
18498        // chain, a class named like an enum owns fields under that same name, a
18499        // `$` in a short name is an owner separator the dotted prefix rule must
18500        // not split on, and the same enum name in two namespaces is two owners.
18501        for (source, name) in [
18502            ("struct S { enum E { V }; };\n", "nested.hpp"),
18503            (
18504                "enum Color { RED };\nstruct Color { int RED; };\n",
18505                "class-like.c",
18506            ),
18507            ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
18508            (
18509                "enum E { V };\nnamespace ns { enum E { V }; }\n",
18510                "repeated.hpp",
18511            ),
18512            ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
18513        ] {
18514            field_owner_index_agreement(source, name);
18515        }
18516    }
18517
18518    /// `blocks` copies of one plain class-with-methods template. The class the
18519    /// question is about is always the first one, so the only thing that
18520    /// changes between two of these sources is how much unrelated tree
18521    /// surrounds it.
18522    fn repeated_class_blocks(blocks: usize) -> String {
18523        let mut source = String::from("namespace demo {\n");
18524        for index in 0..blocks {
18525            let _ = write!(
18526                source,
18527                "\nclass Widget{index} {{\npublic:\n    int translate() const {{ return {index}; }}\n    int helper(const Widget{index}& other) const {{ return other.translate(); }}\nprivate:\n    int field = {index};\n}};\n"
18528            );
18529        }
18530        source.push_str("\n}\n");
18531        source
18532    }
18533
18534    /// #1496: asking whether a recovered class shape claims one range must cost
18535    /// the path to that range, not a pass over the whole translation unit.
18536    ///
18537    /// The C++ inverse scan asks this once per candidate type reference, so a
18538    /// whole-tree walk makes one file's scan quadratic in its own size. Both
18539    /// sources here answer `None` -- these are plain classes that no recovery
18540    /// shape claims -- which is exactly the case that used to pay full price.
18541    #[test]
18542    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
18543        let mut answers = Vec::new();
18544        let mut visits = Vec::new();
18545        let mut node_counts = Vec::new();
18546        for blocks in [200usize, 400] {
18547            let source = repeated_class_blocks(blocks);
18548            let mut parser = tree_sitter::Parser::new();
18549            parser
18550                .set_language(&tree_sitter_cpp::LANGUAGE.into())
18551                .unwrap();
18552            let tree = parser.parse(&source, None).unwrap();
18553            let start_byte = source.find("class Widget0 ").expect("first class");
18554            let end_byte = start_byte
18555                + source[start_byte..]
18556                    .find("};")
18557                    .expect("first class terminator")
18558                + "};".len();
18559            let range = Range {
18560                start_byte,
18561                end_byte,
18562                start_line: 0,
18563                end_line: 0,
18564            };
18565            reset_recovered_class_body_node_visits_for_test();
18566            let recovered_export_classes =
18567                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
18568            answers.push(recovered_class_body_at(
18569                &recovered_export_classes,
18570                tree.root_node(),
18571                &source,
18572                "Widget0",
18573                &range,
18574            ));
18575            visits.push(recovered_class_body_node_visits_for_test());
18576            let mut nodes = 0usize;
18577            let mut stack = vec![tree.root_node()];
18578            while let Some(node) = stack.pop() {
18579                nodes += 1;
18580                let mut cursor = node.walk();
18581                stack.extend(node.named_children(&mut cursor));
18582            }
18583            node_counts.push(nodes);
18584        }
18585
18586        assert_eq!(
18587            answers,
18588            vec![None, None],
18589            "no recovered shape claims a plain class"
18590        );
18591        assert_eq!(
18592            visits[0], visits[1],
18593            "the walk must follow the range's own path, so doubling the unrelated \
18594             classes must not change the node count: {visits:?} over trees of \
18595             {node_counts:?} nodes"
18596        );
18597        assert!(
18598            visits[1] * 20 < node_counts[1],
18599            "the walk must stay far below one pass over the tree: {visits:?} over \
18600             trees of {node_counts:?} nodes"
18601        );
18602    }
18603}