Skip to main content

brokk_bifrost_cpp/
declarations.rs

1//! The C++ declaration walk, including the macro-sentinel error recovery.
2//!
3//! Every function here is a pure function of a parsed tree and its source text.
4//! `analyzer/cpp/adapter.rs` in `brokk-bifrost-analysis` drives
5//! [`CppVisitor`] out of `LanguageAdapter::parse_file`.
6
7use brokk_bifrost_core::analyzer::common::{node_source_text, parse_source_region};
8use brokk_bifrost_core::analyzer::fq_name::{
9    FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
10};
11use brokk_bifrost_core::analyzer::model::{
12    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
13    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
14    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
15    ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
16    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
17};
18use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
19use brokk_bifrost_core::analyzer::structural::materialization::{
20    GenerationKind, MaterializationRecord,
21};
22use brokk_bifrost_core::analyzer::tree_walk::{ParentIndex, WalkControl, walk_named_tree_preorder};
23use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
24use brokk_bifrost_core::hash::{HashMap, HashSet};
25use regex::Regex;
26use tree_sitter::{Node, Parser, Tree};
27
28/// Intern one qualified-name segment in the process-global interner.
29fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
30    segment_interner().intern(text, kind)
31}
32
33/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
34/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
35/// `::` head is exactly the mixed-separator store issue #1163 is about; the
36/// structured form records each namespace component, and the equivalence check
37/// renders it natively (with `::` between adjacent Package segments) so it
38/// round-trips to the legacy string. Splitting the already-joined string here is
39/// the M1 bridge — the legacy strings stay authoritative until M3.
40fn cpp_push_package(fq: &mut FqName, package_name: &str) {
41    for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
42        fq.push(cpp_segment(component, SegmentKind::Package));
43    }
44}
45
46/// C++ namespace paths are stored `::`-joined in `package_name` (issue #1163).
47const CPP_PACKAGE_SEPARATOR: &str = "::";
48
49/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
50/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
51/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
52/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
53/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
54/// cpp-specific native rendering rule is needed for this chain.
55fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
56    let mut first = true;
57    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
58    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
59    // entry point, not re-inference of an already-structured name.
60    for component in chain.split('$').filter(|c| !c.is_empty()) {
61        let kind = if first {
62            SegmentKind::Type
63        } else {
64            SegmentKind::Nested
65        };
66        fq.push(cpp_segment(component, kind));
67        first = false;
68    }
69}
70
71/// Structured name for a C++ namespace module: every `::`-separated component is
72/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
73/// `short_name` with an empty `package_name`).
74fn cpp_namespace_fq(full_name: &str) -> FqName {
75    let mut fq = FqName::new();
76    cpp_push_package(&mut fq, full_name);
77    fq
78}
79
80/// The per-level namespace components a `namespace_definition`'s `name` field
81/// declares.
82///
83/// A C++17 nested definition (`namespace a::b::c`) parses as a
84/// `nested_namespace_specifier` whose named children are the per-level
85/// `namespace_identifier`s plus, for three or more levels, a further
86/// `nested_namespace_specifier`; the `::` separators, the optional per-level
87/// `inline`, and the leading global `::` are all anonymous tokens the walk
88/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
89/// segment path as the expanded `namespace a { namespace b { } }` form.
90///
91/// A shape outside that grammar is the deliberately ill-formed source the
92/// diagnostic corpora carry. Those keep their historical single-component
93/// reading of the raw name text, which the caller still joins to the lexical
94/// namespace exactly as before.
95fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
96    let mut components = Vec::new();
97    let mut stack = vec![node];
98    while let Some(current) = stack.pop() {
99        match current.kind() {
100            "namespace_identifier" | "identifier" => {
101                components.push(normalize_cpp_whitespace(node_text(current, source)));
102            }
103            "nested_namespace_specifier" => {
104                for index in (0..current.named_child_count()).rev() {
105                    stack.push(
106                        current
107                            .named_child(index)
108                            .expect("index below the node's own named child count"),
109                    );
110                }
111            }
112            _ => return cpp_raw_namespace_name_components(node, source),
113        }
114    }
115    if components.iter().any(String::is_empty) {
116        return cpp_raw_namespace_name_components(node, source);
117    }
118    components
119}
120
121/// The historical reading of a namespace name node: its whole source text as
122/// one component, with a leading global `::` marker dropped so the caller's
123/// global-scope handling stays the AST boundary rather than a text prefix.
124///
125/// This is the recovery path for source outside the C++ grammar, so the text it
126/// returns can carry a separator that names nothing. react-native-windows
127/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
128/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
129/// trailing separator with no tail. The caller stores this component in
130/// `short_name` and derives the fq by splitting it back apart, so an empty tail
131/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
132/// here, at the one place the malformed text enters, rather than leaving each
133/// consumer to guard (#2353, a variant of #1878).
134fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
135    let start = node
136        .child(0)
137        .filter(|child| !child.is_named() && child.kind() == "::")
138        .map_or(node.start_byte(), |marker| marker.end_byte());
139    let text = normalize_cpp_whitespace(
140        source
141            .get(start..node.end_byte())
142            .expect("namespace name node covers one source range"),
143    );
144    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
145    if text.is_empty() {
146        return Vec::new();
147    }
148    vec![text]
149}
150
151/// Return the named namespace path that structurally encloses `node`.
152///
153/// This intentionally follows namespace AST ancestors rather than inspecting
154/// source text. Anonymous namespaces are not representable in the legacy C++
155/// package field, so a path containing one fails closed.
156fn cpp_lexical_namespace_name<'tree>(
157    node: Node<'tree>,
158    source: &str,
159    ancestry: &ParentIndex<'tree>,
160) -> Option<String> {
161    let mut components = Vec::new();
162    let mut ancestor = ancestry.parent(node);
163    while let Some(current) = ancestor {
164        if current.kind() == "namespace_definition" {
165            let name_node = current.child_by_field_name("name")?;
166            let name = normalize_cpp_whitespace(node_text(name_node, source));
167            if name.is_empty() {
168                return None;
169            }
170            components.push(name);
171        }
172        ancestor = ancestry.parent(current);
173    }
174    if components.is_empty() {
175        return None;
176    }
177    components.reverse();
178    // Same shared empty-component decision as `cpp_push_package`, which splits
179    // this string back apart: an enclosing namespace whose own name node is
180    // malformed contributes a component carrying its own separator (#2353).
181    Some(
182        normalize_joined(
183            &components.join(CPP_PACKAGE_SEPARATOR),
184            CPP_PACKAGE_SEPARATOR,
185        )
186        .into_owned(),
187    )
188}
189
190/// Nested-class `$` join for short names. An anonymous parent class (empty
191/// short_name) contributes no segment: the FqName bridge drops empty
192/// components, so a bare `parent$child` join would desync `short_name` from
193/// the fq and trip the package/short boundary assert in
194/// `CodeUnit::with_signature_and_fq` (#2140).
195fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
196    if parent_short.is_empty() {
197        name.to_string()
198    } else {
199        format!("{parent_short}${name}")
200    }
201}
202
203/// Member `.` join for short names; same anonymous-parent guard as
204/// [`cpp_join_nested_short`] (#2140).
205fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
206    if parent_short.is_empty() {
207        name.to_string()
208    } else {
209        format!("{parent_short}.{name}")
210    }
211}
212
213/// Structural fq for a leaf declaration: the parent unit's fq plus this
214/// declaration's own name as one segment (or the package segments plus the
215/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
216/// chain, so a literal `$` inside a source identifier (Cython template
217/// substitution points, gcc `$`-identifiers) survives instead of corrupting
218/// the chain and tripping the package/short boundary assert (#2140).
219fn cpp_leaf_fq(
220    package_name: &str,
221    parent: Option<&CodeUnit>,
222    name: &str,
223    kind_if_nested: SegmentKind,
224    kind_if_top: SegmentKind,
225) -> FqName {
226    if let Some(parent) = parent {
227        parent
228            .fq()
229            .clone()
230            .with_pushed(cpp_segment(name, kind_if_nested))
231    } else {
232        let mut fq = FqName::new();
233        cpp_push_package(&mut fq, package_name);
234        fq.push(cpp_segment(name, kind_if_top));
235        fq
236    }
237}
238
239/// Structured name for a member unit (function, field, enumerator). The
240/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
241/// the member has an owner, by `.member`; free functions and globals have no
242/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
243/// C++ member names never contain a literal `.`, so the single `.` (if any)
244/// separates the owner chain from the member.
245pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
246    let mut fq = FqName::new();
247    cpp_push_package(&mut fq, package_name);
248    match short_name.rsplit_once('.') {
249        Some((owner_chain, member)) => {
250            cpp_push_type_chain(&mut fq, owner_chain);
251            fq.push(cpp_segment(member, SegmentKind::Member));
252        }
253        None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
254    }
255    fq
256}
257
258#[derive(Clone)]
259pub struct ScopeInfo {
260    package_name: String,
261    module: Option<CodeUnit>,
262    class_unit: Option<CodeUnit>,
263    template_signature: Option<String>,
264    template_metadata: Option<CppTemplateMetadata>,
265    declarations_are_fields: bool,
266    recovered_specialization_member_scope: bool,
267    /// Namespace targets of every `using namespace X;` directive lexically
268    /// visible at this point in the file (declaration order), threaded
269    /// forward sibling-by-sibling by the sequential container walk (see
270    /// `CppWork::Siblings`). An out-of-line member definition written as a
271    /// bare `Class::method` at file/namespace scope with no enclosing
272    /// `namespace {}` block (issue #1093, e.g. log4cxx's
273    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
274    /// const { ... }`) has no other structural signal for which namespace
275    /// actually owns `Class`; this is the best-effort candidate list used to
276    /// recover it so the definition's indexed identity matches its header
277    /// declaration's.
278    visible_using_namespaces: Vec<String>,
279}
280
281struct CppContainer<'tree> {
282    node: Node<'tree>,
283    scope: ScopeInfo,
284}
285
286struct CppNodeWork<'tree> {
287    node: Node<'tree>,
288    scope: ScopeInfo,
289}
290
291/// Cursor over one container's remaining named children, processed one at a
292/// time (rather than all at once) so a `using namespace X;` sibling can
293/// update `scope.visible_using_namespaces` for the siblings that follow it,
294/// matching real C++ using-directive semantics. Nested container work is
295/// still pushed and fully drained before the cursor resumes (stack LIFO
296/// order), preserving the original left-to-right visitation order.
297struct CppSiblingsWork<'tree> {
298    children: std::vec::IntoIter<Node<'tree>>,
299    scope: ScopeInfo,
300}
301
302enum CppWork<'tree> {
303    Container(CppContainer<'tree>),
304    Node(CppNodeWork<'tree>),
305    Siblings(CppSiblingsWork<'tree>),
306}
307
308fn class_like_name<'tree>(
309    node: Node<'tree>,
310    source: &str,
311    ancestry: &ParentIndex<'tree>,
312) -> Option<String> {
313    let best = class_like_name_from_children(node, source);
314    if let Some(parent) = ancestry.parent(node)
315        && matches!(
316            parent.kind(),
317            "declaration" | "field_declaration" | "function_definition"
318        )
319        // A class_specifier carrying its own body proves the grammar name is
320        // the real class name: a sibling declarator then declares an object
321        // (`class X {} x;`), never a displaced class name. The gate matters
322        // when the class name is itself an all-caps token (`X`, `API`) --
323        // without it the export-macro re-read below steals the object
324        // declarator's name for the class (#2283). The genuine export-macro
325        // shapes leave the class_specifier bodyless, the same invariant
326        // recover_malformed_exported_multiple_base_class already gates on.
327        && cpp_body_node(node).is_none()
328        && node
329            .child_by_field_name("name")
330            .map(|name_node| {
331                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
332            })
333            .unwrap_or(false)
334        && let Some(recovered) = exported_class_name_from_node(parent, source)
335        && best.as_deref() != Some(recovered.as_str())
336    {
337        return Some(recovered);
338    }
339    best.or_else(|| {
340        node.child_by_field_name("name")
341            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
342            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
343    })
344}
345
346fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
347    let mut grammar_name = None;
348    if let Some(name_node) = node.child_by_field_name("name") {
349        let name = normalize_cpp_whitespace(node_text(name_node, source));
350        if name.is_empty() {
351            return None;
352        }
353        if !cpp_export_macro_token(&name) {
354            return Some(name);
355        }
356        grammar_name = Some(name);
357    }
358
359    let mut best = None;
360    let mut cursor = node.walk();
361    let mut stack = Vec::new();
362    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
363        if matches!(
364            child.kind(),
365            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
366        ) {
367            break;
368        }
369        stack.push(child);
370    }
371
372    while let Some(current) = stack.pop() {
373        if matches!(current.kind(), "type_identifier" | "identifier") {
374            let name = normalize_cpp_whitespace(node_text(current, source));
375            if !name.is_empty() && !cpp_export_macro_token(&name) {
376                best = Some(name);
377            }
378            continue;
379        }
380
381        for index in (0..current.named_child_count()).rev() {
382            if let Some(child) = current.named_child(index) {
383                stack.push(child);
384            }
385        }
386    }
387    best.or(grammar_name)
388}
389
390pub fn cpp_export_macro_token(token: &str) -> bool {
391    token
392        .chars()
393        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
394}
395
396struct RecoveredExportedClass<'tree> {
397    declaration_node: Node<'tree>,
398    name: String,
399    body: Option<Node<'tree>>,
400    raw_supertypes: Option<Vec<String>>,
401    uses_initializer_body: bool,
402    /// Present only for the fragmented multiple-base export shape (issue #938).
403    /// Carries the true class-body byte region -- the members tree-sitter scattered
404    /// out of the recovered node -- so they can be reparsed and re-owned as members
405    /// rather than lost inside the truncated `initializer_list` stand-in.
406    fragmented_body: Option<FragmentedExportBody>,
407}
408
409struct RecoveredFunctionLikeExportClassPair {
410    name: String,
411    range: Range,
412    raw_supertypes: Option<Vec<String>>,
413    fragmented_body: FragmentedExportBody,
414}
415
416struct RecoveredEmbeddedFunctionLikeExportClass {
417    name: String,
418    range: Range,
419    raw_supertypes: Vec<String>,
420    fragmented_body: FragmentedExportBody,
421}
422
423/// The recovered class-body geometry for a fragmented multiple-base export class.
424/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
425/// verbatim for a region reparse (issue #941 machinery) so every recovered member
426/// keeps its exact original byte/line position. `class_range` is the full class
427/// navigation range spanning to the displaced closing brace.
428struct FragmentedExportBody {
429    reparse_start: usize,
430    reparse_end: usize,
431    class_range: Range,
432}
433
434fn recovered_fragmented_export_body(
435    body: Node<'_>,
436    class_range: Range,
437) -> Option<FragmentedExportBody> {
438    let open = body.child(0).filter(|child| child.kind() == "{")?;
439    let close = body
440        .child(body.child_count().saturating_sub(1))
441        .filter(|child| child.kind() == "}" && !child.is_missing());
442    Some(FragmentedExportBody {
443        reparse_start: open.end_byte(),
444        // A zero-width missing `}` contributes no source byte. Keep the whole
445        // body range in that case; subtracting one byte would discard the last
446        // member's semicolon and make the otherwise valid region unsafe to
447        // index. A real close token is excluded by its structured start.
448        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
449        class_range,
450    })
451}
452
453struct DisplacedFragmentNamespaceBoundary<'tree> {
454    class_close: Node<'tree>,
455    class_semicolon: Node<'tree>,
456    namespace_items: Vec<Node<'tree>>,
457}
458
459/// Result of validating a reparsed fragmented class body.  A complete tree can
460/// safely consume the whole region.  A partial tree may contain only the exact
461/// class-named constructor that tree-sitter merged into an access label; its
462/// remaining siblings must stay on the ordinary outer walk.
463enum FragmentedExportMembers {
464    Complete(Tree),
465    ConditionalConstructor(Tree),
466}
467
468#[derive(Clone, Copy)]
469struct DisplacedMacroClassTail {
470    split_index: usize,
471    class_range: Range,
472}
473
474fn recover_exported_class_declaration<'tree>(
475    node: Node<'tree>,
476    source: &str,
477) -> Option<RecoveredExportedClass<'tree>> {
478    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
479        return Some(recovered);
480    }
481
482    let class_node = first_class_like_child(node)?;
483    if let Some(name_node) = class_node.child_by_field_name("name") {
484        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
485        if cpp_export_macro_token(&class_name) {
486            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
487            // Name declarator. Only a bare declarator can be the displaced class name;
488            // wrappers describe an object whose type merely happens to look macro-like.
489            let mut cursor = node.walk();
490            if node
491                .children_by_field_name("declarator", &mut cursor)
492                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
493            {
494                return None;
495            }
496        } else if has_direct_cpp_declarator(node) {
497            return None;
498        }
499    }
500    let name = exported_class_name_from_node(class_node, source)?;
501    Some(RecoveredExportedClass {
502        declaration_node: class_node,
503        name,
504        body: cpp_body_node(class_node),
505        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
506            .then(|| extract_cpp_supertypes(class_node, source)),
507        uses_initializer_body: false,
508        fragmented_body: None,
509    })
510}
511
512fn recover_malformed_exported_base_class<'tree>(
513    node: Node<'tree>,
514    source: &str,
515) -> Option<RecoveredExportedClass<'tree>> {
516    if node.kind() != "declaration" {
517        return None;
518    }
519    let class_node = node.child_by_field_name("type")?;
520    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
521        return None;
522    }
523    let macro_name = class_node
524        .child_by_field_name("name")
525        .and_then(|name| direct_identifier_name(name, source))?;
526    if !cpp_export_macro_token(&macro_name) {
527        return None;
528    }
529
530    let mut named_cursor = node.walk();
531    let mut named = node.named_children(&mut named_cursor);
532    if named
533        .next()
534        .is_none_or(|child| !same_node(child, class_node))
535    {
536        return None;
537    }
538    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
539    if displaced.kind() != "ERROR" {
540        return None;
541    }
542    let name = displaced_exported_class_name(displaced, source)?;
543
544    let remaining = named.collect::<Vec<_>>();
545    let init = *remaining.last()?;
546    if init.kind() != "init_declarator" {
547        return None;
548    }
549    let final_base = init
550        .child_by_field_name("declarator")
551        .and_then(|base| recovered_malformed_base_name(base, source))?;
552    let body = init.child_by_field_name("value")?;
553    // A complete reduction has a real closing brace here. In Chromium's Widget
554    // declaration, tree-sitter instead emits the same direct `}` slot as a
555    // zero-width missing node where the first body macro truncates the prefix.
556    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
557        return None;
558    }
559
560    if remaining[..remaining.len() - 1]
561        .iter()
562        .any(|child| match child.kind() {
563            "qualified_identifier"
564            | "scoped_type_identifier"
565            | "type_identifier"
566            | "identifier" => false,
567            "ERROR" => !is_malformed_inheritance_access(*child, source),
568            _ => true,
569        })
570    {
571        return None;
572    }
573
574    let mut raw_supertypes = Vec::new();
575    for base in &remaining[..remaining.len() - 1] {
576        if base.kind() == "ERROR" {
577            continue;
578        }
579        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
580    }
581    raw_supertypes.push(final_base);
582
583    Some(RecoveredExportedClass {
584        declaration_node: node,
585        name,
586        body: Some(body),
587        raw_supertypes: Some(raw_supertypes),
588        uses_initializer_body: true,
589        fragmented_body: fragmented_export_body_region(node, body, source),
590    })
591}
592
593/// Locate the true class-body region for a fragmented multiple-base export class.
594///
595/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
596/// emits in place of the real class body. Tree-sitter reduces that body in one of
597/// two shapes, both of which lose the members from the recovered node:
598///
599/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
600///   a real closing brace and holds the whole body text inline. The interior between
601///   the braces reparses to the members directly.
602/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
603///   first member with a zero-width MISSING `}`; every later member -- and the real
604///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
605///   siblings. The interior runs from the opening brace to that displaced `}`.
606///
607/// Returns the interior byte range to reparse plus the full class navigation range.
608fn fragmented_export_body_region(
609    node: Node<'_>,
610    body: Node<'_>,
611    source: &str,
612) -> Option<FragmentedExportBody> {
613    let reparse_start = body.start_byte() + 1;
614    let close = direct_close_brace(body)?;
615    if close.end_byte() > close.start_byte() {
616        return Some(FragmentedExportBody {
617            reparse_start,
618            reparse_end: close.start_byte(),
619            class_range: cpp_declaration_range(node),
620        });
621    }
622    // The closing brace was displaced past the recovered node. A balanced nested
623    // class keeps its own braces, so the first lone-`}` sibling is this class's.
624    let mut sibling = node.next_named_sibling();
625    let displaced_close = loop {
626        let Some(current) = sibling else {
627            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
628        };
629        if cpp_is_stray_close_brace(current, source) {
630            break current;
631        }
632        sibling = current.next_named_sibling();
633    };
634    Some(FragmentedExportBody {
635        reparse_start,
636        reparse_end: displaced_close.start_byte(),
637        class_range: Range {
638            start_byte: node.start_byte(),
639            end_byte: displaced_close.end_byte(),
640            start_line: node.start_position().row + 1,
641            end_line: displaced_close.end_position().row + 1,
642        },
643    })
644}
645
646/// Locate the true class-body region for the export-macro class shape that
647/// tree-sitter promotes to a `function_definition`.
648///
649/// In this shape the synthetic function body closes at the first inline
650/// method, while the class's real members continue as root-level siblings until
651/// a stray `}` followed by the displaced class `;`. Reparse the complete
652/// interior so those siblings are visited with the recovered class scope.
653fn fragmented_export_function_body_region(
654    node: Node<'_>,
655    body: Node<'_>,
656    source: &str,
657    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
658) -> Option<FragmentedExportBody> {
659    let reparse_start = body.start_byte().checked_add(1)?;
660    if let Some(boundary) = displaced_namespace {
661        return Some(FragmentedExportBody {
662            reparse_start,
663            reparse_end: boundary.class_close.start_byte(),
664            class_range: Range {
665                start_byte: node.start_byte(),
666                end_byte: boundary.class_semicolon.end_byte(),
667                start_line: node.start_position().row + 1,
668                end_line: boundary.class_semicolon.end_position().row + 1,
669            },
670        });
671    }
672    let siblings = cpp_following_named_siblings(node, source);
673    let boundary = fragmented_export_sibling_class_boundary(node, source);
674    let boundary_index = boundary.and_then(|boundary| {
675        siblings
676            .iter()
677            .position(|candidate| same_node(*candidate, boundary))
678    });
679    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
680    let mut sibling_index = 0;
681    // A complete recovered class's synthetic wrapper is immediately followed
682    // by its displaced semicolon (comments and a trailing attribute macro --
683    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
684    // may sit between the body and that semicolon). Only scan for a later
685    // stray close when real member siblings intervene; otherwise every earlier
686    // complete class would borrow the next malformed class's close and claim
687    // its members. The trailing-attribute case is the gtest shape: the scan
688    // borrowed a close ~1900 lines later and re-owned a following
689    // `namespace testing { namespace internal {` block as class members,
690    // doubling the package path ("testing::internal::testing::internal") and
691    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
692    // boundary assert (#2297).
693    while let Some(current) = siblings.get(sibling_index).copied() {
694        if current.kind() == "comment" {
695            sibling_index += 1;
696            continue;
697        }
698        if is_trailing_attribute_macro_sibling(current) {
699            sibling_index += 1;
700            continue;
701        }
702        if cpp_is_stray_semicolon(current, source) {
703            return None;
704        }
705        break;
706    }
707    while let Some(current) = siblings.get(sibling_index).copied() {
708        let next = siblings.get(sibling_index + 1).copied();
709        if cpp_is_stray_close_brace(current, source)
710            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
711        {
712            let semicolon = next.expect("checked above");
713            return Some(FragmentedExportBody {
714                reparse_start,
715                reparse_end: current.start_byte(),
716                class_range: Range {
717                    start_byte: node.start_byte(),
718                    end_byte: semicolon.end_byte(),
719                    start_line: node.start_position().row + 1,
720                    end_line: semicolon.end_position().row + 1,
721                },
722            });
723        }
724        // When the final access label keeps the class close in its malformed
725        // declaration body, tree-sitter nests the lone `}` ERROR below the
726        // label instead of exposing it as a direct sibling. Search only the
727        // scattered siblings after the synthetic wrapper. The first such
728        // close is the class terminator because nested class bodies retain
729        // their own balanced class_specifier nodes.
730        if current.start_byte() >= body.end_byte()
731            && let Some(close) = cpp_nested_stray_close_brace(current, source)
732        {
733            return Some(FragmentedExportBody {
734                reparse_start,
735                reparse_end: close.start_byte(),
736                class_range: Range {
737                    start_byte: node.start_byte(),
738                    end_byte: current.end_byte(),
739                    start_line: node.start_position().row + 1,
740                    end_line: current.end_position().row + 1,
741                },
742            });
743        }
744        sibling_index += 1;
745    }
746    boundary.map(|boundary| FragmentedExportBody {
747        reparse_start,
748        reparse_end: boundary.start_byte(),
749        class_range: Range {
750            start_byte: node.start_byte(),
751            end_byte: boundary.start_byte(),
752            start_line: node.start_position().row + 1,
753            end_line: boundary.start_position().row + 1,
754        },
755    })
756}
757
758/// Find a later macro-export class that tree-sitter lifted through an enclosing
759/// preprocessor container. A class that is still a direct sibling can be a
760/// nested member of the current fragmented class, so only a changed parent is
761/// a proven boundary between the two recovered class envelopes.
762fn fragmented_export_sibling_class_boundary<'tree>(
763    node: Node<'tree>,
764    source: &str,
765) -> Option<Node<'tree>> {
766    let node_parent = node.parent()?;
767    cpp_following_named_siblings(node, source)
768        .into_iter()
769        .find(|candidate| {
770            recover_exported_class_function_definition(*candidate, source).is_some()
771                && candidate
772                    .parent()
773                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
774        })
775}
776
777/// A trailing attribute macro after a recovered class's closing brace, spelled
778/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
779/// bare identifier is never a class member (members need a type), so this
780/// sibling can only be the class's own tail (#2297).
781fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
782    if node.kind() != "expression_statement" {
783        return false;
784    }
785    let mut cursor = node.walk();
786    let mut children = node.named_children(&mut cursor);
787    children
788        .next()
789        .is_some_and(|child| child.kind() == "identifier")
790        && children.next().is_none()
791}
792
793/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
794/// export-class wrapper can place the class close inside an access-label node,
795/// so direct-sibling checks alone miss the boundary.  Walk named CST children
796/// only; the helper does not inspect source text beyond the existing structured
797/// stray-brace predicate.
798fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
799    let mut stack = vec![node];
800    while let Some(current) = stack.pop() {
801        if cpp_is_stray_close_brace(current, source) {
802            return Some(current);
803        }
804        let mut cursor = current.walk();
805        stack.extend(current.named_children(&mut cursor));
806    }
807    None
808}
809
810/// Return named siblings that follow `node`, including siblings that tree-sitter
811/// attached to an enclosing container after malformed recovery split the local
812/// declaration list. Stop at the first structurally visible class close so a
813/// later namespace or exported class cannot supply the recovery boundary.
814fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
815    let mut siblings = Vec::new();
816    let mut anchor = node;
817    while let Some(parent) = anchor.parent() {
818        let at_translation_unit = parent.kind() == "translation_unit";
819        let mut sibling = anchor.next_named_sibling();
820        while let Some(current) = sibling {
821            if at_translation_unit
822                && (current.kind() == "namespace_definition"
823                    || (current.kind() == "function_definition"
824                        && first_class_like_child(current).is_some()))
825            {
826                return siblings;
827            }
828            siblings.push(current);
829            if cpp_is_stray_close_brace(current, source) {
830                if let Some(semicolon) = current
831                    .next_named_sibling()
832                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
833                {
834                    siblings.push(semicolon);
835                }
836                return siblings;
837            }
838            if current.start_byte() >= node.end_byte()
839                && matches!(current.kind(), "ERROR" | "labeled_statement")
840                && cpp_nested_stray_close_brace(current, source).is_some()
841            {
842                return siblings;
843            }
844            sibling = current.next_named_sibling();
845        }
846        anchor = parent;
847    }
848    siblings
849}
850
851fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
852    if node.start_byte() >= class_end {
853        return false;
854    }
855    node.end_byte() <= class_end
856        || cpp_nested_stray_close_brace(node, source)
857            .is_some_and(|close| close.start_byte() == class_end)
858}
859
860/// Recover a plain class whose opening prefix is retained in one ERROR node
861/// while one or more nested class closes and the outer close are displaced to
862/// sibling `}`/`;` nodes. This is the non-export counterpart to the fragmented
863/// export-class recovery above. All boundaries come from tree-sitter nodes: the
864/// direct class tokens establish nesting depth and the displaced close nodes
865/// terminate it.
866fn fragmented_plain_class_body<'tree>(
867    node: Node<'tree>,
868    source: &str,
869) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
870    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
871        return Some(recovered);
872    }
873    let supported_container = node.kind() == "ERROR"
874        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
875    if !supported_container {
876        return None;
877    }
878    let mut cursor = node.walk();
879    let children = node.children(&mut cursor).collect::<Vec<_>>();
880    let keyword = children.first()?;
881    if !matches!(keyword.kind(), "class" | "struct" | "union") {
882        return None;
883    }
884    let name_node = children
885        .iter()
886        .copied()
887        .skip(1)
888        .find(|child| child.is_named())?;
889    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
890        return None;
891    }
892    let name = normalize_cpp_whitespace(node_text(name_node, source));
893    if name.is_empty() || cpp_export_macro_token(&name) {
894        return None;
895    }
896    let open_index = children.iter().position(|child| child.kind() == "{")?;
897    let open = children[open_index];
898    let nested_class_opens = children[open_index + 1..]
899        .iter()
900        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
901        .count();
902    let mut closes_remaining = 1 + nested_class_opens;
903    let mut sibling = node.next_named_sibling();
904    while let Some(candidate) = sibling {
905        let next = candidate.next_named_sibling();
906        if cpp_is_stray_close_brace(candidate, source) {
907            closes_remaining -= 1;
908            if closes_remaining == 0 {
909                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
910                if open.end_byte() >= candidate.start_byte() {
911                    return None;
912                }
913                return Some((
914                    node,
915                    name,
916                    FragmentedExportBody {
917                        reparse_start: open.end_byte(),
918                        reparse_end: candidate.start_byte(),
919                        class_range: Range {
920                            start_byte: node.start_byte(),
921                            end_byte: semicolon.end_byte(),
922                            start_line: node.start_position().row + 1,
923                            end_line: semicolon.end_position().row + 1,
924                        },
925                    },
926                ));
927            }
928        }
929        sibling = next;
930    }
931    None
932}
933
934pub(crate) fn recovered_fragmented_plain_class_has_body(
935    node: Node<'_>,
936    source: &str,
937    expected_name: &str,
938    expected_range: &Range,
939) -> bool {
940    fragmented_plain_class_body(node, source).is_some_and(|(_, name, fragmented)| {
941        name == expected_name
942            && fragmented.class_range.start_byte == expected_range.start_byte
943            && fragmented.class_range.end_byte == expected_range.end_byte
944    })
945}
946
947/// Recover a plain class whose parser-visible body ends inside a malformed
948/// inline member. Tree-sitter then attaches either the next real member
949/// declarator or the unfinished `else` branch directly to the outer function
950/// definition and leaves the class's actual `};` among later siblings. Those
951/// structured continuations and the close/semicolon siblings establish the
952/// complete body envelope without interpreting source text.
953fn fragmented_plain_class_declaration_body<'tree>(
954    node: Node<'tree>,
955    source: &str,
956) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
957    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
958        return None;
959    }
960    let class_node = node.child_by_field_name("type")?;
961    if !matches!(
962        class_node.kind(),
963        "class_specifier" | "struct_specifier" | "union_specifier"
964    ) {
965        return None;
966    }
967    let name_node = class_node.child_by_field_name("name")?;
968    let name = normalize_cpp_whitespace(node_text(name_node, source));
969    if name.is_empty() || cpp_export_macro_token(&name) {
970        return None;
971    }
972    let body = cpp_body_node(class_node)?;
973    if body.kind() != "field_declaration_list" {
974        return None;
975    }
976    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
977        if declarator.start_byte() < class_node.end_byte() {
978            return None;
979        }
980        let mut cursor = node.walk();
981        node.named_children(&mut cursor).any(|child| {
982            if child.kind() != "ERROR"
983                || child.start_byte() < class_node.end_byte()
984                || child.end_byte() > declarator.start_byte()
985            {
986                return false;
987            }
988            let mut cursor = child.walk();
989            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
990            let Some((return_type, attributes)) = components.split_last() else {
991                return false;
992            };
993            matches!(
994                return_type.kind(),
995                "identifier"
996                    | "type_identifier"
997                    | "primitive_type"
998                    | "decltype"
999                    | "placeholder_type_specifier"
1000            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1001                && attributes.iter().all(|attribute| {
1002                    matches!(attribute.kind(), "identifier" | "type_identifier")
1003                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1004                            *attribute, source,
1005                        )))
1006                })
1007        })
1008    } else {
1009        let mut cursor = node.walk();
1010        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1011        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1012            if same_node(*candidate_class, class_node)
1013                && continuation.kind() == "identifier"
1014                && node_text(*continuation, source) == "else"
1015                && continuation_body.kind() == "compound_statement"
1016                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1017                && continuation_body
1018                    .child(continuation_body.child_count().saturating_sub(1))
1019                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1020    };
1021    if !displaced_member {
1022        return None;
1023    }
1024    let open = body
1025        .children(&mut body.walk())
1026        .find(|child| child.kind() == "{")?;
1027    let siblings = cpp_following_named_siblings(node, source);
1028    let ordinary_boundary =
1029        siblings
1030            .iter()
1031            .copied()
1032            .enumerate()
1033            .find_map(|(close_index, close)| {
1034                cpp_is_stray_close_brace(close, source)
1035                    .then(|| {
1036                        siblings
1037                            .get(close_index + 1)
1038                            .copied()
1039                            .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1040                            .map(|semicolon| (close, semicolon))
1041                    })
1042                    .flatten()
1043            });
1044    let (close, semicolon) =
1045        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1046            (boundary.class_close, boundary.class_semicolon)
1047        } else {
1048            ordinary_boundary?
1049        };
1050    if open.end_byte() >= close.start_byte() {
1051        return None;
1052    }
1053    Some((
1054        class_node,
1055        name,
1056        FragmentedExportBody {
1057            reparse_start: open.end_byte(),
1058            reparse_end: close.start_byte(),
1059            class_range: Range {
1060                start_byte: class_node.start_byte(),
1061                end_byte: semicolon.end_byte(),
1062                start_line: class_node.start_position().row + 1,
1063                end_line: semicolon.end_position().row + 1,
1064            },
1065        },
1066    ))
1067}
1068
1069fn displaced_export_function_namespace_shape<'tree>(
1070    declaration: Node<'tree>,
1071    source: &str,
1072) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1073    let mut nested = Vec::new();
1074    for index in (0..declaration.named_child_count()).rev() {
1075        nested.push(declaration.named_child(index)?);
1076    }
1077    while let Some(current) = nested.pop() {
1078        // A recovered export class nested in this class can consume the first
1079        // parser-visible namespace close itself. In that shape the existing
1080        // later-class boundary logic already distinguishes the nested and
1081        // namespace-sibling owners; do not mistake the nested close for this
1082        // class's terminator.
1083        if recover_exported_class_function_definition(current, source).is_some() {
1084            return None;
1085        }
1086        for index in (0..current.named_child_count()).rev() {
1087            nested.push(current.named_child(index)?);
1088        }
1089    }
1090    let mut same_envelope_sibling = declaration.next_named_sibling();
1091    while let Some(current) = same_envelope_sibling {
1092        if recover_exported_class_function_definition(current, source).is_some() {
1093            return None;
1094        }
1095        same_envelope_sibling = current.next_named_sibling();
1096    }
1097    let declaration_list = declaration.parent()?;
1098    if declaration_list.kind() != "declaration_list" {
1099        return None;
1100    }
1101    let namespace = declaration_list.parent()?;
1102    if namespace.kind() != "namespace_definition"
1103        || namespace.child_by_field_name("body") != Some(declaration_list)
1104    {
1105        return None;
1106    }
1107    let class_close = direct_close_brace(declaration_list)?;
1108    let trailing_semicolon = namespace.next_named_sibling()?;
1109    if trailing_semicolon.kind() != "expression_statement"
1110        || trailing_semicolon.named_child_count() != 0
1111    {
1112        return None;
1113    }
1114    // A chain of malformed export classes can consume one parser-visible
1115    // namespace close per class. Walk through the enclosing sibling levels so
1116    // the later real namespace close remains the structural boundary; a
1117    // direct next-sibling walk stops at the first collapsed namespace and
1118    // incorrectly makes its intervening items members of this class.
1119    let siblings = cpp_following_named_siblings(namespace, source);
1120    let trailing_index = siblings
1121        .iter()
1122        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1123    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1124        recover_exported_class_function_definition(*candidate, source).is_some()
1125    }) {
1126        // Consecutive recovered classes already have an exact sibling-class
1127        // boundary. Preserve that established path, including nested export
1128        // classes, instead of interpreting the first class close as a
1129        // collapsed namespace boundary.
1130        return None;
1131    }
1132    let mut namespace_items = Vec::new();
1133    let mut nested_fragment_end = 0;
1134    for current in siblings.into_iter().skip(trailing_index + 1) {
1135        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1136        {
1137            return Some(DisplacedFragmentNamespaceBoundary {
1138                class_close,
1139                class_semicolon: trailing_semicolon,
1140                namespace_items,
1141            });
1142        }
1143        if current.start_byte() >= nested_fragment_end
1144            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1145        {
1146            nested_fragment_end = fragmented.class_range.end_byte;
1147        } else if current.start_byte() >= nested_fragment_end
1148            && recover_exported_class_function_definition(current, source).is_some()
1149            && let Some(body) = cpp_body_node(current)
1150            && let Some(fragmented) =
1151                fragmented_export_function_body_region(current, body, source, None)
1152        {
1153            nested_fragment_end = fragmented.class_range.end_byte;
1154        }
1155        namespace_items.push(current);
1156    }
1157    None
1158}
1159
1160fn displaced_fragment_namespace_boundary<'tree>(
1161    declaration: Node<'tree>,
1162    body: Node<'tree>,
1163    source: &str,
1164) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1165    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1166    let reparse_start = body.start_byte() + 1;
1167    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1168    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1169}
1170
1171/// Recover the class/namespace brace geometry for a declaration whose class
1172/// close tree-sitter consumed as the enclosing namespace close. This proof is
1173/// independent of whether every member in the class body can be reparsed: the
1174/// ordinary-tree fallback can still re-own bounded sibling declarations when
1175/// an unknown macro makes the complete body reparse unsafe.
1176fn displaced_fragment_namespace_geometry<'tree>(
1177    declaration: Node<'tree>,
1178    source: &str,
1179) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1180    // A templated class's malformed function wrapper remains beneath the
1181    // template node even though its later members have escaped to the
1182    // enclosing declaration list. Lift only that exact declaration child.
1183    let envelope = declaration
1184        .parent()
1185        .filter(|parent| {
1186            parent.kind() == "template_declaration"
1187                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1188        })
1189        .unwrap_or(declaration);
1190    let declaration_list = envelope.parent()?;
1191    if declaration_list.kind() != "declaration_list" {
1192        return None;
1193    }
1194    let namespace = declaration_list.parent()?;
1195    if namespace.kind() != "namespace_definition"
1196        || namespace.child_by_field_name("body") != Some(declaration_list)
1197    {
1198        return None;
1199    }
1200    let class_close = direct_close_brace(declaration_list)?;
1201    let trailing_semicolon = namespace.next_named_sibling()?;
1202    if trailing_semicolon.kind() != "expression_statement"
1203        || trailing_semicolon.named_child_count() != 0
1204    {
1205        return None;
1206    }
1207    let mut namespace_items = Vec::new();
1208    let mut sibling = trailing_semicolon.next_named_sibling();
1209    let mut nested_fragment_end = 0;
1210    loop {
1211        let current = sibling?;
1212        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1213        {
1214            break;
1215        }
1216        if current.start_byte() >= nested_fragment_end
1217            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1218        {
1219            nested_fragment_end = fragmented.class_range.end_byte;
1220        }
1221        namespace_items.push(current);
1222        sibling = current.next_named_sibling();
1223    }
1224    Some(DisplacedFragmentNamespaceBoundary {
1225        class_close,
1226        class_semicolon: trailing_semicolon,
1227        namespace_items,
1228    })
1229}
1230
1231/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1232fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1233    (0..node.child_count())
1234        .filter_map(|index| node.child(index))
1235        .find(|child| !child.is_named() && child.kind() == "}")
1236}
1237
1238/// A displaced lone closing brace: the class close that the fragmented multiple-base
1239/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1240fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1241    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1242}
1243
1244/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1245/// text while skipping line/block comments and string/char literals. The
1246/// exported-class recovery needs this when tree-sitter's bogus
1247/// `function_definition` body runs past the class's true closing brace and
1248/// swallows following siblings (issue #1524): the grammar tree carries no
1249/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1250/// close is located textually. Returns `None` when the text is unbalanced or
1251/// contains a construct the scanner deliberately does not interpret (raw
1252/// strings) -- callers treat that as "cannot partition" and keep the
1253/// un-split recovery.
1254fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1255    let bytes = source.as_bytes();
1256    if bytes.get(open_byte) != Some(&b'{') {
1257        return None;
1258    }
1259    let mut depth = 0usize;
1260    let mut i = open_byte;
1261    while i < bytes.len() {
1262        match bytes[i] {
1263            b'{' => depth += 1,
1264            b'}' => {
1265                depth = depth.checked_sub(1)?;
1266                if depth == 0 {
1267                    return Some(i);
1268                }
1269            }
1270            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1271                while i < bytes.len() && bytes[i] != b'\n' {
1272                    i += 1;
1273                }
1274                continue;
1275            }
1276            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1277                i += 2;
1278                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1279                    i += 1;
1280                }
1281                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1282                continue;
1283            }
1284            quote @ (b'"' | b'\'') => {
1285                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1286                // bail out rather than mis-count.
1287                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1288                    return None;
1289                }
1290                i += 1;
1291                while i < bytes.len() && bytes[i] != quote {
1292                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1293                }
1294                if i >= bytes.len() {
1295                    return None;
1296                }
1297            }
1298            _ => {}
1299        }
1300        i += 1;
1301    }
1302    None
1303}
1304
1305fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1306    let mut name = None;
1307    let mut colon_count = 0;
1308    let mut access_count = 0;
1309    for index in 0..node.child_count() {
1310        let child = node.child(index)?;
1311        match child.kind() {
1312            "identifier" | "type_identifier" if child.is_named() => {
1313                if name.is_some() {
1314                    return None;
1315                }
1316                let candidate = normalize_cpp_whitespace(node_text(child, source));
1317                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1318                    return None;
1319                }
1320                name = Some(candidate);
1321            }
1322            "template_function" | "template_type" if child.is_named() => {
1323                if name.is_some() {
1324                    return None;
1325                }
1326                let candidate = child
1327                    .child_by_field_name("name")
1328                    .and_then(|name| direct_identifier_name(name, source))?;
1329                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1330                    return None;
1331                }
1332                name = Some(candidate);
1333            }
1334            ":" if !child.is_named() => colon_count += 1,
1335            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1336            _ => return None,
1337        }
1338    }
1339    (colon_count == 1 && access_count == 1)
1340        .then_some(name)
1341        .flatten()
1342}
1343
1344fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1345    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1346        return false;
1347    }
1348    node.named_child(0)
1349        .and_then(|child| direct_identifier_name(child, source))
1350        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1351}
1352
1353fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1354    (0..node.child_count()).any(|index| {
1355        node.child(index)
1356            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1357    })
1358}
1359
1360fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1361    match node.kind() {
1362        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1363            recovered_base_atom(node, source)
1364        }
1365        "template_type" | "template_function" => node
1366            .child_by_field_name("name")
1367            .and_then(|name| recovered_malformed_base_name(name, source)),
1368        "ERROR" => None,
1369        "qualified_identifier" | "scoped_type_identifier" => {
1370            let suffix = node
1371                .child_by_field_name("name")
1372                .and_then(|name| recovered_malformed_base_name(name, source))?;
1373            let scope = node
1374                .child_by_field_name("scope")
1375                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1376            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1377                malformed_qualified_prefix(node, source)?
1378            } else {
1379                if malformed_qualified_prefix(node, source).is_some() {
1380                    return None;
1381                }
1382                scope
1383            };
1384            Some(format!("{prefix}::{suffix}"))
1385        }
1386        _ => None,
1387    }
1388}
1389
1390fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1391    if !matches!(
1392        node.kind(),
1393        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1394    ) {
1395        return None;
1396    }
1397    let name = normalize_cpp_whitespace(node_text(node, source));
1398    (!name.is_empty()).then_some(name)
1399}
1400
1401fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1402    let mut prefix = None;
1403    let mut cursor = node.walk();
1404    for error in node
1405        .named_children(&mut cursor)
1406        .filter(|child| child.kind() == "ERROR")
1407    {
1408        if error.named_child_count() != 1 || prefix.is_some() {
1409            return None;
1410        }
1411        prefix = error
1412            .named_child(0)
1413            .and_then(|child| recovered_base_atom(child, source));
1414        prefix.as_ref()?;
1415    }
1416    prefix
1417}
1418
1419fn recover_exported_class_function_definition<'tree>(
1420    node: Node<'tree>,
1421    source: &str,
1422) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1423    if node.kind() != "function_definition" {
1424        return None;
1425    }
1426    if let Some(prefix) = node.prev_named_sibling()
1427        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
1428        && recovered.range.end_byte == node.end_byte()
1429    {
1430        return Some((node, recovered.name, recovered.raw_supertypes));
1431    }
1432    let type_node = node.child_by_field_name("type")?;
1433    let declarator = node.child_by_field_name("declarator")?;
1434
1435    if matches!(
1436        type_node.kind(),
1437        "class_specifier" | "struct_specifier" | "union_specifier"
1438    ) {
1439        let type_name = type_node
1440            .child_by_field_name("name")
1441            .and_then(|name| direct_identifier_name(name, source));
1442        let exported_macro_type = type_name
1443            .as_ref()
1444            .is_some_and(|name| cpp_export_macro_token(name));
1445        if exported_macro_type {
1446            let mut cursor = node.walk();
1447            let errors_before_declarator = node
1448                .named_children(&mut cursor)
1449                .filter(|child| {
1450                    child.kind() == "ERROR"
1451                        && child.start_byte() >= type_node.end_byte()
1452                        && child.end_byte() <= declarator.start_byte()
1453                })
1454                .collect::<Vec<_>>();
1455            if let Some(name) = errors_before_declarator
1456                .iter()
1457                .find_map(|error| displaced_exported_class_name(*error, source))
1458            {
1459                let raw_supertypes = errors_before_declarator
1460                    .iter()
1461                    .any(|error| malformed_inheritance_syntax(*error))
1462                    .then(|| recovered_malformed_base_name(declarator, source))
1463                    .flatten()
1464                    .map(|base| vec![base]);
1465                return Some((node, name, raw_supertypes));
1466            }
1467            if errors_before_declarator
1468                .iter()
1469                .any(|error| malformed_inheritance_syntax(*error))
1470            {
1471                return None;
1472            }
1473        }
1474        if !exported_macro_type
1475            && let Some(name) = type_name
1476            && !cpp_export_macro_token(&name)
1477            && let Some(base) =
1478                recovered_postfix_export_macro_base(node, type_node, declarator, source)
1479        {
1480            return Some((node, name, Some(vec![base])));
1481        }
1482        if let Some(name) = direct_identifier_name(declarator, source)
1483            && exported_macro_type
1484            && !cpp_export_macro_token(&name)
1485        {
1486            let raw_supertypes = exported_macro_type
1487                .then(|| recovered_single_base_after_declarator(node, declarator, source))
1488                .flatten()
1489                .map(|base| vec![base]);
1490            return Some((node, name, raw_supertypes));
1491        }
1492        if declarator.kind() == "parenthesized_declarator"
1493            && type_node
1494                .child_by_field_name("name")
1495                .and_then(|name| direct_identifier_name(name, source))
1496                .is_some_and(|name| cpp_export_macro_token(&name))
1497        {
1498            if let Some((name, base)) =
1499                recovered_function_like_export_class_owner(declarator, source)
1500            {
1501                return Some((node, name, Some(vec![base])));
1502            }
1503            let body_start = node
1504                .child_by_field_name("body")
1505                .map(|body| body.start_byte())
1506                .unwrap_or(node.end_byte());
1507            let mut cursor = node.walk();
1508            if let Some(name) = node
1509                .named_children(&mut cursor)
1510                .filter(|child| {
1511                    child.kind() == "ERROR"
1512                        && child.start_byte() >= declarator.end_byte()
1513                        && child.end_byte() <= body_start
1514                })
1515                .find_map(|error| declarator_name_from_node(error, source))
1516            {
1517                return Some((node, name, None));
1518            }
1519        }
1520    }
1521
1522    let declarator_text = direct_identifier_name(declarator, source)?;
1523    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1524        return None;
1525    }
1526    class_identifier_before_body(node, source).map(|name| (node, name, None))
1527}
1528
1529fn recovered_function_like_export_class_owner(
1530    declarator: Node<'_>,
1531    source: &str,
1532) -> Option<(String, String)> {
1533    if declarator.kind() != "parenthesized_declarator" {
1534        return None;
1535    }
1536    let mut cursor = declarator.walk();
1537    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
1538    let [prefix, base] = children.as_slice() else {
1539        return None;
1540    };
1541    if prefix.kind() != "ERROR"
1542        || !matches!(
1543            base.kind(),
1544            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
1545        )
1546    {
1547        return None;
1548    }
1549    let mut identifiers = Vec::new();
1550    let mut prefix_cursor = prefix.walk();
1551    for child in prefix.named_children(&mut prefix_cursor) {
1552        match child.kind() {
1553            "number_literal" | "string_literal" | "char_literal" => {}
1554            "identifier" | "type_identifier" => {
1555                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
1556            }
1557            _ => return None,
1558        }
1559    }
1560    let name = match identifiers.as_slice() {
1561        [name] => name.clone(),
1562        [name, final_token] if final_token == "final" => name.clone(),
1563        _ => return None,
1564    };
1565    if name.is_empty() || cpp_export_macro_token(&name) {
1566        return None;
1567    }
1568    let base = recovered_malformed_base_name(*base, source)?;
1569    Some((name, base))
1570}
1571
1572fn recover_function_like_export_class_pair(
1573    node: Node<'_>,
1574    source: &str,
1575) -> Option<RecoveredFunctionLikeExportClassPair> {
1576    if node.kind() != "ERROR" {
1577        return None;
1578    }
1579    let class_node = first_class_like_child(node)?;
1580    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1581        return None;
1582    }
1583    let macro_name = class_node
1584        .child_by_field_name("name")
1585        .and_then(|name| direct_identifier_name(name, source))?;
1586    if !cpp_export_macro_token(&macro_name) {
1587        return None;
1588    }
1589    let sibling = node.next_named_sibling()?;
1590    let (name, raw_supertypes, body) = match sibling.kind() {
1591        "expression_statement" => {
1592            let compound = sibling.named_child(0)?;
1593            if compound.kind() != "compound_literal_expression" {
1594                return None;
1595            }
1596            let body = compound.child_by_field_name("value")?;
1597            if body.kind() != "initializer_list" {
1598                return None;
1599            }
1600            (
1601                compound
1602                    .child_by_field_name("type")
1603                    .and_then(|name| direct_identifier_name(name, source))?,
1604                None,
1605                body,
1606            )
1607        }
1608        "labeled_statement" => {
1609            let label = sibling.child_by_field_name("label")?;
1610            if label.kind() != "statement_identifier" {
1611                return None;
1612            }
1613            let name = normalize_cpp_whitespace(node_text(label, source));
1614            let declaration = sibling
1615                .named_children(&mut sibling.walk())
1616                .find(|child| child.kind() == "declaration")?;
1617            let access = declaration.child_by_field_name("type")?;
1618            if !matches!(
1619                node_text(access, source),
1620                "public" | "protected" | "private"
1621            ) {
1622                return None;
1623            }
1624            let init = declaration.child_by_field_name("declarator")?;
1625            if init.kind() != "init_declarator" {
1626                return None;
1627            }
1628            let body = init.child_by_field_name("value")?;
1629            if body.kind() != "initializer_list" {
1630                return None;
1631            }
1632            let base = init
1633                .child_by_field_name("declarator")
1634                .and_then(|base| recovered_malformed_base_name(base, source))?;
1635            (name, Some(vec![base]), body)
1636        }
1637        "function_definition" => {
1638            let type_node = sibling.child_by_field_name("type")?;
1639            let body = sibling.child_by_field_name("body")?;
1640            if body.kind() != "compound_statement" {
1641                return None;
1642            }
1643            let name = direct_identifier_name(type_node, source)?;
1644            let mut bases = Vec::new();
1645            let mut sibling_cursor = sibling.walk();
1646            for child in sibling.named_children(&mut sibling_cursor) {
1647                if same_node(child, type_node) || same_node(child, body) {
1648                    continue;
1649                }
1650                if child.kind() == "ERROR" {
1651                    let mut error_cursor = child.walk();
1652                    bases.extend(
1653                        child
1654                            .named_children(&mut error_cursor)
1655                            .filter_map(|part| recovered_malformed_base_name(part, source)),
1656                    );
1657                } else if let Some(base) = recovered_malformed_base_name(child, source) {
1658                    bases.push(base);
1659                }
1660            }
1661            bases.retain(|base| {
1662                !matches!(base.as_str(), "final" | "public" | "protected" | "private")
1663            });
1664            let [base] = bases.as_slice() else {
1665                return None;
1666            };
1667            (name, Some(vec![base.clone()]), body)
1668        }
1669        _ => return None,
1670    };
1671    if name.is_empty() || cpp_export_macro_token(&name) {
1672        return None;
1673    }
1674    let range = Range {
1675        start_byte: node.start_byte(),
1676        end_byte: sibling.end_byte(),
1677        start_line: node.start_position().row + 1,
1678        end_line: sibling.end_position().row + 1,
1679    };
1680    Some(RecoveredFunctionLikeExportClassPair {
1681        name,
1682        raw_supertypes,
1683        range,
1684        fragmented_body: recovered_fragmented_export_body(body, range)?,
1685    })
1686}
1687
1688/// Recover a function-like export-macro class that tree-sitter embedded in a
1689/// larger error after an earlier malformed class body. The grammar still
1690/// preserves every part of the class head: the `class` token, export macro
1691/// identifier and argument list, displaced class identifier, access specifier,
1692/// base field, and initializer-list-shaped body. Match only that complete
1693/// structured sequence and keep each recovered class's exact byte envelope.
1694fn recover_embedded_function_like_export_classes(
1695    node: Node<'_>,
1696    source: &str,
1697) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
1698    if node.kind() != "ERROR" {
1699        return Vec::new();
1700    }
1701
1702    let mut nodes = Vec::new();
1703    let mut stack = vec![node];
1704    while let Some(current) = stack.pop() {
1705        nodes.push(current);
1706        for index in (0..current.child_count()).rev() {
1707            stack.push(
1708                current
1709                    .child(index)
1710                    .expect("index below the node's own child count"),
1711            );
1712        }
1713    }
1714    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
1715
1716    let mut recovered = Vec::new();
1717    for class_token in nodes
1718        .iter()
1719        .copied()
1720        .filter(|child| !child.is_named() && child.kind() == "class")
1721    {
1722        let row = class_token.start_position().row;
1723        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
1724            candidate.start_byte() >= class_token.end_byte()
1725                && candidate.start_position().row == row
1726                && matches!(
1727                    candidate.kind(),
1728                    "identifier" | "type_identifier" | "field_identifier"
1729                )
1730                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*candidate, source)))
1731        }) else {
1732            continue;
1733        };
1734        let Some(arguments) = nodes.iter().copied().find(|candidate| {
1735            candidate.kind() == "argument_list"
1736                && candidate.start_byte() >= macro_name.end_byte()
1737                && candidate.start_position().row == row
1738        }) else {
1739            continue;
1740        };
1741        let Some(name_node) = nodes.iter().copied().find(|candidate| {
1742            candidate.kind() == "identifier"
1743                && candidate.start_byte() >= arguments.end_byte()
1744                && candidate.start_position().row == row
1745        }) else {
1746            continue;
1747        };
1748        let name = normalize_cpp_whitespace(node_text(name_node, source));
1749        if name.is_empty() || cpp_export_macro_token(&name) {
1750            continue;
1751        }
1752        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
1753            candidate.kind() == "field_initializer"
1754                && candidate.start_byte() >= name_node.end_byte()
1755                && candidate
1756                    .child_by_field_name("field")
1757                    .or_else(|| candidate.named_child(0))
1758                    .is_some()
1759                && candidate
1760                    .child_by_field_name("value")
1761                    .or_else(|| {
1762                        let mut cursor = candidate.walk();
1763                        candidate
1764                            .named_children(&mut cursor)
1765                            .find(|child| child.kind() == "initializer_list")
1766                    })
1767                    .is_some_and(|value| value.kind() == "initializer_list")
1768        }) else {
1769            continue;
1770        };
1771        let has_access = nodes.iter().copied().any(|candidate| {
1772            candidate.start_byte() >= name_node.end_byte()
1773                && candidate.end_byte() <= base_initializer.start_byte()
1774                && matches!(
1775                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
1776                    "public" | "protected" | "private"
1777                )
1778        });
1779        if !has_access {
1780            continue;
1781        }
1782        let Some(base_node) = base_initializer
1783            .child_by_field_name("field")
1784            .or_else(|| base_initializer.named_child(0))
1785        else {
1786            continue;
1787        };
1788        let Some(base) = recovered_malformed_base_name(base_node, source) else {
1789            continue;
1790        };
1791        let body = base_initializer
1792            .child_by_field_name("value")
1793            .or_else(|| {
1794                let mut cursor = base_initializer.walk();
1795                base_initializer
1796                    .named_children(&mut cursor)
1797                    .find(|child| child.kind() == "initializer_list")
1798            })
1799            .expect("initializer-list value checked above");
1800        let range = Range {
1801            start_byte: class_token.start_byte(),
1802            end_byte: body.end_byte(),
1803            start_line: class_token.start_position().row + 1,
1804            end_line: body.end_position().row + 1,
1805        };
1806        if recovered
1807            .iter()
1808            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
1809                existing.name == name && existing.range == range
1810            })
1811        {
1812            continue;
1813        }
1814        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
1815            name,
1816            range,
1817            raw_supertypes: vec![base],
1818            fragmented_body: match recovered_fragmented_export_body(body, range) {
1819                Some(fragmented) => fragmented,
1820                None => continue,
1821            },
1822        });
1823    }
1824    recovered
1825}
1826
1827fn lifted_function_like_export_class_namespace<'tree>(
1828    node: Node<'tree>,
1829    source: &str,
1830    ancestry: &ParentIndex<'tree>,
1831) -> Option<String> {
1832    // A long malformed body can embed the next exported class several levels
1833    // below a bogus top-level function_definition. Compare namespace evidence
1834    // against that top-level envelope, not only the recovered ERROR's direct
1835    // parent. The source tree still proves the same boundary: one earlier
1836    // malformed namespace and one later standalone closing brace.
1837    let mut anchor = node;
1838    let parent = loop {
1839        let parent = ancestry.parent(anchor)?;
1840        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
1841            break parent;
1842        }
1843        anchor = parent;
1844    };
1845    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
1846        sibling.start_byte() > anchor.end_byte()
1847            && sibling.kind() == "ERROR"
1848            && sibling.named_child_count() == 0
1849            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
1850    });
1851    if !has_later_close {
1852        return None;
1853    }
1854    let candidates = parent
1855        .named_children(&mut parent.walk())
1856        .filter(|sibling| {
1857            sibling.kind() == "namespace_definition"
1858                && sibling.has_error()
1859                && sibling.end_byte() < anchor.start_byte()
1860        })
1861        .filter_map(|namespace| {
1862            namespace
1863                .child_by_field_name("name")
1864                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
1865                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
1866        })
1867        .collect::<Vec<_>>();
1868    let [namespace] = candidates.as_slice() else {
1869        return None;
1870    };
1871    Some(namespace.clone())
1872}
1873
1874pub(crate) fn recovered_function_like_export_class_pair_has_body(
1875    node: Node<'_>,
1876    source: &str,
1877    identifier: &str,
1878    range: &Range,
1879) -> bool {
1880    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
1881        recovered.name == identifier
1882            && recovered.range.start_byte == range.start_byte
1883            && recovered.range.end_byte == range.end_byte
1884    })
1885}
1886
1887pub(crate) fn recovered_embedded_function_like_export_class_has_body(
1888    node: Node<'_>,
1889    source: &str,
1890    identifier: &str,
1891    range: &Range,
1892) -> bool {
1893    recover_embedded_function_like_export_classes(node, source)
1894        .into_iter()
1895        .any(|recovered| {
1896            recovered.name == identifier
1897                && recovered.range.start_byte == range.start_byte
1898                && recovered.range.end_byte == range.end_byte
1899        })
1900}
1901
1902/// Whether `node` is the base type displaced into the declarator field of an
1903/// export-macro class that tree-sitter represented as a declaration or
1904/// function definition.
1905///
1906/// Declaration extraction already recovers this exact malformed envelope as a
1907/// class and records the declarator as its base. Reference extraction must use
1908/// the same structural fact instead of treating the node as a function name.
1909pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
1910    if !matches!(
1911        node.kind(),
1912        "qualified_identifier" | "scoped_type_identifier" | "template_type"
1913    ) {
1914        return false;
1915    }
1916    if let Some(function) = node.parent().filter(|parent| {
1917        parent.kind() == "function_definition"
1918            && parent
1919                .child_by_field_name("declarator")
1920                .is_some_and(|declarator| same_node(declarator, node))
1921    }) {
1922        return recover_exported_class_function_definition(function, source)
1923            .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
1924    }
1925    let Some(initializer) = node.parent().filter(|parent| {
1926        parent.kind() == "init_declarator"
1927            && parent
1928                .child_by_field_name("declarator")
1929                .is_some_and(|declarator| same_node(declarator, node))
1930    }) else {
1931        return false;
1932    };
1933    initializer
1934        .parent()
1935        .filter(|parent| parent.kind() == "declaration")
1936        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
1937        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
1938}
1939
1940/// Recover the class item from a region reparse that still carries the
1941/// sentinel's synthetic function envelope.  An unknown class attribute can
1942/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
1943/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
1944/// then nested below that function, so direct class-child lookup is not enough.
1945struct CppSentinelReparsedClass<'tree> {
1946    declaration_node: Node<'tree>,
1947    name: String,
1948    body: Node<'tree>,
1949    raw_supertypes: Option<Vec<String>>,
1950}
1951
1952fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1953    let mut cursor = root.walk();
1954    root.named_children(&mut cursor)
1955        .find(|child| child.kind() != "comment")
1956        .filter(|child| child.kind() == "template_declaration")
1957}
1958
1959fn cpp_sentinel_reparsed_class<'tree>(
1960    root: Node<'tree>,
1961    template_node: Option<Node<'tree>>,
1962    source: &str,
1963    ancestry: &ParentIndex<'tree>,
1964) -> Option<CppSentinelReparsedClass<'tree>> {
1965    let container = template_node.unwrap_or(root);
1966    let mut cursor = container.walk();
1967    for child in container.named_children(&mut cursor) {
1968        if matches!(
1969            child.kind(),
1970            "class_specifier" | "struct_specifier" | "union_specifier"
1971        ) {
1972            let name = class_like_name(child, source, ancestry)?;
1973            let body = cpp_body_node(child)?;
1974            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1975                .then(|| extract_cpp_supertypes(child, source));
1976            return Some(CppSentinelReparsedClass {
1977                declaration_node: child,
1978                name,
1979                body,
1980                raw_supertypes,
1981            });
1982        }
1983        if child.kind() == "declaration"
1984            && let Some(class_node) = first_class_like_child(child)
1985        {
1986            let name = class_like_name(class_node, source, ancestry)?;
1987            let body = cpp_body_node(class_node)?;
1988            let raw_supertypes =
1989                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1990                    .then(|| extract_cpp_supertypes(class_node, source));
1991            return Some(CppSentinelReparsedClass {
1992                declaration_node: class_node,
1993                name,
1994                body,
1995                raw_supertypes,
1996            });
1997        }
1998        // Only when the nested class item carries its own body. A bodyless
1999        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
2000        // a function definition -- is the export-macro shape recovered by the
2001        // next arm, and must fall through to it rather than abort the search.
2002        if child.kind() == "function_definition"
2003            && let Some(class_node) = first_class_like_child(child)
2004            && let Some(body) = cpp_body_node(class_node)
2005            && let Some(name) = class_like_name(class_node, source, ancestry)
2006        {
2007            let raw_supertypes =
2008                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
2009                    .then(|| extract_cpp_supertypes(class_node, source));
2010            return Some(CppSentinelReparsedClass {
2011                declaration_node: class_node,
2012                name,
2013                body,
2014                raw_supertypes,
2015            });
2016        }
2017        if child.kind() == "function_definition"
2018            && let Some((_, name, raw_supertypes)) =
2019                recover_exported_class_function_definition(child, source)
2020        {
2021            let body = cpp_body_node(child)?;
2022            return Some(CppSentinelReparsedClass {
2023                declaration_node: child,
2024                name,
2025                body,
2026                raw_supertypes,
2027            });
2028        }
2029    }
2030    None
2031}
2032
2033fn recovered_postfix_export_macro_base(
2034    node: Node<'_>,
2035    type_node: Node<'_>,
2036    declarator: Node<'_>,
2037    source: &str,
2038) -> Option<String> {
2039    let mut cursor = node.walk();
2040    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
2041        child.kind() == "ERROR"
2042            && child.start_byte() >= type_node.end_byte()
2043            && child.end_byte() <= declarator.start_byte()
2044            && postfix_export_macro_inheritance(*child, source)
2045    });
2046    malformed_clauses.next()?;
2047    if malformed_clauses.next().is_some() {
2048        return None;
2049    }
2050    recovered_malformed_base_name(declarator, source)
2051}
2052
2053fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
2054    let mut macro_count = 0;
2055    let mut colon_count = 0;
2056    let mut access_count = 0;
2057    for index in 0..node.child_count() {
2058        let Some(child) = node.child(index) else {
2059            return false;
2060        };
2061        match child.kind() {
2062            "identifier" | "type_identifier" if child.is_named() => {
2063                let candidate = normalize_cpp_whitespace(node_text(child, source));
2064                if !cpp_export_macro_token(&candidate) {
2065                    return false;
2066                }
2067                macro_count += 1;
2068            }
2069            ":" if !child.is_named() => colon_count += 1,
2070            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
2071            _ => return false,
2072        }
2073    }
2074    macro_count == 1 && colon_count == 1 && access_count == 1
2075}
2076
2077fn recovered_single_base_after_declarator(
2078    node: Node<'_>,
2079    declarator: Node<'_>,
2080    source: &str,
2081) -> Option<String> {
2082    let body_start = node
2083        .child_by_field_name("body")
2084        .map(|body| body.start_byte())
2085        .unwrap_or(node.end_byte());
2086    let mut cursor = node.walk();
2087    let mut bases = node
2088        .named_children(&mut cursor)
2089        .filter(|child| {
2090            child.kind() == "ERROR"
2091                && child.start_byte() >= declarator.end_byte()
2092                && child.end_byte() <= body_start
2093        })
2094        .filter_map(|error| displaced_exported_class_name(error, source));
2095    let base = bases.next()?;
2096    bases.next().is_none().then_some(base)
2097}
2098
2099fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
2100    (0..node.child_count()).any(|index| {
2101        node.child(index)
2102            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
2103    })
2104}
2105
2106pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
2107    recover_exported_class_function_definition(node, source).is_some()
2108}
2109
2110fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
2111    matches!(
2112        kind,
2113        "ERROR"
2114            | "preproc_if"
2115            | "preproc_ifdef"
2116            | "preproc_ifndef"
2117            | "preproc_else"
2118            | "preproc_elif"
2119    ) || (kind == "labeled_statement" && in_class_scope)
2120}
2121
2122pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
2123    if node.kind() != "declaration" {
2124        return false;
2125    }
2126    let mut ancestor = node.parent();
2127    while let Some(container) = ancestor {
2128        match container.kind() {
2129            "compound_statement" => {
2130                return container.parent().is_some_and(|class_container| {
2131                    is_recovered_exported_class_container(class_container, source)
2132                });
2133            }
2134            // These containers preserve ScopeInfo in visit_node. declaration_list is
2135            // the body container selected for a linkage specification.
2136            "template_declaration" | "linkage_specification" | "declaration_list" => {}
2137            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
2138            _ => return false,
2139        }
2140        ancestor = container.parent();
2141    }
2142    false
2143}
2144
2145pub fn recovered_exported_class_has_body(
2146    node: Node<'_>,
2147    source: &str,
2148    expected_name: &str,
2149) -> Option<bool> {
2150    match node.kind() {
2151        "function_definition" => {
2152            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
2153            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
2154        }
2155        "declaration" | "field_declaration" => {
2156            let recovered = recover_exported_class_declaration(node, source)?;
2157            (recovered.name == expected_name).then(|| recovered.body.is_some())
2158        }
2159        _ => None,
2160    }
2161}
2162
2163fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
2164    let body_start = node
2165        .child_by_field_name("body")
2166        .map(|body| body.start_byte())
2167        .unwrap_or(node.end_byte());
2168    let mut stack = Vec::new();
2169    for index in (0..node.named_child_count()).rev() {
2170        let Some(child) = node.named_child(index) else {
2171            continue;
2172        };
2173        if child.start_byte() >= body_start {
2174            continue;
2175        }
2176        stack.push(child);
2177    }
2178
2179    let mut best = None;
2180    while let Some(current) = stack.pop() {
2181        if matches!(current.kind(), "identifier" | "type_identifier") {
2182            let name = normalize_cpp_whitespace(node_text(current, source));
2183            if !name.is_empty()
2184                && !cpp_export_macro_token(&name)
2185                && !matches!(name.as_str(), "class" | "struct" | "union")
2186            {
2187                best = Some(name);
2188            }
2189            continue;
2190        }
2191
2192        for index in (0..current.named_child_count()).rev() {
2193            if let Some(child) = current.named_child(index)
2194                && child.start_byte() < body_start
2195            {
2196                stack.push(child);
2197            }
2198        }
2199    }
2200    best
2201}
2202
2203fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
2204    if node.kind() == "declaration"
2205        && node
2206            .child_by_field_name("type")
2207            .or_else(|| first_class_like_child(node))
2208            .is_some_and(|type_node| {
2209                matches!(
2210                    type_node.kind(),
2211                    "class_specifier" | "struct_specifier" | "union_specifier"
2212                )
2213            })
2214        && let Some(name) = node
2215            .child_by_field_name("declarator")
2216            .and_then(|declarator| declarator_name_from_node(declarator, source))
2217        && !cpp_export_macro_token(&name)
2218    {
2219        return Some(name);
2220    }
2221
2222    if node.kind() == "function_definition"
2223        && node.child_by_field_name("type").is_some_and(|type_node| {
2224            matches!(
2225                type_node.kind(),
2226                "class_specifier" | "struct_specifier" | "union_specifier"
2227            )
2228        })
2229        && let Some(name) = node
2230            .child_by_field_name("declarator")
2231            .and_then(|declarator| direct_identifier_name(declarator, source))
2232        && !cpp_export_macro_token(&name)
2233    {
2234        return Some(name);
2235    }
2236
2237    let class_node = if matches!(
2238        node.kind(),
2239        "class_specifier" | "struct_specifier" | "union_specifier"
2240    ) {
2241        node
2242    } else {
2243        first_class_like_child(node)?
2244    };
2245    class_like_name_from_children(class_node, source)
2246}
2247
2248fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
2249    if !matches!(
2250        node.kind(),
2251        "identifier" | "field_identifier" | "type_identifier"
2252    ) {
2253        return None;
2254    }
2255    let name = normalize_cpp_whitespace(node_text(node, source));
2256    (!name.is_empty()).then_some(name)
2257}
2258
2259fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
2260    match node.kind() {
2261        "identifier" | "field_identifier" | "type_identifier" => {
2262            let name = normalize_cpp_whitespace(node_text(node, source));
2263            (!name.is_empty()).then_some(name)
2264        }
2265        _ => {
2266            let mut cursor = node.walk();
2267            node.named_children(&mut cursor)
2268                .find_map(|child| declarator_name_from_node(child, source))
2269        }
2270    }
2271}
2272
2273fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
2274    let mut cursor = node.walk();
2275    node.named_children(&mut cursor).find(|child| {
2276        matches!(
2277            child.kind(),
2278            "class_specifier" | "struct_specifier" | "union_specifier"
2279        )
2280    })
2281}
2282
2283/// Push a container's children as a `Siblings` cursor rather than snapshotting
2284/// them all with one shared scope: children are visited one at a time so a
2285/// `using namespace X;` sibling can affect the scope threaded to the siblings
2286/// that textually follow it (issue #1093).
2287fn push_cpp_container_work<'tree>(
2288    node: Node<'tree>,
2289    scope: ScopeInfo,
2290    stack: &mut Vec<CppWork<'tree>>,
2291) {
2292    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
2293}
2294
2295/// Materialize one selected named-child range with a tree-sitter cursor. The
2296/// cursor advances linearly across the parent's concrete children; repeatedly
2297/// asking for `named_child(index)` is quadratic on very wide generated nodes.
2298fn push_cpp_sibling_range<'tree>(
2299    parent: Node<'tree>,
2300    start_index: usize,
2301    end_index: usize,
2302    scope: ScopeInfo,
2303    stack: &mut Vec<CppWork<'tree>>,
2304) {
2305    let mut cursor = parent.walk();
2306    let children = parent
2307        .named_children(&mut cursor)
2308        .skip(start_index)
2309        .take(end_index.saturating_sub(start_index))
2310        .collect::<Vec<_>>()
2311        .into_iter();
2312    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
2313}
2314
2315/// Advance a `Siblings` cursor by one child: dispatch the current child under
2316/// the scope accumulated from its *earlier* siblings, then push a
2317/// continuation for the remaining siblings carrying the scope updated for
2318/// *this* child (only `using namespace X;` directives change it). Pushing the
2319/// continuation before the current child's own node work means the current
2320/// child's subtree fully drains (LIFO) before the next sibling is visited,
2321/// preserving left-to-right order.
2322fn advance_cpp_siblings<'tree>(
2323    mut siblings: CppSiblingsWork<'tree>,
2324    source: &str,
2325    stack: &mut Vec<CppWork<'tree>>,
2326) {
2327    let Some(child) = siblings.children.next() else {
2328        return;
2329    };
2330    let current_scope = siblings.scope.clone();
2331    if let Some(namespace) = cpp_using_namespace_target(child, source) {
2332        siblings.scope.visible_using_namespaces.push(namespace);
2333    }
2334    if !siblings.children.as_slice().is_empty() {
2335        stack.push(CppWork::Siblings(siblings));
2336    }
2337    stack.push(CppWork::Node(CppNodeWork {
2338        node: child,
2339        scope: current_scope,
2340    }));
2341}
2342
2343/// The namespace target of a `using namespace X;` directive, or `None` for
2344/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
2345/// kind. Distinguished structurally by the presence of the grammar's literal
2346/// `namespace` keyword token among the node's children -- not by inspecting
2347/// source text -- so it never misreads a member-importing using-declaration
2348/// as a namespace directive.
2349fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
2350    if node.kind() != "using_declaration" {
2351        return None;
2352    }
2353    let mut cursor = node.walk();
2354    let is_namespace_directive = node
2355        .children(&mut cursor)
2356        .any(|child| child.kind() == "namespace");
2357    if !is_namespace_directive {
2358        return None;
2359    }
2360    let target = node.named_child(0)?;
2361    // A leading `::` is the explicit-global marker, not part of the namespace
2362    // path (`using namespace ::std::chrono;`). Drop that AST token before
2363    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
2364    // keeps: storing the marker verbatim desynced the legacy package string from
2365    // the FqName bridge, which splits on `::` and drops the empty leading
2366    // component, tripping the package/short boundary assert when a bare-owner
2367    // out-of-line definition borrowed the directive's namespace (#1093 path).
2368    let start = target
2369        .child(0)
2370        .filter(|child| !child.is_named() && child.kind() == "::")
2371        .map_or(target.start_byte(), |marker| marker.end_byte());
2372    let text = normalize_cpp_whitespace(
2373        source
2374            .get(start..target.end_byte())
2375            .expect("using-directive target covers one source range"),
2376    );
2377    (!text.is_empty()).then_some(text)
2378}
2379
2380/// Every `using namespace X;` directive target in a file, in source order, for
2381/// resolution-time consumers that need the file's using-directives without the
2382/// per-position scope threading extraction does. Parses `source` fresh and
2383/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
2384/// keys on the grammar's `namespace` keyword token, not source text), so it
2385/// never misreads a member-importing `using X::Y;` as a namespace directive.
2386///
2387/// This is a whole-file over-approximation of what is in scope at any one point
2388/// (a directive nested inside a `namespace {}` block or a function body is still
2389/// reported), which is exactly what the #1134 identity reconciler wants: extra
2390/// candidate namespaces that no visible class confirms are harmless, and two
2391/// that both confirm are treated as a genuine ambiguity by the reconciler.
2392pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
2393    let mut parser = Parser::new();
2394    if parser
2395        .set_language(&tree_sitter_cpp::LANGUAGE.into())
2396        .is_err()
2397    {
2398        return Vec::new();
2399    }
2400    let Some(tree) = parser.parse(source, None) else {
2401        return Vec::new();
2402    };
2403    let mut namespaces = Vec::new();
2404    let mut seen = std::collections::HashSet::new();
2405    let mut stack = vec![tree.root_node()];
2406    while let Some(node) = stack.pop() {
2407        if let Some(namespace) = cpp_using_namespace_target(node, source)
2408            && seen.insert(namespace.clone())
2409        {
2410            namespaces.push(namespace);
2411        }
2412        let mut cursor = node.walk();
2413        stack.extend(node.named_children(&mut cursor));
2414    }
2415    namespaces
2416}
2417
2418pub struct CppVisitor<'a> {
2419    pub file: &'a ProjectFile,
2420    pub source: &'a str,
2421    pub parsed: &'a mut ParsedFile,
2422    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
2423    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
2424    ///
2425    /// C has no nested tag scope: a struct/union/enum tag declared inside
2426    /// another aggregate's member list has the scope of the outer declaration
2427    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
2428    /// therefore declares a file-scope `inner` that a later file-scope
2429    /// `struct inner *p;` legitimately references, where C++ would make the
2430    /// same shape a nested class `outer::inner`. Headers carry no compilation
2431    /// language of their own and keep the conservative C++ interpretation.
2432    pub c_tag_semantics: bool,
2433    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
2434    /// Byte regions whose contents were re-owned by a fragmented export-class
2435    /// recovery (#938): the scattered members between the fragmented
2436    /// declaration and its displaced closing brace are indexed as members of
2437    /// the recovered class by the region reparse, so the ordinary sibling walk
2438    /// must not ALSO index them as top-level declarations (that double-indexing
2439    /// made a scattered nested class ambiguous between `Inner` and
2440    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
2441    /// linear scan at visit time is fine.
2442    pub consumed_fragment_regions: Vec<(usize, usize)>,
2443}
2444
2445impl<'a> CppVisitor<'a> {
2446    fn visit_function_like_export_class_pair<'tree>(
2447        &mut self,
2448        node: Node<'tree>,
2449        scope: &ScopeInfo,
2450        stack: &mut Vec<CppWork<'tree>>,
2451        ancestry: &ParentIndex<'tree>,
2452    ) -> bool {
2453        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
2454            return false;
2455        };
2456        let member_outcome = self
2457            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
2458        // A malformed class body can escape into several following siblings
2459        // before the next export-macro class head appears. Inspect siblings in
2460        // order and stop at the first envelope that contains such a head. One
2461        // envelope can contain several following classes, all recovered in a
2462        // single bounded traversal.
2463        let mut displaced = node.next_named_sibling();
2464        while let Some(candidate) = displaced {
2465            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
2466                break;
2467            }
2468            displaced = candidate.next_named_sibling();
2469        }
2470        let class_unit = self.visit_named_class_like_shape(
2471            node,
2472            recovered.name,
2473            // The adjacent initializer_list proves the class body envelope,
2474            // but its children are expression-shaped rather than declaration-
2475            // preserving. Index the class identity here; callable definitions
2476            // remain available from their ordinary out-of-line declarations.
2477            None,
2478            true,
2479            Some(recovered.range),
2480            recovered.raw_supertypes,
2481            scope,
2482            stack,
2483            ancestry,
2484        );
2485        self.parsed
2486            .record_materialization(MaterializationRecord::RecoveredDeclaration {
2487                recovery: recovered.range,
2488                unit: class_unit.clone(),
2489            });
2490        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
2491            && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
2492                tree.root_node(),
2493                class_unit.identifier(),
2494                self.source,
2495            )
2496        {
2497            self.visit_recovered_fragment_constructor(
2498                range,
2499                body,
2500                node,
2501                &class_unit,
2502                scope,
2503                ancestry,
2504            );
2505        }
2506        if let Some(outcome) = member_outcome {
2507            self.visit_fragmented_export_class_members(outcome, class_unit, scope);
2508        }
2509        self.consumed_fragment_regions
2510            .push((node.start_byte(), recovered.range.end_byte));
2511        true
2512    }
2513
2514    fn visit_embedded_function_like_export_classes<'tree>(
2515        &mut self,
2516        node: Node<'tree>,
2517        scope: &ScopeInfo,
2518        stack: &mut Vec<CppWork<'tree>>,
2519        ancestry: &ParentIndex<'tree>,
2520    ) -> bool {
2521        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
2522        let found = !recovered_classes.is_empty();
2523        for recovered in recovered_classes {
2524            let member_outcome = self.reparse_fragmented_export_class_members(
2525                &recovered.fragmented_body,
2526                &recovered.name,
2527            );
2528            let class_unit = self.visit_named_class_like_shape(
2529                node,
2530                recovered.name,
2531                None,
2532                true,
2533                Some(recovered.range),
2534                Some(recovered.raw_supertypes),
2535                scope,
2536                stack,
2537                ancestry,
2538            );
2539            self.parsed
2540                .record_materialization(MaterializationRecord::RecoveredDeclaration {
2541                    recovery: recovered.range,
2542                    unit: class_unit.clone(),
2543                });
2544            if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
2545                && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
2546                    tree.root_node(),
2547                    class_unit.identifier(),
2548                    self.source,
2549                )
2550            {
2551                self.visit_recovered_fragment_constructor(
2552                    range,
2553                    body,
2554                    node,
2555                    &class_unit,
2556                    scope,
2557                    ancestry,
2558                );
2559            }
2560            if let Some(outcome) = member_outcome {
2561                self.visit_fragmented_export_class_members(outcome, class_unit, scope);
2562            }
2563        }
2564        found
2565    }
2566
2567    #[allow(clippy::too_many_arguments)]
2568    pub fn visit_container(
2569        &mut self,
2570        node: Node<'_>,
2571        package_name: &str,
2572        module: Option<CodeUnit>,
2573        class_unit: Option<CodeUnit>,
2574        template_signature: Option<String>,
2575        visible_using_namespaces: Vec<String>,
2576    ) {
2577        let scope = ScopeInfo {
2578            package_name: package_name.to_string(),
2579            module,
2580            class_unit,
2581            template_signature,
2582            template_metadata: None,
2583            declarations_are_fields: false,
2584            recovered_specialization_member_scope: false,
2585            visible_using_namespaces,
2586        };
2587        self.run_container_work(node, scope);
2588    }
2589
2590    /// Whether a work node lies entirely inside a byte region consumed by a
2591    /// fragmented export-class recovery (#938); such nodes were already indexed
2592    /// as members of the recovered class by the region reparse.
2593    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
2594        self.consumed_fragment_regions
2595            .iter()
2596            .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
2597    }
2598
2599    /// Drive the container work loop from an explicit seed scope to completion. The
2600    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
2601    /// stays alive for the whole traversal.
2602    fn run_container_work<'tree>(&mut self, node: Node<'tree>, scope: ScopeInfo) {
2603        // Every ancestor question this walk asks is answered from one index
2604        // built here. Asking tree-sitter itself costs the node's position in
2605        // the tree, which made a generated header with thousands of top-level
2606        // declarations quadratic (#2361). `node` is the root of its own tree in
2607        // every caller -- the file's tree, or a region reparse's -- and the
2608        // ascent below costs nothing in that case while keeping the index
2609        // correct if a caller ever seeds the walk lower down.
2610        let mut root = node;
2611        while let Some(parent) = root.parent() {
2612            root = parent;
2613        }
2614        let ancestry = ParentIndex::new(root);
2615        let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
2616        while let Some(work) = stack.pop() {
2617            match work {
2618                CppWork::Container(container) => {
2619                    push_cpp_container_work(container.node, container.scope, &mut stack);
2620                }
2621                CppWork::Siblings(siblings) => {
2622                    advance_cpp_siblings(siblings, self.source, &mut stack);
2623                }
2624                CppWork::Node(work) => {
2625                    if self.node_is_inside_consumed_fragment(work.node) {
2626                        continue;
2627                    }
2628                    self.visit_node(work.node, &work.scope, &mut stack, &ancestry);
2629                }
2630            }
2631        }
2632    }
2633
2634    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
2635    /// it only when the entire region is member-shaped. This validation must happen
2636    /// before registering the recovered class because a rejected speculative range
2637    /// must not leak into the ordinary recovery path.
2638    fn reparse_fragmented_export_class_members(
2639        &self,
2640        fragmented: &FragmentedExportBody,
2641        class_name: &str,
2642    ) -> Option<FragmentedExportMembers> {
2643        if fragmented.reparse_start >= fragmented.reparse_end {
2644            return None;
2645        }
2646        let tree = cpp_reparse_fragmented_class_body(
2647            self.source,
2648            fragmented.reparse_start,
2649            fragmented.reparse_end,
2650        )?;
2651        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
2652            return Some(FragmentedExportMembers::Complete(tree));
2653        }
2654        let has_conditional_constructor = {
2655            let root = tree.root_node();
2656            let mut cursor = root.walk();
2657            root.named_children(&mut cursor).any(|child| {
2658                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
2659            })
2660        };
2661        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
2662    }
2663
2664    /// Index an already validated fragmented body as members of `class_unit`. The
2665    /// region reparse keeps each member's exact original byte and line positions.
2666    fn visit_fragmented_export_class_members(
2667        &mut self,
2668        outcome: FragmentedExportMembers,
2669        class_unit: CodeUnit,
2670        scope: &ScopeInfo,
2671    ) -> bool {
2672        let (tree, complete) = match outcome {
2673            FragmentedExportMembers::Complete(tree) => (tree, true),
2674            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
2675        };
2676        let root = tree.root_node();
2677        let class_name = class_unit.identifier().to_string();
2678        let member_scope = ScopeInfo {
2679            // A recovered export-macro class may borrow its namespace from an
2680            // earlier forward declaration even when the malformed node itself
2681            // sits at file scope. Use the recovered class identity as the
2682            // authoritative package for reparsed members as well.
2683            package_name: class_unit.package_name().to_string(),
2684            module: scope.module.clone(),
2685            class_unit: Some(class_unit),
2686            template_signature: scope.template_signature.clone(),
2687            template_metadata: None,
2688            declarations_are_fields: true,
2689            recovered_specialization_member_scope: false,
2690            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2691        };
2692        if !complete {
2693            // A conditional beginning immediately after an access label can
2694            // fragment one constructor declaration while leaving the rest of
2695            // the class body as unsafe statement soup. Recover only that
2696            // structurally proven constructor and leave the outer-tree
2697            // siblings unconsumed for their ordinary walk.
2698            let mut cursor = root.walk();
2699            let constructors = root
2700                .named_children(&mut cursor)
2701                .filter_map(|child| {
2702                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
2703                })
2704                .collect::<Vec<_>>();
2705            // The reparsed region is its own tree, so this drain walks it with
2706            // its own parent index.
2707            let reparsed_ancestry = ParentIndex::new(root);
2708            for constructor in constructors {
2709                let mut stack = Vec::new();
2710                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
2711                while let Some(work) = stack.pop() {
2712                    match work {
2713                        CppWork::Container(container) => {
2714                            push_cpp_container_work(container.node, container.scope, &mut stack);
2715                        }
2716                        CppWork::Siblings(siblings) => {
2717                            advance_cpp_siblings(siblings, self.source, &mut stack);
2718                        }
2719                        CppWork::Node(work) => {
2720                            self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
2721                        }
2722                    }
2723                }
2724            }
2725            return false;
2726        }
2727        self.run_container_work(root, member_scope);
2728        true
2729    }
2730
2731    fn visit_recovered_fragment_constructor<'tree>(
2732        &mut self,
2733        range: std::ops::Range<usize>,
2734        constructor_body: Node<'tree>,
2735        class_declaration: Node<'tree>,
2736        class_unit: &CodeUnit,
2737        scope: &ScopeInfo,
2738        ancestry: &ParentIndex<'tree>,
2739    ) {
2740        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
2741            return;
2742        };
2743        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
2744            tree.root_node(),
2745            range.start,
2746            class_unit.identifier(),
2747            self.source,
2748        ) else {
2749            return;
2750        };
2751        let member_scope = ScopeInfo {
2752            package_name: class_unit.package_name().to_string(),
2753            module: scope.module.clone(),
2754            class_unit: Some(class_unit.clone()),
2755            template_signature: scope.template_signature.clone(),
2756            template_metadata: None,
2757            declarations_are_fields: true,
2758            recovered_specialization_member_scope: false,
2759            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2760        };
2761        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
2762        else {
2763            return;
2764        };
2765        debug_assert_eq!(function.name, class_unit.identifier());
2766        let code_unit = function.code_unit(self.file.clone());
2767        self.parsed.add_code_unit_with_range(
2768            code_unit.clone(),
2769            Range {
2770                start_byte: function_declarator.start_byte(),
2771                end_byte: constructor_body.end_byte(),
2772                start_line: function_declarator.start_position().row + 1,
2773                end_line: constructor_body.end_position().row + 1,
2774            },
2775            None,
2776            None,
2777        );
2778        self.parsed.add_signature_with_metadata(
2779            code_unit.clone(),
2780            cpp_signature_metadata(
2781                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
2782                function_declarator,
2783                self.source,
2784                ancestry,
2785            )
2786            .with_declaration_only(false)
2787            .with_callable_linkage(cpp_callable_linkage(
2788                class_declaration,
2789                self.source,
2790                ancestry,
2791            )),
2792        );
2793        self.parsed.add_child(class_unit.clone(), code_unit);
2794    }
2795
2796    fn visit_recovered_fragment_prefix_members<'tree>(
2797        &mut self,
2798        root: Node<'tree>,
2799        constructor_start: usize,
2800        class_unit: &CodeUnit,
2801        scope: &ScopeInfo,
2802        ancestry: &ParentIndex<'tree>,
2803    ) {
2804        let member_scope = ScopeInfo {
2805            package_name: class_unit.package_name().to_string(),
2806            module: scope.module.clone(),
2807            class_unit: Some(class_unit.clone()),
2808            template_signature: scope.template_signature.clone(),
2809            template_metadata: None,
2810            declarations_are_fields: true,
2811            recovered_specialization_member_scope: false,
2812            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2813        };
2814        let mut stack = vec![root];
2815        while let Some(current) = stack.pop() {
2816            if current.kind() == "comment" || current.start_byte() >= constructor_start {
2817                continue;
2818            }
2819            if current.end_byte() <= constructor_start
2820                && current.kind() != "translation_unit"
2821                && current.kind() != "labeled_statement"
2822                && current.kind() != "ERROR"
2823            {
2824                let mut work_stack = Vec::new();
2825                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
2826                while let Some(work) = work_stack.pop() {
2827                    match work {
2828                        CppWork::Container(container) => {
2829                            push_cpp_container_work(
2830                                container.node,
2831                                container.scope,
2832                                &mut work_stack,
2833                            );
2834                        }
2835                        CppWork::Siblings(siblings) => {
2836                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
2837                        }
2838                        CppWork::Node(work) => {
2839                            self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
2840                        }
2841                    }
2842                }
2843                continue;
2844            }
2845            if matches!(
2846                current.kind(),
2847                "translation_unit" | "labeled_statement" | "ERROR"
2848            ) {
2849                let mut cursor = current.walk();
2850                stack.extend(current.named_children(&mut cursor));
2851            }
2852        }
2853    }
2854
2855    fn visit_node<'tree>(
2856        &mut self,
2857        node: Node<'tree>,
2858        scope: &ScopeInfo,
2859        stack: &mut Vec<CppWork<'tree>>,
2860        ancestry: &ParentIndex<'tree>,
2861    ) {
2862        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
2863            self.visit_node(node, &recovered_scope, stack, ancestry);
2864            return;
2865        }
2866        // Fragmented-class recovery below may consume a malformed function
2867        // envelope before the ordinary kind dispatch runs. Recover any
2868        // export-macro class embedded in that envelope first; the strict class
2869        // head/base/body predicate is independent of which later recovery owns
2870        // the surrounding parser fragment.
2871        if node.kind() == "function_definition" && node.has_error() {
2872            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
2873        }
2874        if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
2875        {
2876            let displaced_namespace_items =
2877                displaced_fragment_namespace_geometry(node, self.source)
2878                    .map(|boundary| boundary.namespace_items)
2879                    .unwrap_or_default();
2880            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
2881            let mut class_stack = Vec::new();
2882            // When the full body cannot be safely reparsed, the original class
2883            // node still proves ownership for its parser-visible prefix.
2884            let parser_visible_body =
2885                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
2886                    .then(|| cpp_body_node(class_node))
2887                    .flatten();
2888            let class_unit = self.visit_named_class_like_shape(
2889                class_node,
2890                name,
2891                parser_visible_body,
2892                true,
2893                Some(fragmented.class_range),
2894                Some(extract_cpp_supertypes(class_node, self.source)),
2895                scope,
2896                &mut class_stack,
2897                ancestry,
2898            );
2899            let member_scope = ScopeInfo {
2900                package_name: class_unit.package_name().to_string(),
2901                module: scope.module.clone(),
2902                class_unit: Some(class_unit.clone()),
2903                template_signature: scope.template_signature.clone(),
2904                template_metadata: None,
2905                declarations_are_fields: true,
2906                recovered_specialization_member_scope: false,
2907                visible_using_namespaces: scope.visible_using_namespaces.clone(),
2908            };
2909            let complete = outcome.is_some_and(|outcome| {
2910                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
2911            });
2912            if complete {
2913                self.consumed_fragment_regions
2914                    .push((node.start_byte(), fragmented.class_range.end_byte));
2915            } else {
2916                // A macro-constrained member can make the full body reparse
2917                // unsafe while tree-sitter still exposes later class members
2918                // as bounded siblings up to the displaced `}`/`;`. Keep the
2919                // structurally proven class/base declaration and re-own those
2920                // sibling nodes under it. They retain their original parser
2921                // nodes and exact ranges; the close boundary comes solely from
2922                // `fragmented_plain_class_body`.
2923                // Template wrappers put the escaped members beside the
2924                // template rather than beside its malformed declaration.
2925                for candidate in cpp_following_named_siblings(node, self.source) {
2926                    if candidate.start_byte() >= fragmented.reparse_end {
2927                        break;
2928                    }
2929                    if cpp_fragment_sibling_is_class_member(
2930                        candidate,
2931                        fragmented.reparse_end,
2932                        self.source,
2933                    ) {
2934                        self.recovered_class_sibling_scopes
2935                            .insert(candidate.id(), member_scope.clone());
2936                    }
2937                }
2938            }
2939            for item in displaced_namespace_items {
2940                self.recovered_class_sibling_scopes
2941                    .insert(item.id(), scope.clone());
2942            }
2943            stack.extend(class_stack);
2944            return;
2945        }
2946        match node.kind() {
2947            "template_declaration" => {
2948                if let Some(recovered) =
2949                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
2950                {
2951                    let mut template_scope = scope.clone();
2952                    template_scope.template_signature =
2953                        cpp_template_signature(node, recovered.declaration_node, self.source);
2954                    template_scope.template_metadata =
2955                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
2956                    let raw_supertypes =
2957                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
2958                    let mut class_stack = Vec::new();
2959                    let class_unit = self.visit_named_class_like_shape(
2960                        recovered.class_node,
2961                        recovered.name,
2962                        Some(recovered.body),
2963                        true,
2964                        Some(recovered.range),
2965                        raw_supertypes,
2966                        &template_scope,
2967                        &mut class_stack,
2968                        ancestry,
2969                    );
2970                    self.parsed.record_materialization(
2971                        MaterializationRecord::RecoveredDeclaration {
2972                            recovery: recovered.range,
2973                            unit: class_unit.clone(),
2974                        },
2975                    );
2976                    let member_scope = ScopeInfo {
2977                        package_name: template_scope.package_name.clone(),
2978                        module: template_scope.module.clone(),
2979                        class_unit: Some(class_unit.clone()),
2980                        template_signature: template_scope.template_signature.clone(),
2981                        template_metadata: None,
2982                        declarations_are_fields: true,
2983                        recovered_specialization_member_scope: recovered
2984                            .class_node
2985                            .child_by_field_name("name")
2986                            .is_some_and(|name| name.kind() == "template_type"),
2987                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
2988                    };
2989                    for tail_member in recovered.tail_members.into_iter().rev() {
2990                        stack.push(CppWork::Node(CppNodeWork {
2991                            node: tail_member,
2992                            scope: member_scope.clone(),
2993                        }));
2994                    }
2995                    stack.extend(class_stack);
2996                    for sibling in recovered.member_siblings {
2997                        self.recovered_class_sibling_scopes
2998                            .insert(sibling.id(), member_scope.clone());
2999                    }
3000                    return;
3001                }
3002                for index in (0..node.named_child_count()).rev() {
3003                    let Some(child) = node.named_child(index) else {
3004                        continue;
3005                    };
3006                    if matches!(
3007                        child.kind(),
3008                        "class_specifier"
3009                            | "struct_specifier"
3010                            | "union_specifier"
3011                            | "enum_specifier"
3012                            | "function_definition"
3013                            | "declaration"
3014                            | "field_declaration"
3015                            | "alias_declaration"
3016                            | "namespace_definition"
3017                    ) {
3018                        let mut template_scope = scope.clone();
3019                        template_scope.template_signature =
3020                            cpp_template_signature(node, child, self.source);
3021                        template_scope.template_metadata =
3022                            cpp_template_metadata(node, child, self.source, ancestry);
3023                        if let Some(recovered) = recover_fragmented_partial_specialization(
3024                            node,
3025                            child,
3026                            self.source,
3027                            ancestry,
3028                        ) {
3029                            let code_unit = self.visit_named_class_like_shape(
3030                                recovered.declaration_node,
3031                                recovered.name,
3032                                None,
3033                                true,
3034                                Some(recovered.range),
3035                                None,
3036                                &template_scope,
3037                                stack,
3038                                ancestry,
3039                            );
3040                            self.parsed.record_materialization(
3041                                MaterializationRecord::RecoveredDeclaration {
3042                                    recovery: recovered.range,
3043                                    unit: code_unit.clone(),
3044                                },
3045                            );
3046                            let mut member_scope = template_scope.clone();
3047                            member_scope.class_unit = Some(code_unit);
3048                            member_scope.declarations_are_fields = true;
3049                            member_scope.recovered_specialization_member_scope = true;
3050                            for prefix_member in recovered.prefix_members.into_iter().rev() {
3051                                stack.push(CppWork::Node(CppNodeWork {
3052                                    node: prefix_member,
3053                                    scope: member_scope.clone(),
3054                                }));
3055                            }
3056                            for sibling in recovered.member_siblings {
3057                                self.recovered_class_sibling_scopes
3058                                    .insert(sibling.id(), member_scope.clone());
3059                            }
3060                            for following in recovered.following_declarations.into_iter().rev() {
3061                                stack.push(CppWork::Node(CppNodeWork {
3062                                    node: following,
3063                                    scope: scope.clone(),
3064                                }));
3065                            }
3066                            return;
3067                        }
3068                        stack.push(CppWork::Node(CppNodeWork {
3069                            node: child,
3070                            scope: template_scope,
3071                        }));
3072                    }
3073                }
3074            }
3075            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
3076            "linkage_specification" => {
3077                if let Some(body) = cpp_body_node(node) {
3078                    stack.push(CppWork::Container(CppContainer {
3079                        node: body,
3080                        scope: scope.clone(),
3081                    }));
3082                } else {
3083                    stack.push(CppWork::Container(CppContainer {
3084                        node,
3085                        scope: scope.clone(),
3086                    }));
3087                }
3088            }
3089            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
3090                self.visit_class_like(node, scope, stack, ancestry)
3091            }
3092            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
3093            // A bare namespace-begin sentinel can make tree-sitter promote the
3094            // wrapped declaration to an ERROR node instead of the usual bogus
3095            // function_definition envelope. Keep the recovery entry point on
3096            // the same structured path for both shapes; ordinary ERROR nodes
3097            // retain their declaration-preserving wrapper traversal when the
3098            // sentinel predicate does not match.
3099            "ERROR" => {
3100                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
3101                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
3102                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
3103                        return;
3104                    }
3105                    self.visit_macro_swallowed_function_declarations(node, scope);
3106                    stack.push(CppWork::Container(CppContainer {
3107                        node,
3108                        scope: scope.clone(),
3109                    }));
3110                }
3111            }
3112            "declaration" => {
3113                if scope.class_unit.is_some()
3114                    && scope.declarations_are_fields
3115                    && scope.recovered_specialization_member_scope
3116                    && let Some(alias_name) =
3117                        recovered_using_declaration_alias_name(node, self.source)
3118                {
3119                    self.add_type_aliases(node, scope, vec![alias_name]);
3120                } else {
3121                    self.visit_declaration(
3122                        node,
3123                        scope,
3124                        scope.declarations_are_fields,
3125                        stack,
3126                        ancestry,
3127                    )
3128                }
3129            }
3130            "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
3131            "type_definition" | "alias_declaration" => {
3132                self.visit_type_declaration(node, scope, stack, ancestry)
3133            }
3134            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
3135            "preproc_include" => self.visit_include(node),
3136            kind if preserves_declaration_scope_through_wrapper(
3137                kind,
3138                scope.class_unit.is_some(),
3139            ) =>
3140            {
3141                // A preprocessor conditional gates every declaration inside it
3142                // on a configuration this analyzer never evaluates; record the
3143                // interval so declaration state can say so (issue #1476). The
3144                // else/elif branches are children of the `preproc_if` node, so
3145                // recording the openers covers every branch.
3146                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
3147                    let mut range = cpp_declaration_range(node);
3148                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
3149                        range.end_byte = boundary.end_byte;
3150                        range.end_line = boundary.end_line;
3151                    }
3152                    self.parsed.record_materialization(
3153                        MaterializationRecord::ConfigurationConditional { range },
3154                    );
3155                    if node.has_error() {
3156                        // A malformed export-macro class can close the namespace
3157                        // node early while the enclosing include guard still owns
3158                        // the remaining class-head/body pairs. The ordinary walk
3159                        // cannot carry the lost namespace through those promoted
3160                        // siblings. Scan only structured ERROR nodes in this
3161                        // already-malformed conditional; the pair recovery's
3162                        // exact class/macro/body predicate remains the admission
3163                        // gate, and its namespace lifting restores the owner.
3164                        let mut candidates = vec![node];
3165                        while let Some(candidate) = candidates.pop() {
3166                            if candidate.kind() == "ERROR"
3167                                && self.visit_function_like_export_class_pair(
3168                                    candidate, scope, stack, ancestry,
3169                                )
3170                            {
3171                                continue;
3172                            }
3173                            for index in (0..candidate.named_child_count()).rev() {
3174                                candidates.push(
3175                                    candidate
3176                                        .named_child(index)
3177                                        .expect("index below the node's own named child count"),
3178                                );
3179                            }
3180                        }
3181                    }
3182                }
3183                stack.push(CppWork::Container(CppContainer {
3184                    node,
3185                    scope: scope.clone(),
3186                }))
3187            }
3188            _ => {}
3189        }
3190    }
3191
3192    fn visit_macro_swallowed_function_declarations<'tree>(
3193        &mut self,
3194        envelope: Node<'tree>,
3195        scope: &ScopeInfo,
3196    ) {
3197        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
3198            || envelope.kind() == "ERROR"
3199                && envelope
3200                    .parent()
3201                    .is_some_and(|parent| parent.kind() == "ERROR")
3202        {
3203            return;
3204        }
3205        let mut stack = (0..envelope.named_child_count())
3206            .filter_map(|index| envelope.named_child(index))
3207            .collect::<Vec<_>>();
3208        while let Some(node) = stack.pop() {
3209            if node.kind() == "function_declarator" {
3210                self.visit_error_swallowed_function_declaration(node, scope);
3211            }
3212            for index in 0..node.named_child_count() {
3213                if let Some(child) = node.named_child(index) {
3214                    stack.push(child);
3215                }
3216            }
3217        }
3218    }
3219
3220    fn visit_error_swallowed_function_declaration<'tree>(
3221        &mut self,
3222        node: Node<'tree>,
3223        scope: &ScopeInfo,
3224    ) -> bool {
3225        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
3226            return false;
3227        };
3228        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
3229            return false;
3230        };
3231        let root = tree.root_node();
3232        let mut cursor = root.walk();
3233        let declarations = root
3234            .named_children(&mut cursor)
3235            .filter(|child| child.kind() != "comment")
3236            .collect::<Vec<_>>();
3237        let [declaration] = declarations.as_slice() else {
3238            return false;
3239        };
3240        if declaration.kind() != "declaration"
3241            || declaration.has_error()
3242            || declaration.start_byte() != start
3243            || declaration.end_byte() != end
3244        {
3245            return false;
3246        }
3247        let recovery = cpp_recovery_window(self.source, start, end);
3248        self.record_recovered_declarations(recovery, |visitor| {
3249            visitor.run_container_work(root, scope.clone());
3250        });
3251        true
3252    }
3253
3254    fn visit_namespace<'tree>(
3255        &mut self,
3256        node: Node<'tree>,
3257        scope: &ScopeInfo,
3258        stack: &mut Vec<CppWork<'tree>>,
3259        ancestry: &ParentIndex<'tree>,
3260    ) {
3261        let name_node = node.child_by_field_name("name");
3262        let Some(name_node) = name_node else {
3263            if let Some(body) = cpp_body_node(node) {
3264                stack.push(CppWork::Container(CppContainer {
3265                    node: body,
3266                    scope: scope.clone(),
3267                }));
3268            }
3269            return;
3270        };
3271        // Diagnostic corpora contain deliberately ill-formed global namespace
3272        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
3273        // the leading global `::` as the first anonymous child. Honor that AST
3274        // boundary instead of appending the name to the lexical namespace;
3275        // appending produced legacy names such as `outer::::outer::inner`, which
3276        // could not round-trip through the structured FqName boundary.
3277        let explicitly_global = name_node
3278            .child(0)
3279            .is_some_and(|child| !child.is_named() && child.kind() == "::");
3280        let components = cpp_namespace_name_components(name_node, self.source);
3281        if components.is_empty() {
3282            return;
3283        }
3284        // One Module per namespace level. C++17's `namespace a::b { ... }` is
3285        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
3286        // shorthand must declare `a` as well as `a::b` -- extracting only the
3287        // innermost level left the enclosing namespace undeclared and made the
3288        // two spellings of one construct disagree (issue #1878).
3289        let mut package_name = if explicitly_global {
3290            String::new()
3291        } else {
3292            scope.package_name.clone()
3293        };
3294        let mut module = None;
3295        for component in components {
3296            let full_name = if package_name.is_empty() {
3297                component
3298            } else {
3299                format!("{package_name}::{component}")
3300            };
3301            let level = CodeUnit::new_fq(
3302                self.file.clone(),
3303                CodeUnitType::Module,
3304                "",
3305                full_name.clone(),
3306                cpp_namespace_fq(&full_name),
3307            );
3308            if !self.parsed.contains_declaration(&level) {
3309                self.parsed
3310                    .add_code_unit(level.clone(), node, self.source, None, None);
3311            }
3312            package_name = full_name;
3313            module = Some(level);
3314        }
3315
3316        let namespace_scope = ScopeInfo {
3317            package_name,
3318            module,
3319            // C++ never nests a namespace inside a class, so a surviving
3320            // class_unit here is always recovery bleed: a malformed-region
3321            // boundary upstream mis-scoped this namespace block. Keeping the
3322            // owner would mint the namespace's declarations as class members
3323            // under a re-appended package, desyncing the fq boundary assert
3324            // (#2306). Dropping it is identity-neutral for valid code, where
3325            // class_unit is always empty at a namespace definition.
3326            class_unit: None,
3327            template_signature: scope.template_signature.clone(),
3328            template_metadata: scope.template_metadata.clone(),
3329            declarations_are_fields: false,
3330            recovered_specialization_member_scope: false,
3331            visible_using_namespaces: scope.visible_using_namespaces.clone(),
3332        };
3333        let container = cpp_body_node(node).unwrap_or(node);
3334        // A malformed export-macro class body may turn the following class
3335        // into a descendant of a bogus function/labeled/error envelope. Those
3336        // descendants are not declaration containers and the ordinary walk
3337        // intentionally does not descend into them. Scan the namespace tree
3338        // once for the strict embedded class geometry before scheduling its
3339        // normal declarations. When one envelope matches, its helper recovers
3340        // every embedded class and the walk need not inspect its descendants.
3341        let mut candidates = vec![container];
3342        while let Some(candidate) = candidates.pop() {
3343            if matches!(
3344                candidate.kind(),
3345                "ERROR" | "function_definition" | "labeled_statement"
3346            ) && self.visit_embedded_function_like_export_classes(
3347                candidate,
3348                &namespace_scope,
3349                stack,
3350                ancestry,
3351            ) {
3352                continue;
3353            }
3354            for index in (0..candidate.named_child_count()).rev() {
3355                candidates.push(
3356                    candidate
3357                        .named_child(index)
3358                        .expect("index below the node's own named child count"),
3359                );
3360            }
3361        }
3362        stack.push(CppWork::Container(CppContainer {
3363            node: container,
3364            scope: namespace_scope,
3365        }));
3366    }
3367
3368    fn visit_class_like<'tree>(
3369        &mut self,
3370        node: Node<'tree>,
3371        scope: &ScopeInfo,
3372        stack: &mut Vec<CppWork<'tree>>,
3373        ancestry: &ParentIndex<'tree>,
3374    ) {
3375        let Some(name) = class_like_name(node, self.source, ancestry) else {
3376            return;
3377        };
3378        let name = qualified_class_name_chain(node, self.source, scope)
3379            .map(|chain| chain.join("$"))
3380            .unwrap_or(name);
3381        self.visit_named_class_like(node, name, scope, stack, ancestry);
3382    }
3383
3384    fn visit_named_class_like<'tree>(
3385        &mut self,
3386        node: Node<'tree>,
3387        name: String,
3388        scope: &ScopeInfo,
3389        stack: &mut Vec<CppWork<'tree>>,
3390        ancestry: &ParentIndex<'tree>,
3391    ) {
3392        let body = cpp_body_node(node);
3393        let definition_body_present = body.is_some();
3394        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
3395            .then(|| extract_cpp_supertypes(node, self.source));
3396        self.visit_named_class_like_shape(
3397            node,
3398            name,
3399            body,
3400            definition_body_present,
3401            None,
3402            raw_supertypes,
3403            scope,
3404            stack,
3405            ancestry,
3406        );
3407    }
3408
3409    /// Whether this class-like declaration is a C tag that belongs to the
3410    /// enclosing non-aggregate scope rather than to the aggregate it is
3411    /// lexically written inside.
3412    ///
3413    /// `class_specifier` is deliberately excluded: `class` is not C, so text
3414    /// that spells one in a `.c` file is not C code and keeps the C++ reading
3415    /// rather than getting a half-C identity.
3416    fn mints_tag_at_enclosing_c_scope(
3417        &self,
3418        declaration_node: Node<'_>,
3419        scope: &ScopeInfo,
3420        ancestry: &ParentIndex<'_>,
3421    ) -> bool {
3422        self.c_tag_semantics
3423            && scope.class_unit.is_some()
3424            && class_like_name(declaration_node, self.source, ancestry).is_some()
3425            && matches!(
3426                declaration_node.kind(),
3427                "struct_specifier" | "union_specifier" | "enum_specifier"
3428            )
3429    }
3430
3431    #[allow(clippy::too_many_arguments)]
3432    fn visit_named_class_like_shape<'tree>(
3433        &mut self,
3434        declaration_node: Node<'tree>,
3435        name: String,
3436        body: Option<Node<'tree>>,
3437        definition_body_present: bool,
3438        explicit_range: Option<Range>,
3439        raw_supertypes: Option<Vec<String>>,
3440        scope: &ScopeInfo,
3441        stack: &mut Vec<CppWork<'tree>>,
3442        ancestry: &ParentIndex<'tree>,
3443    ) -> CodeUnit {
3444        let displaced_macro_tail = if explicit_range.is_none() {
3445            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
3446        } else {
3447            None
3448        };
3449        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
3450        let recovered_scope = self.scope_for_recovered_exported_class(
3451            declaration_node,
3452            &name,
3453            definition_body_present,
3454            scope,
3455            ancestry,
3456        );
3457        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
3458        // aggregate's member list is declared at the enclosing non-aggregate
3459        // scope, not nested inside the aggregate. `scope.class_unit` is the
3460        // only aggregate carrier in this walk, so dropping it puts the tag at
3461        // the nearest enclosing non-aggregate scope -- the module at file or
3462        // namespace scope, and the same block-scope representation a
3463        // function-local aggregate already gets. The tag's own body scope
3464        // below still owns its members, so fields and enumerators are
3465        // unaffected.
3466        let c_tag_scope;
3467        let scope =
3468            if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
3469                c_tag_scope = ScopeInfo {
3470                    class_unit: None,
3471                    ..recovered_scope.clone()
3472                };
3473                &c_tag_scope
3474            } else {
3475                &recovered_scope
3476            };
3477        let short_name = if let Some(parent) = &scope.class_unit {
3478            cpp_join_nested_short(parent.short_name(), &name)
3479        } else {
3480            name.clone()
3481        };
3482        // A top-level out-of-line qualified class definition (`struct
3483        // Outer::Inner { ... }` inside its namespace, #2246) carries its
3484        // nesting chain as the `$`-joined display name; push one Type/Nested
3485        // segment per class so segment-pop owner navigation keeps working.
3486        // Every other leaf name stays opaque so a literal `$` in a source
3487        // identifier never crosses the split/join boundary (#2140).
3488        let qualified_chain = if scope.class_unit.is_none() {
3489            qualified_class_name_chain(declaration_node, self.source, scope)
3490                .filter(|chain| chain.join("$") == name)
3491        } else {
3492            None
3493        };
3494        let fq = if let Some(chain) = qualified_chain {
3495            let mut fq = FqName::new();
3496            cpp_push_package(&mut fq, &scope.package_name);
3497            let mut first = true;
3498            for component in chain {
3499                let kind = if first {
3500                    SegmentKind::Type
3501                } else {
3502                    SegmentKind::Nested
3503                };
3504                fq.push(cpp_segment(&component, kind));
3505                first = false;
3506            }
3507            fq
3508        } else {
3509            cpp_leaf_fq(
3510                &scope.package_name,
3511                scope.class_unit.as_ref(),
3512                &name,
3513                SegmentKind::Nested,
3514                SegmentKind::Type,
3515            )
3516        };
3517        let code_unit = CodeUnit::with_signature_and_fq(
3518            self.file.clone(),
3519            CodeUnitType::Class,
3520            scope.package_name.clone(),
3521            short_name,
3522            scope.template_signature.clone(),
3523            false,
3524            fq,
3525        );
3526        let has_body = definition_body_present;
3527        if !has_body && self.parsed.contains_declaration(&code_unit) {
3528            self.parsed.record_navigation_range(
3529                code_unit.clone(),
3530                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
3531            );
3532            return code_unit;
3533        }
3534        if has_body {
3535            if let Some(range) = explicit_range {
3536                self.parsed.replace_code_unit_with_range_deferred(
3537                    code_unit.clone(),
3538                    range,
3539                    None,
3540                    None,
3541                );
3542            } else {
3543                self.parsed.replace_code_unit_deferred(
3544                    code_unit.clone(),
3545                    declaration_node,
3546                    self.source,
3547                    None,
3548                    None,
3549                );
3550            }
3551        } else {
3552            self.parsed
3553                .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3554        }
3555        if let Some(raw_supertypes) = raw_supertypes {
3556            self.parsed
3557                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
3558        }
3559        self.parsed.add_signature(
3560            code_unit.clone(),
3561            render_cpp_type_signature(
3562                declaration_node,
3563                self.source,
3564                scope.template_signature.as_deref(),
3565            ),
3566        );
3567        if let Some(metadata) = &scope.template_metadata {
3568            let primary_short_name = if let Some(parent) = &scope.class_unit {
3569                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
3570            } else {
3571                metadata.primary_name.clone()
3572            };
3573            let primary_fq_name = CodeUnit::new(
3574                self.file.clone(),
3575                CodeUnitType::Class,
3576                scope.package_name.clone(),
3577                primary_short_name,
3578            )
3579            .fq_name();
3580            let mut metadata = metadata.clone();
3581            metadata.primary_fq_name = primary_fq_name;
3582            self.parsed
3583                .set_cpp_template_metadata(code_unit.clone(), metadata);
3584        }
3585        if let Some(parent) = &scope.class_unit {
3586            self.parsed.add_child(parent.clone(), code_unit.clone());
3587        } else if let Some(module) = &scope.module {
3588            self.parsed.add_child(module.clone(), code_unit.clone());
3589        }
3590
3591        if let Some(body) = body {
3592            let mut nested_scope = scope.clone();
3593            nested_scope.class_unit = Some(code_unit.clone());
3594            nested_scope.template_signature = scope.template_signature.clone();
3595            // Template metadata describes the class just created. It must not
3596            // leak into ordinary nested declarations in that class's body.
3597            // Recovered export-macro specializations carry a separate scope bit
3598            // for their declaration-shaped body members.
3599            nested_scope.template_metadata = None;
3600            // Export-macro class bodies recovered from a function_definition use
3601            // compound_statement children, whose direct fields are declarations.
3602            nested_scope.recovered_specialization_member_scope =
3603                scope.template_metadata.as_ref().is_some_and(|metadata| {
3604                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
3605                });
3606            nested_scope.declarations_are_fields =
3607                is_recovered_exported_class_container(declaration_node, self.source)
3608                    || nested_scope.recovered_specialization_member_scope;
3609            if let Some(displaced) = displaced_macro_tail {
3610                // A macro-shaped field without a source semicolon can make
3611                // tree-sitter consume the real class terminator as an ERROR
3612                // inside that field, then retain following namespace items as
3613                // later field-list children. Drain the proven class prefix
3614                // first and re-own only the structured tail with the outer
3615                // scope. The tail is pushed first because the work stack is
3616                // LIFO.
3617                push_cpp_sibling_range(
3618                    body,
3619                    displaced.split_index,
3620                    usize::MAX,
3621                    scope.clone(),
3622                    stack,
3623                );
3624                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
3625            } else {
3626                stack.push(CppWork::Container(CppContainer {
3627                    node: body,
3628                    scope: nested_scope,
3629                }));
3630            }
3631        }
3632        if declaration_node.kind() == "enum_specifier" {
3633            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
3634            if !self.has_enum_enumerator_units(&code_unit) {
3635                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
3636            }
3637        }
3638        code_unit
3639    }
3640
3641    fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
3642        let prefix = format!("{}.", parent.short_name());
3643        let parent_short = parent.short_name();
3644        self.parsed.declarations().iter().any(|unit| {
3645            unit.kind() == CodeUnitType::Field
3646                && unit.source() == parent.source()
3647                && unit.package_name() == parent.package_name()
3648                && if parent_short.is_empty() {
3649                    // Anonymous enum/union parent: its enumerators carry bare
3650                    // short names (#2140), so presence means any ownerless
3651                    // field in this file.
3652                    !unit.short_name().contains(['.', '$'])
3653                } else {
3654                    unit.short_name().starts_with(&prefix)
3655                }
3656        })
3657    }
3658
3659    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
3660        walk_named_tree_preorder(node, false, |child| {
3661            if child.kind() != "enumerator" {
3662                return WalkControl::Continue;
3663            }
3664            let Some(name_node) = child.child_by_field_name("name") else {
3665                return WalkControl::Continue;
3666            };
3667            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
3668            if name.is_empty() {
3669                return WalkControl::Continue;
3670            }
3671            let code_unit = CodeUnit::new_fq(
3672                self.file.clone(),
3673                CodeUnitType::Field,
3674                scope.package_name.clone(),
3675                cpp_join_member_short(parent.short_name(), &name),
3676                parent
3677                    .fq()
3678                    .clone()
3679                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
3680            );
3681            if self.parsed.contains_declaration(&code_unit) {
3682                return WalkControl::Continue;
3683            }
3684            self.parsed.add_code_unit(
3685                code_unit.clone(),
3686                child,
3687                self.source,
3688                Some(parent.clone()),
3689                None,
3690            );
3691            self.parsed.add_signature(
3692                code_unit,
3693                normalize_cpp_whitespace(node_text(child, self.source)),
3694            );
3695            WalkControl::Continue
3696        });
3697    }
3698
3699    fn visit_enum_enumerators_from_text(
3700        &mut self,
3701        node: Node<'_>,
3702        scope: &ScopeInfo,
3703        parent: &CodeUnit,
3704    ) {
3705        let text = node_text(node, self.source);
3706        let Some((_, body)) = text.split_once('{') else {
3707            return;
3708        };
3709        let Some((body, _)) = body.rsplit_once('}') else {
3710            return;
3711        };
3712        for entry in body.split(',') {
3713            let trimmed = entry.trim();
3714            let name = trimmed
3715                .split('=')
3716                .next()
3717                .unwrap_or("")
3718                .split_whitespace()
3719                .next()
3720                .unwrap_or("");
3721            if name.is_empty() {
3722                continue;
3723            }
3724            let code_unit = CodeUnit::new_fq(
3725                self.file.clone(),
3726                CodeUnitType::Field,
3727                scope.package_name.clone(),
3728                cpp_join_member_short(parent.short_name(), name),
3729                parent
3730                    .fq()
3731                    .clone()
3732                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
3733            );
3734            if self.parsed.contains_declaration(&code_unit) {
3735                continue;
3736            }
3737            self.parsed.add_code_unit(
3738                code_unit.clone(),
3739                node,
3740                self.source,
3741                Some(parent.clone()),
3742                None,
3743            );
3744            self.parsed.add_signature(code_unit, trimmed.to_string());
3745        }
3746    }
3747
3748    fn visit_function_definition<'tree>(
3749        &mut self,
3750        node: Node<'tree>,
3751        scope: &ScopeInfo,
3752        stack: &mut Vec<CppWork<'tree>>,
3753        ancestry: &ParentIndex<'tree>,
3754    ) {
3755        // A file-scope object-like macro sentinel the parser cannot see (issue
3756        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
3757        // prefixes as a bogus `function_definition` that swallows real namespaces,
3758        // classes, and members. Reparse the swallowed interior as C++ items so the
3759        // ordinary declaration visitors index it with byte/line-exact ownership.
3760        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
3761            return;
3762        }
3763        if node.has_error() {
3764            self.visit_macro_swallowed_function_declarations(node, scope);
3765        }
3766        if let Some((class_node, name, raw_supertypes)) =
3767            recover_exported_class_function_definition(node, self.source)
3768        {
3769            let body = cpp_body_node(class_node);
3770            let displaced_namespace = cpp_body_node(node)
3771                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
3772            let fragmented = cpp_body_node(node).and_then(|body| {
3773                fragmented_export_function_body_region(
3774                    node,
3775                    body,
3776                    self.source,
3777                    displaced_namespace.as_ref(),
3778                )
3779            });
3780            // The recovery tuple's first node is the class-like type when the
3781            // parser exposes one, but the synthetic wrapper owns the compound
3782            // statement that contains the truncated class body. Use the
3783            // wrapper body for fragmented-member detection; retain the
3784            // class-node body for the ordinary (non-fragmented) path below.
3785            if let Some(fragmented) = fragmented {
3786                // The lifted sibling no longer sits below the parser-visible
3787                // namespace node. Restore the current parent scope when the
3788                // ordinary work walk reaches that class.
3789                if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
3790                    .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
3791                {
3792                    let mut boundary_scope = scope.clone();
3793                    for sibling in cpp_following_named_siblings(node, self.source) {
3794                        if sibling.start_byte() >= boundary.start_byte() {
3795                            break;
3796                        }
3797                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
3798                            boundary_scope.visible_using_namespaces.push(namespace);
3799                        }
3800                    }
3801                    self.recovered_class_sibling_scopes
3802                        .insert(boundary.id(), boundary_scope);
3803                }
3804                let mut recovered_constructor = None;
3805                let mut recovered_prefix_tree = None;
3806                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
3807                {
3808                    Some(FragmentedExportMembers::Complete(tree)) => {
3809                        if let Some(body) = body
3810                            && let Some(range) =
3811                                cpp_reparsed_synthetic_initializer_constructor_range(
3812                                    tree.root_node(),
3813                                    &name,
3814                                    self.source,
3815                                    body.end_byte(),
3816                                )
3817                        {
3818                            recovered_constructor = Some(range);
3819                            recovered_prefix_tree = Some(tree);
3820                            None
3821                        } else {
3822                            Some(FragmentedExportMembers::Complete(tree))
3823                        }
3824                    }
3825                    outcome => outcome,
3826                };
3827                let mut class_stack = Vec::new();
3828                let class_unit = self.visit_named_class_like_shape(
3829                    class_node,
3830                    name,
3831                    None,
3832                    true,
3833                    Some(fragmented.class_range),
3834                    raw_supertypes,
3835                    scope,
3836                    &mut class_stack,
3837                    ancestry,
3838                );
3839                self.parsed
3840                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
3841                        recovery: fragmented.class_range,
3842                        unit: class_unit.clone(),
3843                    });
3844                let complete = outcome.is_some_and(|outcome| {
3845                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
3846                });
3847                if complete {
3848                    self.consumed_fragment_regions
3849                        .push((node.start_byte(), fragmented.class_range.end_byte));
3850                } else {
3851                    // The reparse can fail when the first constructor or a
3852                    // method body is split into statement-shaped siblings.
3853                    // Keep the recovered class envelope, but do not visit the
3854                    // synthetic wrapper body: its initializer expressions can
3855                    // look like same-named member functions (for example
3856                    // `Token.location(loc)`). Re-own only the original sibling
3857                    // nodes that fall inside the proven class range. Their CST
3858                    // shapes retain the real field/function kinds and ranges.
3859                    let member_scope = ScopeInfo {
3860                        package_name: class_unit.package_name().to_string(),
3861                        module: scope.module.clone(),
3862                        class_unit: Some(class_unit.clone()),
3863                        template_signature: scope.template_signature.clone(),
3864                        template_metadata: None,
3865                        declarations_are_fields: true,
3866                        recovered_specialization_member_scope: false,
3867                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
3868                    };
3869                    for candidate in cpp_following_named_siblings(node, self.source) {
3870                        if candidate.start_byte() >= fragmented.reparse_end {
3871                            break;
3872                        }
3873                        if cpp_fragment_sibling_is_class_member(
3874                            candidate,
3875                            fragmented.reparse_end,
3876                            self.source,
3877                        ) {
3878                            self.recovered_class_sibling_scopes
3879                                .insert(candidate.id(), member_scope.clone());
3880                        }
3881                    }
3882                    if let Some(range) = recovered_constructor
3883                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
3884                    {
3885                        self.visit_recovered_fragment_prefix_members(
3886                            prefix_tree.root_node(),
3887                            range.start,
3888                            &class_unit,
3889                            scope,
3890                            ancestry,
3891                        );
3892                        self.visit_recovered_fragment_constructor(
3893                            range,
3894                            body,
3895                            class_node,
3896                            &class_unit,
3897                            scope,
3898                            ancestry,
3899                        );
3900                    }
3901                }
3902                if let Some(boundary) = displaced_namespace {
3903                    for item in boundary.namespace_items {
3904                        self.recovered_class_sibling_scopes
3905                            .insert(item.id(), scope.clone());
3906                    }
3907                }
3908                stack.extend(class_stack);
3909                return;
3910            }
3911            let mut stack = Vec::new();
3912            let class_unit = self.visit_named_class_like_shape(
3913                class_node,
3914                name,
3915                body,
3916                body.is_some(),
3917                None,
3918                raw_supertypes,
3919                scope,
3920                &mut stack,
3921                ancestry,
3922            );
3923            self.parsed
3924                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3925                    recovery: cpp_declaration_range(node),
3926                    unit: class_unit,
3927                });
3928            // Issue #1524: the bogus `function_definition` body can run past
3929            // the class's true closing brace (the parse ends it with a
3930            // zero-width `MISSING "}"`), swallowing following namespace-scope
3931            // siblings -- they would index as members of the recovered class.
3932            // When the body's text-balanced close lands before the body's own
3933            // end, re-own the swallowed tail with the outer scope instead.
3934            if let Some(body) = body
3935                && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
3936                && class_close < body.end_byte()
3937            {
3938                let split = {
3939                    let mut cursor = body.walk();
3940                    body.named_children(&mut cursor)
3941                        .position(|child| child.start_byte() > class_close)
3942                };
3943                if let Some(split) = split {
3944                    // The seeded work is a single Container over the whole
3945                    // body with the class scope; replace it with the bounded
3946                    // head (class scope) plus the swallowed tail (outer
3947                    // scope). Push tail first so the head drains first.
3948                    let seeded = stack.pop();
3949                    match seeded {
3950                        Some(CppWork::Container(container)) => {
3951                            push_cpp_sibling_range(
3952                                body,
3953                                split,
3954                                usize::MAX,
3955                                scope.clone(),
3956                                &mut stack,
3957                            );
3958                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
3959                        }
3960                        // visit_named_class_like_shape always seeds exactly
3961                        // one Container when a body is present.
3962                        _ => unreachable!("exported-class seed is always one Container"),
3963                    }
3964                }
3965            }
3966            while let Some(work) = stack.pop() {
3967                match work {
3968                    CppWork::Container(container) => {
3969                        push_cpp_container_work(container.node, container.scope, &mut stack);
3970                    }
3971                    CppWork::Siblings(siblings) => {
3972                        advance_cpp_siblings(siblings, self.source, &mut stack);
3973                    }
3974                    CppWork::Node(work) => {
3975                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
3976                    }
3977                }
3978            }
3979            return;
3980        }
3981        let recovered_constraint_constructor =
3982            cpp_recovered_template_macro_constructor(node, self.source);
3983        let declarator = recovered_constraint_constructor
3984            .map(|(declarator, _)| declarator)
3985            .or_else(|| node.child_by_field_name("declarator"));
3986        let Some(declarator) = declarator else {
3987            self.visit_malformed_function_definition_container(node, scope, stack);
3988            return;
3989        };
3990        let Some(function_declarator) = extract_function_declarator(declarator) else {
3991            self.visit_malformed_function_definition_container(node, scope, stack);
3992            return;
3993        };
3994        let function = if let Some((_, callable_name)) =
3995            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
3996        {
3997            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
3998        } else {
3999            extract_function_info(function_declarator, self.source, scope)
4000        };
4001        let Some(mut function) = function else {
4002            self.visit_malformed_function_definition_container(node, scope, stack);
4003            return;
4004        };
4005        if let Some((_, template_parameter)) = recovered_constraint_constructor {
4006            function.signature = format!(
4007                "template <{}>{}",
4008                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
4009                function.signature
4010            );
4011        }
4012        let code_unit = function.code_unit(self.file.clone());
4013        // Keep an earlier same-file prototype as another physical occurrence
4014        // of this callable. `CodeUnit` already identifies the role-neutral
4015        // overload, while ranges and signature metadata describe its
4016        // declaration/definition occurrences.
4017        self.parsed
4018            .add_code_unit(code_unit.clone(), node, self.source, None, None);
4019        let signature = if recovered_constraint_constructor.is_some() {
4020            normalize_cpp_whitespace(node_text(function_declarator, self.source))
4021        } else {
4022            render_cpp_function_display_signature_from_node(
4023                node,
4024                self.source,
4025                scope.template_signature.as_deref(),
4026                true,
4027                ancestry,
4028            )
4029        };
4030        self.parsed.add_signature_with_metadata(
4031            code_unit.clone(),
4032            cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
4033                .with_declaration_only(false)
4034                .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
4035        );
4036        if let Some(parent) = &scope.class_unit {
4037            self.parsed.add_child(parent.clone(), code_unit);
4038        } else if let Some(module) = &scope.module {
4039            self.parsed.add_child(module.clone(), code_unit);
4040        }
4041    }
4042
4043    /// Recover the namespace lost when tree-sitter promotes an export-macro
4044    /// class definition to a root-level `function_definition`.  Only a
4045    /// body-bearing, top-level recovery may borrow a namespace, and only when
4046    /// one earlier namespace-scope forward declaration proves the identity.
4047    fn scope_for_recovered_exported_class<'tree>(
4048        &self,
4049        node: Node<'tree>,
4050        name: &str,
4051        definition_body_present: bool,
4052        scope: &ScopeInfo,
4053        ancestry: &ParentIndex<'tree>,
4054    ) -> ScopeInfo {
4055        if !definition_body_present
4056            || !scope.package_name.is_empty()
4057            || scope.class_unit.is_some()
4058            || !(is_recovered_exported_class_container(node, self.source)
4059                || recover_function_like_export_class_pair(node, self.source).is_some()
4060                || recover_embedded_function_like_export_classes(node, self.source)
4061                    .iter()
4062                    .any(|recovered| recovered.name == name)
4063                || matches!(node.kind(), "declaration" | "field_declaration")
4064                    && recover_exported_class_declaration(node, self.source).is_some()
4065                || matches!(
4066                    node.kind(),
4067                    "class_specifier" | "struct_specifier" | "union_specifier"
4068                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
4069                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
4070                        name_node,
4071                        self.source,
4072                    )))
4073                }) || ancestry.parent(node).is_some_and(|parent| {
4074                    matches!(parent.kind(), "declaration" | "field_declaration")
4075                        && recover_exported_class_declaration(parent, self.source).is_some()
4076                        || is_recovered_exported_class_container(parent, self.source)
4077                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
4078        {
4079            return scope.clone();
4080        }
4081        let Some(package_name) =
4082            unique_earlier_cpp_namespace_forward(node, name, self.source, ancestry).or_else(|| {
4083                lifted_function_like_export_class_namespace(node, self.source, ancestry)
4084            })
4085        else {
4086            return scope.clone();
4087        };
4088
4089        let module = CodeUnit::new_fq(
4090            self.file.clone(),
4091            CodeUnitType::Module,
4092            "",
4093            package_name.clone(),
4094            cpp_namespace_fq(&package_name),
4095        );
4096        let mut recovered = scope.clone();
4097        recovered.package_name = package_name;
4098        recovered.module = Some(module);
4099        recovered
4100    }
4101
4102    fn visit_malformed_function_definition_container<'tree>(
4103        &mut self,
4104        node: Node<'tree>,
4105        scope: &ScopeInfo,
4106        stack: &mut Vec<CppWork<'tree>>,
4107    ) {
4108        let Some(body) = cpp_body_node(node) else {
4109            return;
4110        };
4111        if !cpp_contains_namespace_definition(body) {
4112            return;
4113        }
4114        stack.push(CppWork::Container(CppContainer {
4115            node: body,
4116            scope: scope.clone(),
4117        }));
4118    }
4119
4120    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
4121    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
4122    /// emits for a sentinel-prefixed region, reparse the interior after the
4123    /// sentinel identifier as real C++ items -- confined to the region so
4124    /// every reparsed node keeps its original byte/line position -- and run the
4125    /// ordinary container visitation over the result. Returns `true` when it fired
4126    /// (the caller must then skip normal function processing). Nested sentinel
4127    /// regions recover recursively: the reparsed interior is walked through the
4128    /// same `visit_function_definition` path, so a sentinel inside the region hits
4129    /// this recovery again.
4130    /// Runs `reparse_walk` and records every declaration it mints as a
4131    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
4132    /// `recovery` (issue #1657). A reparsed sentinel region has no single
4133    /// recovered envelope unit: the ordinary visitors mint namespaces,
4134    /// classes, and members directly from the reparsed tree, so the walk's
4135    /// declaration delta is the recovered set. Records are ordered by
4136    /// declaration start byte so the parse product stays deterministic.
4137    fn record_recovered_declarations(
4138        &mut self,
4139        recovery: Range,
4140        reparse_walk: impl FnOnce(&mut Self),
4141    ) {
4142        let before = self.parsed.declarations().clone();
4143        reparse_walk(self);
4144        let mut minted: Vec<CodeUnit> = self
4145            .parsed
4146            .declarations()
4147            .iter()
4148            .filter(|unit| !before.contains(*unit))
4149            .cloned()
4150            .collect();
4151        minted.sort_by_cached_key(|unit| {
4152            let start = self
4153                .parsed
4154                .declaration_ranges(unit)
4155                .first()
4156                .map(|range| range.start_byte)
4157                .unwrap_or(usize::MAX);
4158            (start, unit.fq_name().to_string())
4159        });
4160        for unit in minted {
4161            self.parsed
4162                .record_materialization(MaterializationRecord::RecoveredDeclaration {
4163                    recovery,
4164                    unit,
4165                });
4166        }
4167    }
4168
4169    fn visit_sentinel_macro_region<'tree>(
4170        &mut self,
4171        node: Node<'tree>,
4172        scope: &ScopeInfo,
4173        stack: &mut Vec<CppWork<'tree>>,
4174        ancestry: &ParentIndex<'tree>,
4175    ) -> bool {
4176        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
4177            return true;
4178        }
4179        if let Some((
4180            reparse_start,
4181            class_start,
4182            body_start,
4183            class_close_start,
4184            class_close_end,
4185            class_close_line,
4186        )) = cpp_sentinel_macro_class_region(node, self.source)
4187        {
4188            let Some(class_tree) =
4189                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
4190            else {
4191                return false;
4192            };
4193            let class_root = class_tree.root_node();
4194            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
4195            // A region reparse is its own tree and needs its own parent index.
4196            let class_ancestry = ParentIndex::new(class_root);
4197            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
4198                class_root,
4199                template_node,
4200                self.source,
4201                &class_ancestry,
4202            ) else {
4203                return false;
4204            };
4205            let class_node = reparsed_class.declaration_node;
4206            let name = reparsed_class.name;
4207            let mut class_scope = scope.clone();
4208            if let Some(template_node) = template_node {
4209                class_scope.template_signature =
4210                    cpp_template_signature(template_node, class_node, self.source);
4211                class_scope.template_metadata =
4212                    cpp_template_metadata(template_node, class_node, self.source, ancestry);
4213            }
4214            let Some(body_tree) =
4215                cpp_reparse_region_items(self.source, body_start, class_close_start)
4216            else {
4217                return false;
4218            };
4219            let raw_supertypes = reparsed_class.raw_supertypes;
4220            let class_range = Range {
4221                start_byte: class_start,
4222                end_byte: class_close_end,
4223                start_line: class_node.start_position().row + 1,
4224                end_line: class_close_line,
4225            };
4226            let class_scope = self.scope_for_recovered_exported_class(
4227                class_node,
4228                &name,
4229                true,
4230                &class_scope,
4231                ancestry,
4232            );
4233            let mut class_stack = Vec::new();
4234            let class_unit = self.visit_named_class_like_shape(
4235                class_node,
4236                name,
4237                None,
4238                true,
4239                Some(class_range),
4240                raw_supertypes,
4241                &class_scope,
4242                &mut class_stack,
4243                ancestry,
4244            );
4245            self.parsed
4246                .record_materialization(MaterializationRecord::RecoveredDeclaration {
4247                    recovery: class_range,
4248                    unit: class_unit.clone(),
4249                });
4250            let member_scope = ScopeInfo {
4251                package_name: class_scope.package_name.clone(),
4252                module: class_scope.module.clone(),
4253                class_unit: Some(class_unit),
4254                template_signature: class_scope.template_signature.clone(),
4255                template_metadata: None,
4256                declarations_are_fields: true,
4257                recovered_specialization_member_scope: false,
4258                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
4259            };
4260            self.run_container_work(body_tree.root_node(), member_scope);
4261            // Register only after the padded body reparse: its nodes deliberately
4262            // retain offsets inside the consumed region and must be visited first.
4263            self.consumed_fragment_regions
4264                .push((node.start_byte(), class_close_end));
4265            // An ERROR envelope can hold real sibling declarations after the
4266            // recovered class's close (the suffix-reparse boundary in
4267            // `cpp_sentinel_macro_class_region` partitions, it does not
4268            // consume). Walk the envelope's remaining children normally; the
4269            // consumed region above keeps the recovered class from being
4270            // indexed twice.
4271            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
4272                stack.push(CppWork::Container(CppContainer {
4273                    node,
4274                    scope: scope.clone(),
4275                }));
4276            }
4277            return true;
4278        }
4279        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
4280            return false;
4281        };
4282        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
4283            return false;
4284        };
4285        let root = tree.root_node();
4286        if !cpp_reparsed_items_are_indexable(root, self.source) {
4287            return false;
4288        }
4289        let recovery = cpp_recovery_window(self.source, start, end);
4290        self.record_recovered_declarations(recovery, |visitor| {
4291            visitor.visit_container(
4292                root,
4293                &scope.package_name,
4294                scope.module.clone(),
4295                scope.class_unit.clone(),
4296                scope.template_signature.clone(),
4297                scope.visible_using_namespaces.clone(),
4298            );
4299        });
4300        if end > node.end_byte() {
4301            self.consumed_fragment_regions
4302                .push((node.start_byte(), end));
4303        } else if node.kind() == "ERROR" && node.end_byte() > end {
4304            // The sentinel region ended at the first recovered class-like item
4305            // but the ERROR envelope keeps real sibling declarations after it
4306            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
4307            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
4308            // envelope's remaining children normally; the consumed region
4309            // keeps the reparsed prefix from being indexed twice.
4310            self.consumed_fragment_regions
4311                .push((node.start_byte(), end));
4312            stack.push(CppWork::Container(CppContainer {
4313                node,
4314                scope: scope.clone(),
4315            }));
4316        }
4317        true
4318    }
4319
4320    /// Re-own complete class declarations from the structured Abseil
4321    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
4322    /// its direct CST children already prove both namespace components and the
4323    /// class bodies, so the ordinary class/member visitor can retain ownership
4324    /// and exact source ranges without admitting unrelated callable bodies.
4325    fn visit_nested_namespace_sentinel<'tree>(
4326        &mut self,
4327        node: Node<'tree>,
4328        scope: &ScopeInfo,
4329        ancestry: &ParentIndex<'tree>,
4330    ) -> bool {
4331        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
4332            return false;
4333        };
4334
4335        let mut package_name = scope.package_name.clone();
4336        let mut module = scope.module.clone();
4337        for component in recovered.namespace_components {
4338            package_name = if package_name.is_empty() {
4339                component
4340            } else {
4341                format!("{package_name}::{component}")
4342            };
4343            let namespace_module = CodeUnit::new_fq(
4344                self.file.clone(),
4345                CodeUnitType::Module,
4346                "",
4347                package_name.clone(),
4348                cpp_namespace_fq(&package_name),
4349            );
4350            if !self.parsed.contains_declaration(&namespace_module) {
4351                self.parsed.add_code_unit(
4352                    namespace_module.clone(),
4353                    recovered.function,
4354                    self.source,
4355                    None,
4356                    None,
4357                );
4358            }
4359            module = Some(namespace_module);
4360        }
4361
4362        let recovered_scope = ScopeInfo {
4363            package_name,
4364            module,
4365            class_unit: scope.class_unit.clone(),
4366            template_signature: scope.template_signature.clone(),
4367            template_metadata: scope.template_metadata.clone(),
4368            declarations_are_fields: false,
4369            recovered_specialization_member_scope: false,
4370            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4371        };
4372        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
4373            recovered.function,
4374            recovered.body,
4375            self.source,
4376            ancestry,
4377        ) {
4378            let mut class_scope = recovered_scope.clone();
4379            if let Some(template_node) = fragmented.template_node {
4380                class_scope.template_signature =
4381                    cpp_template_signature(template_node, fragmented.class_node, self.source);
4382                class_scope.template_metadata = cpp_template_metadata(
4383                    template_node,
4384                    fragmented.class_node,
4385                    self.source,
4386                    ancestry,
4387                );
4388            }
4389            if let Some(outcome) = self
4390                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
4391            {
4392                let mut class_stack = Vec::new();
4393                let class_unit = self.visit_named_class_like_shape(
4394                    fragmented.class_node,
4395                    fragmented.name.clone(),
4396                    None,
4397                    true,
4398                    Some(fragmented.fragmented.class_range),
4399                    fragmented.raw_supertypes.clone(),
4400                    &class_scope,
4401                    &mut class_stack,
4402                    ancestry,
4403                );
4404                self.parsed
4405                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
4406                        recovery: fragmented.fragmented.class_range,
4407                        unit: class_unit.clone(),
4408                    });
4409                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
4410                    self.consumed_fragment_regions.push((
4411                        fragmented.consumed_start,
4412                        fragmented.fragmented.class_range.end_byte,
4413                    ));
4414                }
4415            }
4416        }
4417        // The class requirement above is the admission gate; once admitted,
4418        // traverse the whole proven inner namespace body so sibling aliases,
4419        // functions, and variables are not silently discarded.
4420        self.run_container_work(recovered.body, recovered_scope);
4421        true
4422    }
4423
4424    fn visit_declaration<'tree>(
4425        &mut self,
4426        node: Node<'tree>,
4427        scope: &ScopeInfo,
4428        in_class_body: bool,
4429        stack: &mut Vec<CppWork<'tree>>,
4430        ancestry: &ParentIndex<'tree>,
4431    ) {
4432        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4433            return;
4434        }
4435        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
4436            !cpp_active_template_type_parameter(
4437                node,
4438                node_text(declarator, self.source),
4439                self.source,
4440                ancestry,
4441            )
4442        }) {
4443            return;
4444        }
4445        if in_class_body
4446            && let Some(parent) = scope.class_unit.as_ref()
4447            && let Some(call) =
4448                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
4449        {
4450            self.visit_recovered_macro_qualified_constructor_definition(
4451                node, call, scope, ancestry,
4452            );
4453            return;
4454        }
4455        if in_class_body
4456            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
4457        {
4458            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
4459            return;
4460        }
4461        if in_class_body
4462            && let Some(declarators) =
4463                recovered_macro_qualified_field_declarators(node, self.source)
4464        {
4465            for declarator in declarators {
4466                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
4467            }
4468            return;
4469        }
4470        let recovered_alias_names = recovered_type_alias_names(node, self.source);
4471        if !recovered_alias_names.is_empty() {
4472            self.add_type_aliases(node, scope, recovered_alias_names);
4473            return;
4474        }
4475        if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
4476        {
4477            return;
4478        }
4479
4480        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
4481            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
4482                // Issue #938: the members tree-sitter scattered out of the fragmented
4483                // multiple-base export node are reparsed from their true body region
4484                // and re-owned as members of the recovered class, with an explicit
4485                // navigation range spanning to the displaced closing brace.
4486                if let Some(outcome) =
4487                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
4488                {
4489                    let consumed_region = (
4490                        recovered.declaration_node.end_byte(),
4491                        fragmented.class_range.end_byte,
4492                    );
4493                    let code_unit = self.visit_named_class_like_shape(
4494                        recovered.declaration_node,
4495                        recovered.name,
4496                        None,
4497                        true,
4498                        Some(fragmented.class_range),
4499                        recovered.raw_supertypes,
4500                        scope,
4501                        stack,
4502                        ancestry,
4503                    );
4504                    self.parsed.record_materialization(
4505                        MaterializationRecord::RecoveredDeclaration {
4506                            recovery: fragmented.class_range,
4507                            unit: code_unit.clone(),
4508                        },
4509                    );
4510                    let consume_fragment =
4511                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
4512                    // Everything between the fragmented declaration and its displaced
4513                    // closing brace now belongs to the recovered class; keep the
4514                    // ordinary walk from re-indexing those scattered siblings at top
4515                    // level. Register the consumed region only after indexing because
4516                    // the reparsed nodes retain byte offsets inside that same region.
4517                    if consume_fragment {
4518                        self.consumed_fragment_regions.push(consumed_region);
4519                    }
4520                    return;
4521                }
4522            }
4523            let uses_initializer_body = recovered.uses_initializer_body;
4524            let definition_body_present = recovered.body.is_some();
4525            let class_unit = self.visit_named_class_like_shape(
4526                recovered.declaration_node,
4527                recovered.name,
4528                recovered.body,
4529                definition_body_present,
4530                None,
4531                recovered.raw_supertypes,
4532                scope,
4533                stack,
4534                ancestry,
4535            );
4536            self.parsed
4537                .record_materialization(MaterializationRecord::RecoveredDeclaration {
4538                    recovery: cpp_declaration_range(node),
4539                    unit: class_unit,
4540                });
4541            if uses_initializer_body {
4542                return;
4543            }
4544        }
4545
4546        let mut handled_function = false;
4547        let mut handled_declarator = false;
4548        let mut cursor = node.walk();
4549        for child in node.named_children(&mut cursor) {
4550            if matches!(
4551                child.kind(),
4552                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
4553            ) {
4554                // A named class-like definition remains a declaration even when
4555                // the same statement also declares an object, for example
4556                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
4557                // declaration's type and `kind` as its declarator.  Dropping the
4558                // type here loses both its nested owner and every later lexical
4559                // reference to it.  A body is the structured proof that this is
4560                // a definition rather than an elaborated type use such as
4561                // `class Kind value;`.
4562                if cpp_body_node(child).is_some() {
4563                    self.visit_class_like(child, scope, stack, ancestry);
4564                }
4565                continue;
4566            }
4567        }
4568
4569        let mut cursor = node.walk();
4570        for child in node.children_by_field_name("declarator", &mut cursor) {
4571            if crate::structural::is_recovered_designator_init_declarator(child) {
4572                handled_declarator = true;
4573                continue;
4574            }
4575            if let Some(kind) = classify_declarator(child) {
4576                handled_declarator = true;
4577                match kind {
4578                    DeclaratorKind::Function(function_declarator) => {
4579                        handled_function = true;
4580                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
4581                    }
4582                    DeclaratorKind::Variable(variable_declarator) => {
4583                        self.visit_variable_declaration(
4584                            node,
4585                            variable_declarator,
4586                            scope,
4587                            in_class_body,
4588                            ancestry,
4589                        );
4590                    }
4591                }
4592            }
4593        }
4594
4595        if !handled_declarator {
4596            let mut cursor = node.walk();
4597            for child in node.named_children(&mut cursor) {
4598                if crate::structural::is_recovered_designator_init_declarator(child) {
4599                    handled_declarator = true;
4600                    continue;
4601                }
4602                if !is_unfielded_declarator_candidate(child) {
4603                    continue;
4604                }
4605                let Some(kind) = classify_declarator(child) else {
4606                    continue;
4607                };
4608                handled_declarator = true;
4609                match kind {
4610                    DeclaratorKind::Function(function_declarator) => {
4611                        handled_function = true;
4612                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
4613                    }
4614                    DeclaratorKind::Variable(variable_declarator) => {
4615                        self.visit_variable_declaration(
4616                            node,
4617                            variable_declarator,
4618                            scope,
4619                            in_class_body,
4620                            ancestry,
4621                        );
4622                    }
4623                }
4624            }
4625        }
4626
4627        if handled_function {
4628            return;
4629        }
4630
4631        if !handled_declarator {
4632            if in_class_body {
4633                self.visit_class_members_from_declaration(node, scope, ancestry);
4634            } else {
4635                self.visit_global_variables_from_declaration(node, scope, ancestry);
4636            }
4637        }
4638    }
4639
4640    /// Preserve the member structure of an anonymous C aggregate.
4641    ///
4642    /// An anonymous union with no declarator promotes its fields into the
4643    /// containing aggregate. An anonymous struct/union followed by a named
4644    /// declarator, such as `struct { T *ops; } sock`, declares both the field
4645    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
4646    /// the declarator's structured nested identity so a later `value.sock.ops`
4647    /// chain can traverse it without parsing a type spelling (#2407).
4648    fn visit_c_anonymous_aggregate_declaration<'tree>(
4649        &mut self,
4650        node: Node<'tree>,
4651        scope: &ScopeInfo,
4652        in_class_body: bool,
4653        stack: &mut Vec<CppWork<'tree>>,
4654        ancestry: &ParentIndex<'tree>,
4655    ) -> bool {
4656        if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
4657            return false;
4658        }
4659        let Some(aggregate) = node.child_by_field_name("type") else {
4660            return false;
4661        };
4662        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
4663            || aggregate.child_by_field_name("name").is_some()
4664        {
4665            return false;
4666        }
4667        let Some(body) = cpp_body_node(aggregate) else {
4668            return false;
4669        };
4670
4671        let mut cursor = node.walk();
4672        let declarators = node
4673            .children_by_field_name("declarator", &mut cursor)
4674            .filter_map(|declarator| match classify_declarator(declarator) {
4675                Some(DeclaratorKind::Variable(variable)) => Some(variable),
4676                Some(DeclaratorKind::Function(_)) | None => None,
4677            })
4678            .collect::<Vec<_>>();
4679        if declarators.is_empty() {
4680            stack.push(CppWork::Container(CppContainer {
4681                node: body,
4682                scope: scope.clone(),
4683            }));
4684            return true;
4685        }
4686
4687        for declarator in declarators {
4688            let Some(name) = extract_variable_name(declarator, self.source) else {
4689                continue;
4690            };
4691            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
4692            self.visit_named_class_like_shape(
4693                aggregate,
4694                name,
4695                Some(body),
4696                true,
4697                None,
4698                None,
4699                scope,
4700                stack,
4701                ancestry,
4702            );
4703        }
4704        true
4705    }
4706
4707    fn visit_function_declaration<'tree>(
4708        &mut self,
4709        declaration_node: Node<'tree>,
4710        declarator: Node<'tree>,
4711        scope: &ScopeInfo,
4712        ancestry: &ParentIndex<'tree>,
4713    ) {
4714        let Some(function) = extract_function_info(declarator, self.source, scope) else {
4715            return;
4716        };
4717        let code_unit =
4718            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
4719        if self.parsed.contains_declaration(&code_unit) {
4720            self.parsed
4721                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
4722            return;
4723        }
4724        self.parsed
4725            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4726        let signature = render_cpp_function_display_signature_from_node(
4727            declaration_node,
4728            self.source,
4729            scope.template_signature.as_deref(),
4730            false,
4731            ancestry,
4732        );
4733        self.parsed.add_signature_with_metadata(
4734            code_unit.clone(),
4735            cpp_signature_metadata(signature, declarator, self.source, ancestry)
4736                .with_declaration_only(true)
4737                .with_callable_linkage(cpp_callable_linkage(
4738                    declaration_node,
4739                    self.source,
4740                    ancestry,
4741                )),
4742        );
4743        if let Some(parent) = &scope.class_unit {
4744            self.parsed.add_child(parent.clone(), code_unit);
4745        } else if let Some(module) = &scope.module {
4746            self.parsed.add_child(module.clone(), code_unit);
4747        }
4748    }
4749
4750    fn visit_recovered_macro_qualified_function_declaration<'tree>(
4751        &mut self,
4752        declaration_node: Node<'tree>,
4753        call: Node<'tree>,
4754        scope: &ScopeInfo,
4755        ancestry: &ParentIndex<'tree>,
4756    ) {
4757        let Some(parent) = &scope.class_unit else {
4758            return;
4759        };
4760        let Some(name_node) = call.child_by_field_name("function") else {
4761            return;
4762        };
4763        let Some(arguments) = call.child_by_field_name("arguments") else {
4764            return;
4765        };
4766        let Some((signature, parameter_labels)) =
4767            recovered_macro_qualified_function_parameters(arguments, self.source)
4768        else {
4769            return;
4770        };
4771        let arity = parameter_labels.len();
4772        let function = FunctionInfo {
4773            package_name: scope.package_name.clone(),
4774            owner: Some(CppMemberOwner::Unit(parent.clone())),
4775            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
4776            signature,
4777        };
4778        if function.name.is_empty() {
4779            return;
4780        }
4781        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
4782        if self.parsed.contains_declaration(&code_unit) {
4783            self.parsed
4784                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
4785            return;
4786        }
4787        self.parsed
4788            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4789        let signature_label = render_cpp_function_display_signature_from_node(
4790            declaration_node,
4791            self.source,
4792            scope.template_signature.as_deref(),
4793            false,
4794            ancestry,
4795        );
4796        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
4797            .with_declaration_only(true)
4798            .with_callable_arity(CallableArity::exact(arity))
4799            .with_callable_linkage(cpp_callable_linkage(
4800                declaration_node,
4801                self.source,
4802                ancestry,
4803            ));
4804        self.parsed
4805            .add_signature_with_metadata(code_unit.clone(), metadata);
4806        self.parsed.add_child(parent.clone(), code_unit);
4807    }
4808
4809    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
4810        &mut self,
4811        declaration_node: Node<'tree>,
4812        call: Node<'tree>,
4813        scope: &ScopeInfo,
4814        ancestry: &ParentIndex<'tree>,
4815    ) {
4816        let Some(parent) = &scope.class_unit else {
4817            return;
4818        };
4819        let Some(arguments) = call.child_by_field_name("arguments") else {
4820            return;
4821        };
4822        let Some((mut signature, parameter_labels)) =
4823            recovered_macro_qualified_function_parameters(arguments, self.source)
4824        else {
4825            return;
4826        };
4827        if let Some(template_signature) = &scope.template_signature {
4828            signature = format!("{template_signature}{signature}");
4829        }
4830        let arity = parameter_labels.len();
4831        let function = FunctionInfo {
4832            package_name: scope.package_name.clone(),
4833            owner: Some(CppMemberOwner::Unit(parent.clone())),
4834            name: parent.identifier().to_string(),
4835            signature,
4836        };
4837        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
4838        self.parsed
4839            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4840        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
4841        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
4842            .with_declaration_only(false)
4843            .with_callable_arity(CallableArity::exact(arity))
4844            .with_callable_linkage(cpp_callable_linkage(
4845                declaration_node,
4846                self.source,
4847                ancestry,
4848            ));
4849        self.parsed
4850            .add_signature_with_metadata(code_unit.clone(), metadata);
4851        self.parsed.add_child(parent.clone(), code_unit);
4852    }
4853
4854    fn visit_variable_declaration<'tree>(
4855        &mut self,
4856        declaration_node: Node<'tree>,
4857        declarator: Node<'tree>,
4858        scope: &ScopeInfo,
4859        in_class_body: bool,
4860        ancestry: &ParentIndex<'tree>,
4861    ) {
4862        let Some(name) = extract_variable_name(declarator, self.source) else {
4863            return;
4864        };
4865        let parent = if in_class_body {
4866            let Some(parent) = &scope.class_unit else {
4867                return;
4868            };
4869            Some(parent)
4870        } else {
4871            None
4872        };
4873        let short_name = match parent {
4874            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
4875            None => name.clone(),
4876        };
4877        let fq = cpp_leaf_fq(
4878            &scope.package_name,
4879            parent,
4880            &name,
4881            SegmentKind::Member,
4882            SegmentKind::Member,
4883        );
4884        let code_unit = CodeUnit::new_fq(
4885            self.file.clone(),
4886            CodeUnitType::Field,
4887            scope.package_name.clone(),
4888            short_name,
4889            fq,
4890        );
4891        if self.parsed.contains_declaration(&code_unit) {
4892            return;
4893        }
4894        self.parsed
4895            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4896        self.parsed.add_signature_with_metadata(
4897            code_unit.clone(),
4898            SignatureMetadata::new(
4899                render_cpp_field_signature(declaration_node, declarator, self.source),
4900                Vec::new(),
4901            )
4902            .with_cpp_field_linkage(cpp_field_declaration_linkage(
4903                declaration_node,
4904                self.source,
4905                ancestry,
4906            )),
4907        );
4908        if let Some(parent) = &scope.class_unit {
4909            self.parsed.add_child(parent.clone(), code_unit);
4910        } else if let Some(module) = &scope.module {
4911            self.parsed.add_child(module.clone(), code_unit);
4912        }
4913    }
4914
4915    fn visit_class_members_from_declaration<'tree>(
4916        &mut self,
4917        node: Node<'tree>,
4918        scope: &ScopeInfo,
4919        ancestry: &ParentIndex<'tree>,
4920    ) {
4921        let mut cursor = node.walk();
4922        for child in node.named_children(&mut cursor) {
4923            if child.kind() == "init_declarator"
4924                && let Some(inner) = child.child_by_field_name("declarator")
4925            {
4926                self.visit_variable_declaration(node, inner, scope, true, ancestry);
4927            } else if matches!(
4928                child.kind(),
4929                "identifier"
4930                    | "field_identifier"
4931                    | "pointer_declarator"
4932                    | "reference_declarator"
4933                    | "array_declarator"
4934                    | "parenthesized_declarator"
4935            ) {
4936                self.visit_variable_declaration(node, child, scope, true, ancestry);
4937            }
4938        }
4939    }
4940
4941    fn visit_global_variables_from_declaration<'tree>(
4942        &mut self,
4943        node: Node<'tree>,
4944        scope: &ScopeInfo,
4945        ancestry: &ParentIndex<'tree>,
4946    ) {
4947        let mut cursor = node.walk();
4948        for child in node.named_children(&mut cursor) {
4949            if child.kind() == "init_declarator"
4950                && let Some(inner) = child.child_by_field_name("declarator")
4951            {
4952                self.visit_variable_declaration(node, inner, scope, false, ancestry);
4953            } else if matches!(
4954                child.kind(),
4955                "identifier"
4956                    | "field_identifier"
4957                    | "pointer_declarator"
4958                    | "reference_declarator"
4959                    | "array_declarator"
4960                    | "parenthesized_declarator"
4961            ) {
4962                self.visit_variable_declaration(node, child, scope, false, ancestry);
4963            }
4964        }
4965    }
4966
4967    fn visit_include(&mut self, node: Node<'_>) {
4968        let raw = normalize_cpp_whitespace(node_text(node, self.source));
4969        self.parsed.imports.push(ImportInfo {
4970            raw_snippet: raw,
4971            is_wildcard: false,
4972            is_global: false,
4973            identifier: None,
4974            alias: None,
4975            path: None,
4976            binder_span: None,
4977        });
4978    }
4979
4980    fn visit_type_declaration<'tree>(
4981        &mut self,
4982        node: Node<'tree>,
4983        scope: &ScopeInfo,
4984        stack: &mut Vec<CppWork<'tree>>,
4985        ancestry: &ParentIndex<'tree>,
4986    ) {
4987        let type_node = node.child_by_field_name("type");
4988        if let Some(type_node) = type_node
4989            && matches!(
4990                type_node.kind(),
4991                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
4992            )
4993        {
4994            self.visit_class_like(type_node, scope, stack, ancestry);
4995        }
4996
4997        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
4998            let range = Range {
4999                start_byte: node.start_byte(),
5000                end_byte: recovered.end_node.end_byte(),
5001                start_line: node.start_position().row + 1,
5002                end_line: recovered.end_node.end_position().row + 1,
5003            };
5004            let signature = self
5005                .source
5006                .get(range.start_byte..range.end_byte)
5007                .map(normalize_cpp_whitespace)
5008                .unwrap_or_default();
5009            self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
5010            return;
5011        }
5012
5013        let alias_names = match node.kind() {
5014            "alias_declaration" => extract_alias_declaration_name(node, self.source)
5015                .into_iter()
5016                .collect::<Vec<_>>(),
5017            "type_definition" => extract_typedef_alias_names(node, self.source),
5018            _ => Vec::new(),
5019        };
5020        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
5021            (type_node, alias_names.as_slice())
5022            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
5023            && type_node.child_by_field_name("name").is_none()
5024        {
5025            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
5026        } else {
5027            None
5028        };
5029        self.add_type_aliases(node, scope, alias_names);
5030        if let Some((body, alias_name)) = anonymous_aggregate {
5031            // The typedef alias is also the only user-visible identity of an
5032            // anonymous aggregate. Reuse it as the member owner instead of
5033            // minting a second signatureless class with the same FQN. The
5034            // latter makes forward lookup ambiguous when conditional aliases
5035            // coexist and returns duplicate definitions even without guards.
5036            let signature = normalize_cpp_whitespace(node_text(node, self.source));
5037            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
5038            debug_assert!(self.parsed.contains_declaration(&alias_unit));
5039            let mut nested_scope = scope.clone();
5040            nested_scope.class_unit = Some(alias_unit);
5041            nested_scope.template_signature = scope.template_signature.clone();
5042            nested_scope.template_metadata = None;
5043            nested_scope.declarations_are_fields = false;
5044            nested_scope.recovered_specialization_member_scope = false;
5045            stack.push(CppWork::Container(CppContainer {
5046                node: body,
5047                scope: nested_scope,
5048            }));
5049        }
5050    }
5051
5052    fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
5053        let signature = normalize_cpp_whitespace(node_text(node, self.source));
5054        self.record_type_aliases(
5055            node,
5056            scope,
5057            alias_names,
5058            signature,
5059            cpp_declaration_range(node),
5060        );
5061    }
5062
5063    fn record_type_aliases(
5064        &mut self,
5065        node: Node<'_>,
5066        scope: &ScopeInfo,
5067        alias_names: Vec<String>,
5068        signature: String,
5069        range: Range,
5070    ) {
5071        if signature.is_empty() {
5072            return;
5073        }
5074        let type_name = node
5075            .child_by_field_name("type")
5076            .and_then(|type_node| type_node.child_by_field_name("name"))
5077            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
5078        for alias_name in alias_names {
5079            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
5080                continue;
5081            }
5082            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
5083            // Declaration identity does not include the alias signature. Keep
5084            // each physical range so conditional aliases retain their guards.
5085            self.parsed
5086                .add_code_unit_with_range(code_unit.clone(), range, None, None);
5087            self.parsed
5088                .add_signature(code_unit.clone(), signature.clone());
5089            if let Some(metadata) = &scope.template_metadata {
5090                let mut metadata = metadata.clone();
5091                metadata.primary_fq_name = code_unit.fq_name();
5092                self.parsed
5093                    .set_cpp_template_metadata(code_unit.clone(), metadata);
5094            }
5095            if let Some(parent) = &scope.class_unit {
5096                self.parsed.add_child(parent.clone(), code_unit.clone());
5097            } else if let Some(module) = &scope.module {
5098                self.parsed.add_child(module.clone(), code_unit.clone());
5099            }
5100            self.parsed.mark_type_alias(code_unit);
5101        }
5102    }
5103
5104    fn type_alias_unit(
5105        &self,
5106        scope: &ScopeInfo,
5107        alias_name: String,
5108        signature: String,
5109    ) -> CodeUnit {
5110        let short_name = if let Some(parent) = &scope.class_unit {
5111            cpp_join_nested_short(parent.short_name(), &alias_name)
5112        } else {
5113            alias_name.clone()
5114        };
5115        let fq = cpp_leaf_fq(
5116            &scope.package_name,
5117            scope.class_unit.as_ref(),
5118            &alias_name,
5119            SegmentKind::Nested,
5120            SegmentKind::Type,
5121        );
5122        CodeUnit::with_signature_and_fq(
5123            self.file.clone(),
5124            CodeUnitType::Class,
5125            scope.package_name.clone(),
5126            short_name,
5127            Some(signature),
5128            false,
5129            fq,
5130        )
5131    }
5132
5133    fn visit_macro(&mut self, node: Node<'_>) {
5134        let Some(name) = extract_macro_name(node, self.source) else {
5135            return;
5136        };
5137        let signature = node_text(node, self.source).trim_end().to_string();
5138        if signature.is_empty() {
5139            return;
5140        }
5141        let fq = cpp_member_fq("", &name);
5142        // A macro can be undefined and redefined later in the same file. Its
5143        // structured directive is part of the declaration identity so the
5144        // temporal environment can navigate to the definition active at a
5145        // reference instead of collapsing every spelling to the first range.
5146        // The same physical directive parsed through another C/C++ reading
5147        // still produces the same unit and remains deduplicated.
5148        let code_unit = CodeUnit::with_signature_and_fq(
5149            self.file.clone(),
5150            CodeUnitType::Macro,
5151            "",
5152            name,
5153            Some(signature.clone()),
5154            false,
5155            fq,
5156        );
5157        if self.parsed.contains_declaration(&code_unit) {
5158            return;
5159        }
5160        self.parsed
5161            .add_code_unit(code_unit.clone(), node, self.source, None, None);
5162        let name_range = node
5163            .child_by_field_name("name")
5164            .map(cpp_declaration_range)
5165            .unwrap_or_else(|| cpp_declaration_range(node));
5166        self.parsed
5167            .record_materialization(MaterializationRecord::GeneratedDeclaration {
5168                site: cpp_declaration_range(node),
5169                argument: name_range,
5170                kind: GenerationKind::PreprocessorDefinition,
5171                unit: code_unit.clone(),
5172            });
5173        self.parsed.add_signature(code_unit, signature);
5174    }
5175}
5176
5177/// Classify a C++ field while its declaration syntax is already available.
5178///
5179/// The persisted result lets later visibility queries avoid reparsing the
5180/// complete source file only to recover linkage.
5181pub fn cpp_field_declaration_linkage<'tree>(
5182    declaration: Node<'tree>,
5183    source: &str,
5184    ancestry: &ParentIndex<'tree>,
5185) -> CppFieldLinkage {
5186    let mut current = ancestry.parent(declaration);
5187    let mut enclosed_by_class = false;
5188    while let Some(node) = current {
5189        if node.kind() == "namespace_definition"
5190            && node
5191                .child_by_field_name("name")
5192                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
5193        {
5194            return CppFieldLinkage::Internal;
5195        }
5196        if matches!(
5197            node.kind(),
5198            "class_specifier" | "struct_specifier" | "union_specifier"
5199        ) && node
5200            .child_by_field_name("name")
5201            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
5202        {
5203            return CppFieldLinkage::Internal;
5204        }
5205        if matches!(
5206            node.kind(),
5207            "class_specifier" | "struct_specifier" | "union_specifier"
5208        ) {
5209            enclosed_by_class = true;
5210        }
5211        if matches!(node.kind(), "function_definition" | "lambda_expression") {
5212            return CppFieldLinkage::Internal;
5213        }
5214        current = ancestry.parent(node);
5215    }
5216    if enclosed_by_class {
5217        return CppFieldLinkage::External;
5218    }
5219    let mut cursor = declaration.walk();
5220    let mut has_static = false;
5221    let mut has_extern = false;
5222    let mut has_inline = false;
5223    let mut has_const = false;
5224    let mut has_constexpr = false;
5225    for child in declaration.named_children(&mut cursor) {
5226        let text = normalize_cpp_whitespace(node_text(child, source));
5227        match (child.kind(), text.as_str()) {
5228            ("storage_class_specifier", "static") => has_static = true,
5229            ("storage_class_specifier", "extern") => has_extern = true,
5230            ("storage_class_specifier", "inline") => has_inline = true,
5231            ("storage_class_specifier", "constexpr") => has_constexpr = true,
5232            ("type_qualifier", "const") => has_const = true,
5233            ("type_qualifier", "constexpr") => has_constexpr = true,
5234            _ => {}
5235        }
5236    }
5237    if has_static {
5238        CppFieldLinkage::Internal
5239    } else if has_extern || has_inline {
5240        CppFieldLinkage::External
5241    } else if has_const || has_constexpr {
5242        CppFieldLinkage::InternalUnlessExternalPeer
5243    } else {
5244        CppFieldLinkage::External
5245    }
5246}
5247
5248fn cpp_declaration_range(node: Node<'_>) -> Range {
5249    Range {
5250        start_byte: node.start_byte(),
5251        end_byte: node.end_byte(),
5252        start_line: node.start_position().row + 1,
5253        end_line: node.end_position().row + 1,
5254    }
5255}
5256
5257/// A recovery interval as a [`Range`], for materialization records whose
5258/// window is a byte region rather than one parser node (the sentinel-macro
5259/// region reparses, issue #941/#1657).
5260fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
5261    let line_at = |byte: usize| {
5262        source.as_bytes()[..byte]
5263            .iter()
5264            .filter(|&&b| b == b'\n')
5265            .count()
5266            + 1
5267    };
5268    Range {
5269        start_byte,
5270        end_byte,
5271        start_line: line_at(start_byte),
5272        end_line: line_at(end_byte),
5273    }
5274}
5275
5276pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
5277    let mut in_block_comment = false;
5278    for line in source.lines() {
5279        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
5280        let trimmed = stripped.trim();
5281        if !looks_like_quoted_include_line(trimmed) {
5282            continue;
5283        }
5284
5285        let raw = normalize_cpp_whitespace(trimmed);
5286        // The tree-sitter walk already recorded every `#include` it could see;
5287        // this line scan only recovers the ones a parse error hid, so skip a
5288        // snippet that is already an import binding.
5289        if parsed
5290            .imports
5291            .iter()
5292            .any(|import| import.raw_snippet == raw)
5293        {
5294            continue;
5295        }
5296
5297        parsed.imports.push(ImportInfo {
5298            raw_snippet: raw,
5299            is_wildcard: false,
5300            is_global: false,
5301            identifier: None,
5302            alias: None,
5303            path: None,
5304            binder_span: None,
5305        });
5306    }
5307}
5308
5309fn looks_like_quoted_include_line(line: &str) -> bool {
5310    let Some(rest) = line.trim_start().strip_prefix('#') else {
5311        return false;
5312    };
5313    let Some(rest) = rest.trim_start().strip_prefix("include") else {
5314        return false;
5315    };
5316    rest.trim_start().starts_with('"')
5317}
5318
5319fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
5320    let mut raw = Vec::new();
5321    let mut cursor = node.walk();
5322    for child in node.named_children(&mut cursor) {
5323        if child.kind() == "base_class_clause" {
5324            collect_cpp_base_nodes(child, source, &mut raw);
5325        }
5326    }
5327    raw
5328}
5329
5330fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
5331    walk_named_tree_preorder(node, false, |child| match child.kind() {
5332        "type_identifier" | "qualified_identifier" | "template_type" => {
5333            let text = normalize_cpp_whitespace(node_text(child, source));
5334            if !text.is_empty() {
5335                raw.push(text);
5336            }
5337            WalkControl::SkipChildren
5338        }
5339        _ => WalkControl::Continue,
5340    });
5341}
5342
5343fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
5344    let mut out = String::new();
5345    let chars: Vec<char> = line.chars().collect();
5346    let mut index = 0;
5347    let mut in_string = false;
5348    let mut in_char = false;
5349    let mut escape = false;
5350
5351    while index < chars.len() {
5352        let ch = chars[index];
5353        let next = chars.get(index + 1).copied();
5354
5355        if *in_block_comment {
5356            if ch == '*' && next == Some('/') {
5357                *in_block_comment = false;
5358                index += 2;
5359            } else {
5360                index += 1;
5361            }
5362            continue;
5363        }
5364
5365        if in_string {
5366            out.push(ch);
5367            if escape {
5368                escape = false;
5369            } else if ch == '\\' {
5370                escape = true;
5371            } else if ch == '"' {
5372                in_string = false;
5373            }
5374            index += 1;
5375            continue;
5376        }
5377
5378        if in_char {
5379            out.push(ch);
5380            if escape {
5381                escape = false;
5382            } else if ch == '\\' {
5383                escape = true;
5384            } else if ch == '\'' {
5385                in_char = false;
5386            }
5387            index += 1;
5388            continue;
5389        }
5390
5391        if ch == '/' && next == Some('/') {
5392            break;
5393        }
5394        if ch == '/' && next == Some('*') {
5395            *in_block_comment = true;
5396            index += 2;
5397            continue;
5398        }
5399        if ch == '"' {
5400            in_string = true;
5401            out.push(ch);
5402            index += 1;
5403            continue;
5404        }
5405        if ch == '\'' {
5406            in_char = true;
5407            out.push(ch);
5408            index += 1;
5409            continue;
5410        }
5411
5412        out.push(ch);
5413        index += 1;
5414    }
5415
5416    out
5417}
5418
5419#[derive(Clone)]
5420struct FunctionInfo {
5421    package_name: String,
5422    owner: Option<CppMemberOwner>,
5423    name: String,
5424    signature: String,
5425}
5426
5427/// Owner of a member function, kept structured so a literal `$` inside a
5428/// source-level class name never crosses a join/split boundary: the legacy
5429/// `$`-joined owner string was re-split at fq construction, dropping a leading
5430/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
5431/// and tripping the package/short boundary assert -- the #2140 corruption one
5432/// level up (#2362).
5433#[derive(Clone)]
5434enum CppMemberOwner {
5435    /// Source-level owner class chain from a qualified declarator-id, one
5436    /// class name per component (`Outer::Inner::method` -> `["Outer",
5437    /// "Inner"]`); each component may itself contain a literal `$`.
5438    Chain(Vec<String>),
5439    /// The lexically enclosing or recovered class unit; the member fq extends
5440    /// its fq directly instead of re-splitting its `$`-joined short chain.
5441    Unit(CodeUnit),
5442}
5443
5444impl CppMemberOwner {
5445    /// The legacy `$`-joined owner chain used in the member's short name.
5446    fn short_chain(&self) -> String {
5447        match self {
5448            Self::Chain(chain) => chain.join("$"),
5449            Self::Unit(parent) => parent.short_name().to_string(),
5450        }
5451    }
5452}
5453
5454enum DeclaratorKind<'a> {
5455    Function(Node<'a>),
5456    Variable(Node<'a>),
5457}
5458
5459impl FunctionInfo {
5460    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
5461        self.code_unit_with_synthetic(file, false)
5462    }
5463
5464    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
5465        let short_name = match &self.owner {
5466            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
5467            None => self.name.clone(),
5468        };
5469        let fq = match &self.owner {
5470            Some(CppMemberOwner::Chain(chain)) => {
5471                debug_assert!(
5472                    !chain.is_empty(),
5473                    "an empty owner chain is no owner; producers return None instead"
5474                );
5475                let mut fq = FqName::new();
5476                cpp_push_package(&mut fq, &self.package_name);
5477                let mut first = true;
5478                for component in chain {
5479                    let kind = if first {
5480                        SegmentKind::Type
5481                    } else {
5482                        SegmentKind::Nested
5483                    };
5484                    fq.push(cpp_segment(component, kind));
5485                    first = false;
5486                }
5487                fq.push(cpp_segment(&self.name, SegmentKind::Member));
5488                fq
5489            }
5490            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
5491                .fq()
5492                .clone()
5493                .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
5494            // An anonymous parent (empty short chain) contributes no owner
5495            // segment -- the same guard as cpp_join_member_short above.
5496            Some(CppMemberOwner::Unit(_)) | None => {
5497                let mut fq = FqName::new();
5498                cpp_push_package(&mut fq, &self.package_name);
5499                fq.push(cpp_segment(&self.name, SegmentKind::Member));
5500                fq
5501            }
5502        };
5503        CodeUnit::with_signature_and_fq(
5504            file,
5505            CodeUnitType::Function,
5506            self.package_name.clone(),
5507            short_name,
5508            Some(self.signature.clone()),
5509            synthetic,
5510            fq,
5511        )
5512    }
5513}
5514
5515fn extract_function_info(
5516    declarator: Node<'_>,
5517    source: &str,
5518    scope: &ScopeInfo,
5519) -> Option<FunctionInfo> {
5520    let parameters_node = declarator.child_by_field_name("parameters")?;
5521    let declarator_name_node = declarator
5522        .child_by_field_name("declarator")
5523        .or_else(|| parameters_node.prev_named_sibling())?;
5524    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
5525}
5526
5527fn extract_function_info_from_name(
5528    declarator: Node<'_>,
5529    declarator_name_node: Node<'_>,
5530    source: &str,
5531    scope: &ScopeInfo,
5532) -> Option<FunctionInfo> {
5533    let parameters_node = declarator.child_by_field_name("parameters")?;
5534    let parameters_text = cpp_parameter_signature(parameters_node, source);
5535    let recovered_specialization_member = scope
5536        .recovered_specialization_member_scope
5537        .then(|| {
5538            let terminal = declarator_name_node
5539                .child_by_field_name("name")
5540                .unwrap_or(declarator_name_node);
5541            let name = canonical_cpp_qualified_component(terminal, source)?.name;
5542            let owner = scope.class_unit.as_ref()?;
5543            Some((
5544                Some(CppMemberOwner::Unit(owner.clone())),
5545                name,
5546                scope.package_name.clone(),
5547            ))
5548        })
5549        .flatten();
5550    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
5551        parts
5552    } else if let Some(parts) =
5553        split_structured_templated_cpp_name(declarator_name_node, source, scope)
5554    {
5555        parts
5556    } else {
5557        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
5558            declarator_name_node,
5559            source,
5560        )?);
5561        if raw_name.is_empty() {
5562            return None;
5563        }
5564        split_cpp_name(&raw_name, scope)
5565    };
5566    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
5567    let mut signature = if suffix.is_empty() {
5568        parameters_text
5569    } else {
5570        format!("{parameters_text} {suffix}")
5571    };
5572    if let Some(template_signature) = &scope.template_signature {
5573        signature = format!("{template_signature}{signature}");
5574    }
5575
5576    Some(FunctionInfo {
5577        package_name,
5578        owner,
5579        name,
5580        signature,
5581    })
5582}
5583
5584/// Recover the semantic return type and callable name when a declaration macro
5585/// occupies a function definition's `type` field. Tree-sitter either exposes a
5586/// scalar return as the declarator's apparent name and the callable as the sole
5587/// identifier in an `ERROR`, or joins a template return and callable into a
5588/// qualified identifier with a missing `::`. Both shapes retain the complete
5589/// parameter list and body; a concrete separator remains an out-of-line member.
5590fn cpp_macro_displaced_callable_parts<'tree>(
5591    function_declarator: Node<'tree>,
5592    source: &str,
5593    ancestry: &ParentIndex<'tree>,
5594) -> Option<(Node<'tree>, Node<'tree>)> {
5595    let definition = ancestry.parent(function_declarator)?;
5596    if definition.kind() != "function_definition"
5597        || definition.child_by_field_name("declarator") != Some(function_declarator)
5598        || definition
5599            .child_by_field_name("body")
5600            .is_none_or(|body| body.kind() != "compound_statement")
5601    {
5602        return None;
5603    }
5604    let macro_type = definition.child_by_field_name("type")?;
5605    if macro_type.kind() != "type_identifier"
5606        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
5607    {
5608        return None;
5609    }
5610
5611    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
5612    if apparent_return_type.kind() == "qualified_identifier"
5613        && let (Some(return_type), Some(callable_name)) = (
5614            apparent_return_type.child_by_field_name("scope"),
5615            apparent_return_type.child_by_field_name("name"),
5616        )
5617        && return_type.kind() == "template_type"
5618        && matches!(callable_name.kind(), "identifier" | "field_identifier")
5619        && (0..apparent_return_type.child_count())
5620            .filter_map(|index| apparent_return_type.child(index))
5621            .any(|child| child.kind() == "::" && child.is_missing())
5622        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
5623        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
5624    {
5625        return Some((return_type, callable_name));
5626    }
5627    if !matches!(
5628        apparent_return_type.kind(),
5629        "identifier" | "field_identifier" | "type_identifier"
5630    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
5631    {
5632        return None;
5633    }
5634    let parameters = function_declarator.child_by_field_name("parameters")?;
5635    let mut cursor = function_declarator.walk();
5636    let between = function_declarator
5637        .named_children(&mut cursor)
5638        .filter(|child| child.kind() != "comment")
5639        .filter(|child| {
5640            child.start_byte() >= apparent_return_type.end_byte()
5641                && child.end_byte() <= parameters.start_byte()
5642                && !same_node(*child, apparent_return_type)
5643                && !same_node(*child, parameters)
5644        })
5645        .collect::<Vec<_>>();
5646    let [name_error] = between.as_slice() else {
5647        return None;
5648    };
5649    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
5650        return None;
5651    }
5652    let callable_name = name_error.named_child(0)?;
5653    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
5654        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
5655    {
5656        return None;
5657    }
5658    Some((apparent_return_type, callable_name))
5659}
5660
5661/// The part of a `function_declarator` after its parameter list that belongs to
5662/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
5663/// specification, a trailing return type and a trailing requires-clause.
5664///
5665/// The grammar makes each of these a distinct sibling of the `parameters`
5666/// field, so they are read from the tree. Splitting the declarator's text on
5667/// the parameter list instead silently dropped every qualifier whenever the
5668/// parameter list was spelled with whitespace that normalization rewrote - a
5669/// line break or a double space was enough to make a `const` member definition
5670/// a different logical symbol from its declaration (#1827).
5671///
5672/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
5673/// are deliberately excluded. C++ does not make them part of the signature and
5674/// an out-of-line definition never repeats them, so including them would split
5675/// a declaration from its own definition.
5676fn cpp_declarator_identity_suffix(
5677    declarator: Node<'_>,
5678    parameters_node: Node<'_>,
5679    source: &str,
5680) -> String {
5681    let mut cursor = declarator.walk();
5682    let parts = declarator
5683        .named_children(&mut cursor)
5684        .filter(|child| child.start_byte() >= parameters_node.end_byte())
5685        .filter(|child| {
5686            matches!(
5687                child.kind(),
5688                "type_qualifier"
5689                    | "ref_qualifier"
5690                    | "noexcept"
5691                    | "throw_specifier"
5692                    | "trailing_return_type"
5693                    | "requires_clause"
5694            )
5695        })
5696        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
5697        .filter(|text| !text.is_empty())
5698        .collect::<Vec<_>>();
5699    normalize_cpp_qualifier_suffix(&parts.join(" "))
5700}
5701
5702/// The identity suffix of one callable declarator, for a consumer that holds
5703/// the declarator rather than the declaration walk's parts.
5704///
5705/// The persisted signature concatenates the parameter spelling and this suffix,
5706/// so a comparison that must agree on the suffix alone recomputes it here
5707/// instead of splitting the stored string.
5708pub(crate) fn cpp_callable_identity_suffix(
5709    function_declarator: Node<'_>,
5710    source: &str,
5711) -> Option<String> {
5712    let parameters_node = function_declarator.child_by_field_name("parameters")?;
5713    Some(cpp_declarator_identity_suffix(
5714        function_declarator,
5715        parameters_node,
5716        source,
5717    ))
5718}
5719
5720fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
5721    match classify_declarator(node)? {
5722        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
5723        DeclaratorKind::Variable(_) => None,
5724    }
5725}
5726
5727fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
5728    match node.kind() {
5729        "function_declarator" => {
5730            let inner = node
5731                .child_by_field_name("declarator")
5732                .or_else(|| node.child_by_field_name("name"))
5733                .or_else(|| last_named_child(node));
5734            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
5735                Some(DeclaratorKind::Variable(node))
5736            } else {
5737                Some(DeclaratorKind::Function(node))
5738            }
5739        }
5740        "init_declarator"
5741        | "pointer_declarator"
5742        | "reference_declarator"
5743        | "parenthesized_declarator"
5744        | "array_declarator"
5745        | "attributed_declarator"
5746        | "template_function" => node
5747            .child_by_field_name("declarator")
5748            .or_else(|| node.child_by_field_name("name"))
5749            .or_else(|| last_named_child(node))
5750            .and_then(classify_declarator),
5751        "identifier" | "field_identifier" | "qualified_identifier" => {
5752            Some(DeclaratorKind::Variable(node))
5753        }
5754        _ => node
5755            .child_by_field_name("declarator")
5756            .or_else(|| node.child_by_field_name("name"))
5757            .or_else(|| last_named_child(node))
5758            .and_then(classify_declarator),
5759    }
5760}
5761
5762fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
5763    matches!(
5764        node.kind(),
5765        "function_declarator"
5766            | "init_declarator"
5767            | "pointer_declarator"
5768            | "reference_declarator"
5769            | "parenthesized_declarator"
5770            | "array_declarator"
5771            | "attributed_declarator"
5772            | "template_function"
5773            | "identifier"
5774            | "field_identifier"
5775            | "qualified_identifier"
5776    )
5777}
5778
5779fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
5780    let class_like = first_class_like_child(node);
5781    let mut cursor = node.walk();
5782    node.named_children(&mut cursor).any(|child| {
5783        matches!(
5784            child.kind(),
5785            "init_declarator"
5786                | "pointer_declarator"
5787                | "reference_declarator"
5788                | "array_declarator"
5789                | "function_declarator"
5790                | "parenthesized_declarator"
5791                | "attributed_declarator"
5792        ) || matches!(
5793            child.kind(),
5794            "identifier" | "field_identifier" | "qualified_identifier"
5795        ) && class_like.is_none_or(|class_node| {
5796            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
5797        })
5798    })
5799}
5800
5801/// Find the unique namespace-scope forward declaration that precedes a
5802/// recovered export-macro class definition.  Tree-sitter can close a malformed
5803/// class at the enclosing namespace's closing brace, leaving the later class
5804/// definitions as root-level recovered `function_definition` nodes.  A
5805/// preceding `class Name;` in the same namespace is the only structured identity
5806/// signal available in that shape.
5807///
5808/// The search is deliberately conservative: it only accepts a body-less class
5809/// specifier whose declaration has no declarator and is not nested in a function
5810/// or class body.  More than one matching namespace forward declaration is
5811/// ambiguous and returns `None` rather than guessing.
5812fn unique_earlier_cpp_namespace_forward<'tree>(
5813    recovered_node: Node<'tree>,
5814    name: &str,
5815    source: &str,
5816    ancestry: &ParentIndex<'tree>,
5817) -> Option<String> {
5818    let mut root = recovered_node;
5819    while let Some(parent) = ancestry.parent(root) {
5820        root = parent;
5821    }
5822
5823    let mut candidates = Vec::new();
5824    let mut stack = vec![root];
5825    while let Some(current) = stack.pop() {
5826        if current.start_byte() < recovered_node.start_byte()
5827            && matches!(
5828                current.kind(),
5829                "class_specifier" | "struct_specifier" | "union_specifier"
5830            )
5831            && cpp_body_node(current).is_none()
5832            && current.parent().is_some_and(|parent| {
5833                parent.kind() == "declaration_list"
5834                    || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
5835            })
5836            && class_like_name(current, source, ancestry).as_deref() == Some(name)
5837            && cpp_namespace_definition_for_forward(current, ancestry).is_some_and(|namespace| {
5838                // Borrowing is only justified by the parser-recovery shape we
5839                // are repairing: the namespace that held the forward must
5840                // itself contain a syntax error and must have closed before
5841                // the root-level recovered class. A clean, unrelated
5842                // namespace forward is not an identity proof.
5843                namespace.has_error()
5844                    && namespace.end_byte() < recovered_node.start_byte()
5845                    && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
5846            })
5847            && let Some(package_name) = cpp_namespace_name_for_forward(current, source, ancestry)
5848        {
5849            candidates.push(package_name);
5850        }
5851
5852        let mut cursor = current.walk();
5853        for child in current.named_children(&mut cursor) {
5854            if child.start_byte() < recovered_node.start_byte() {
5855                stack.push(child);
5856            }
5857        }
5858    }
5859
5860    if candidates.len() == 1 {
5861        candidates.pop()
5862    } else {
5863        None
5864    }
5865}
5866
5867fn malformed_namespace_is_nearest_recovery_region(
5868    namespace: Node<'_>,
5869    recovered_node: Node<'_>,
5870) -> bool {
5871    let mut root = recovered_node;
5872    while let Some(parent) = root.parent() {
5873        root = parent;
5874    }
5875    let mut cursor = root.walk();
5876    root.named_children(&mut cursor)
5877        .filter(|sibling| {
5878            namespace.end_byte() <= sibling.start_byte()
5879                && sibling.end_byte() <= recovered_node.start_byte()
5880        })
5881        .all(is_malformed_namespace_recovery_trivia)
5882}
5883
5884fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
5885    matches!(node.kind(), "ERROR" | "comment")
5886        || node.kind().starts_with("preproc_")
5887        || node.kind() == "expression_statement" && node.named_child_count() == 0
5888}
5889
5890/// Return the namespace path for a forward class only when the declaration is
5891/// at namespace scope.  A declaration nested in a function/class body may share
5892/// the same namespace ancestor but cannot identify a top-level class definition.
5893fn cpp_namespace_name_for_forward<'tree>(
5894    node: Node<'tree>,
5895    source: &str,
5896    ancestry: &ParentIndex<'tree>,
5897) -> Option<String> {
5898    cpp_namespace_definition_for_forward(node, ancestry)?;
5899    cpp_lexical_namespace_name(node, source, ancestry)
5900}
5901
5902fn cpp_namespace_definition_for_forward<'tree>(
5903    node: Node<'tree>,
5904    ancestry: &ParentIndex<'tree>,
5905) -> Option<Node<'tree>> {
5906    let declaration = ancestry.parent(node)?;
5907    let mut ancestor = ancestry.parent(declaration);
5908    while let Some(current) = ancestor {
5909        if matches!(
5910            current.kind(),
5911            "compound_statement"
5912                | "field_declaration_list"
5913                | "class_specifier"
5914                | "struct_specifier"
5915                | "union_specifier"
5916                | "function_definition"
5917                | "lambda_expression"
5918        ) {
5919            return None;
5920        }
5921        if current.kind() == "namespace_definition" {
5922            return Some(current);
5923        }
5924        ancestor = ancestry.parent(current);
5925    }
5926    None
5927}
5928
5929fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
5930    match node.kind() {
5931        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
5932        "parenthesized_declarator" => node
5933            .child_by_field_name("declarator")
5934            .or_else(|| last_named_child(node))
5935            .is_some_and(is_pointer_wrapper_declarator),
5936        "template_function" => node
5937            .child_by_field_name("name")
5938            .is_some_and(is_function_pointer_like_inner_declarator),
5939        _ => false,
5940    }
5941}
5942
5943fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
5944    match node.kind() {
5945        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
5946        "parenthesized_declarator" => node
5947            .child_by_field_name("declarator")
5948            .or_else(|| last_named_child(node))
5949            .is_some_and(is_pointer_wrapper_declarator),
5950        _ => false,
5951    }
5952}
5953
5954fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
5955    let cleaned = raw_name.trim_start_matches("template ").trim();
5956    // A leading `::` is the explicit-global marker, not an empty owner segment.
5957    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
5958    // erroneous macro envelope swallowing the first identifier of an
5959    // out-of-line `X::X` constructor, chromium #1573); without this strip the
5960    // split below yields owner_parts `[""]`, constructing a unit with an empty
5961    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
5962    let cleaned = cleaned.trim_start_matches("::");
5963    // Parser recovery can preserve two adjacent scope operators around a
5964    // missing component (for example `X::/**/::method` in compiler diagnostic
5965    // fixtures). Empty components are syntax-recovery artifacts, never C++
5966    // owners. Keeping one as the final owner constructed `short_name=".method"`
5967    // and violated the structured package/short boundary during a large LLVM
5968    // workspace build. This is the same legacy-string-to-FqName bridge as the
5969    // ordinary split above; discard only components that the delimiter itself
5970    // proves empty.
5971    let parts: Vec<_> = cleaned
5972        .split("::")
5973        .filter(|component| !component.is_empty())
5974        .collect();
5975    if parts.is_empty() {
5976        return (None, cleaned.to_string(), scope.package_name.clone());
5977    }
5978    if parts.len() > 1 {
5979        let name = parts.last().unwrap_or(&cleaned).to_string();
5980        let owner_parts = &parts[..parts.len() - 1];
5981        if let Some(class_unit) = &scope.class_unit {
5982            // Lexically inside a class body: the owner is that class, whatever
5983            // the declarator re-qualifies it as.
5984            return (
5985                Some(CppMemberOwner::Unit(class_unit.clone())),
5986                name,
5987                scope.package_name.clone(),
5988            );
5989        }
5990        if !scope.package_name.is_empty() {
5991            // Out-of-line member definition written *inside* an enclosing
5992            // `namespace {}` block (scope package is that namespace). Every
5993            // owner segment before the terminal member is a class-nesting step
5994            // -- an out-of-line nested-class member `Outer::Inner::method` in
5995            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
5996            // namespace path: `using namespace` never brings nested-class
5997            // access into unqualified scope, so C++ always writes the full
5998            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
5999            // that redundantly re-states the enclosing namespace it already
6000            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
6001            // strip that re-qualifying prefix (which duplicates a suffix of the
6002            // enclosing package path) before treating what remains as the
6003            // nested-class chain, so the redundant spelling still lands on the
6004            // same `log4cxx.Foo.method` identity as its header declaration.
6005            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
6006            let owner = (!nested.is_empty()).then(|| {
6007                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
6008            });
6009            return (owner, name, scope.package_name.clone());
6010        }
6011        // File scope (no enclosing `namespace {}` block, scope package empty).
6012        let (owner, package_name) = if owner_parts.len() > 1 {
6013            // A multi-segment qualifier at file scope with no enclosing
6014            // namespace: treat all but the last owner segment as the namespace
6015            // path and the last as the owning class (`ns1::ns2::Class::method`
6016            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
6017            // is really a namespace or an outer class cannot be told from the
6018            // declarator text alone here, and no enclosing namespace or
6019            // in-index owner is available at per-file extraction to confirm the
6020            // class reading, so the far-more-common namespace interpretation is
6021            // kept rather than guessed away (the nested-class-at-file-scope and
6022            // using-directive-qualified nested-class shapes remain on this
6023            // behavior; see #1121).
6024            (
6025                Some(CppMemberOwner::Chain(vec![
6026                    owner_parts.last().unwrap_or(&"").to_string(),
6027                ])),
6028                owner_parts[..owner_parts.len() - 1].join("::"),
6029            )
6030        } else {
6031            // A bare `Class::member` qualifier at file scope carries no
6032            // namespace segment of its own. The declarator alone cannot say
6033            // which namespace owns `Class` -- but a `using namespace X;`
6034            // directive already in effect at this point in the file (#1093,
6035            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
6036            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
6037            // is the remaining structural signal for it, so fall back to it
6038            // rather than leaving the definition's package empty while its
6039            // header declaration (parsed inside the `namespace {}` block) keeps
6040            // the real one -- an identity split that made the same member
6041            // unresolvable under its own displayed spelling.
6042            (
6043                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
6044                cpp_using_directive_namespace_for_bare_owner(scope),
6045            )
6046        };
6047        return (owner, name, package_name);
6048    }
6049
6050    let package_name = scope.package_name.clone();
6051    let owner = scope
6052        .class_unit
6053        .as_ref()
6054        .map(|parent| CppMemberOwner::Unit(parent.clone()));
6055    (owner, cleaned.to_string(), package_name)
6056}
6057
6058/// Drop the leading owner segments of an out-of-line member qualifier that
6059/// merely re-state the enclosing namespace the definition already sits in, so
6060/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
6061/// definition may redundantly write `a::b::Outer::Inner::method` (or the
6062/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
6063/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
6064/// noise, not class-nesting steps. Returns the owner segments with the longest
6065/// such re-qualifying prefix removed (possibly all of them, when the qualifier
6066/// names only the enclosing namespace before the terminal member -- a
6067/// re-qualified free function). `package_name` is the enclosing namespace path
6068/// in its stored `::`-joined form; both sides are split on the same delimiter
6069/// the namespace walker joined them with, so this compares namespace *segments*
6070/// rather than scanning text.
6071fn strip_redundant_namespace_prefix<'a>(
6072    owner_parts: &'a [&'a str],
6073    package_name: &str,
6074) -> &'a [&'a str] {
6075    if package_name.is_empty() {
6076        return owner_parts;
6077    }
6078    let package_segments: Vec<&str> = package_name.split("::").collect();
6079    let max_prefix = owner_parts.len().min(package_segments.len());
6080    for prefix_len in (1..=max_prefix).rev() {
6081        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
6082        if &owner_parts[..prefix_len] == package_suffix {
6083            return &owner_parts[prefix_len..];
6084        }
6085    }
6086    owner_parts
6087}
6088
6089/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
6090/// class name at file/namespace scope, from the `using namespace` directives
6091/// visible at this point in the file. Several may be in scope at once (a
6092/// primary `using namespace NS;` alongside deeper conveniences like `using
6093/// namespace NS::helpers;`); since the declarator gives no way to tell which
6094/// one actually declares the owner class, prefer the shallowest (fewest
6095/// `::`-separated segments) as the file's most likely "home" namespace,
6096/// breaking ties by declaration order. Returns an empty string (leaving the
6097/// caller's package unqualified, as before) when no using-namespace directive
6098/// is in scope.
6099fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
6100    scope
6101        .visible_using_namespaces
6102        .iter()
6103        .min_by_key(|namespace| namespace.split("::").count())
6104        .cloned()
6105        .unwrap_or_default()
6106}
6107
6108struct CppQualifiedNameComponent {
6109    name: String,
6110    is_template_id: bool,
6111}
6112
6113/// Canonical nested-class chain for an out-of-line class definition written
6114/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
6115/// component per class (`["Outer", "Inner"]`).
6116///
6117/// The enclosing namespace fixes the namespace/class boundary: after an
6118/// optional redundant spelling of that namespace, every component belongs to
6119/// the class chain. File-scope qualified class names remain untouched because
6120/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
6121///
6122/// The components stay structured (rather than being `$`-joined here) so the
6123/// fq construction can push one Type/Nested segment per class; the `$`-joined
6124/// short-name display form is derived at the call sites that need it.
6125fn qualified_class_name_chain(
6126    class_node: Node<'_>,
6127    source: &str,
6128    scope: &ScopeInfo,
6129) -> Option<Vec<String>> {
6130    if scope.package_name.is_empty() || scope.class_unit.is_some() {
6131        return None;
6132    }
6133    let name = class_node.child_by_field_name("name")?;
6134    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
6135    if explicitly_global
6136        || components.len() < 2
6137        || components.iter().any(|component| component.is_template_id)
6138    {
6139        return None;
6140    }
6141    let names = components
6142        .iter()
6143        .map(|component| component.name.as_str())
6144        .collect::<Vec<_>>();
6145    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
6146    if class_chain.is_empty() {
6147        return None;
6148    }
6149    Some(class_chain.iter().map(|name| name.to_string()).collect())
6150}
6151
6152fn structured_cpp_qualified_components(
6153    qualified_name: Node<'_>,
6154    source: &str,
6155) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
6156    if qualified_name.kind() != "qualified_identifier" {
6157        return None;
6158    }
6159
6160    let mut components = Vec::new();
6161    let mut current = qualified_name;
6162    let mut explicitly_global = false;
6163    loop {
6164        if current.kind() == "qualified_identifier" {
6165            if let Some(component) = current.child_by_field_name("scope") {
6166                components.push(canonical_cpp_qualified_component(component, source)?);
6167            } else if components.is_empty() {
6168                explicitly_global = true;
6169            } else {
6170                return None;
6171            }
6172            current = current.child_by_field_name("name")?;
6173        } else {
6174            components.push(canonical_cpp_qualified_component(current, source)?);
6175            break;
6176        }
6177    }
6178    Some((components, explicitly_global))
6179}
6180
6181fn split_structured_templated_cpp_name(
6182    declarator_name: Node<'_>,
6183    source: &str,
6184    scope: &ScopeInfo,
6185) -> Option<(Option<CppMemberOwner>, String, String)> {
6186    let (mut components, explicitly_global) =
6187        structured_cpp_qualified_components(declarator_name, source)?;
6188
6189    let terminal = components.pop()?;
6190    let owner_start = components
6191        .iter()
6192        .position(|component| component.is_template_id)?;
6193    let explicit_package = components[..owner_start]
6194        .iter()
6195        .map(|component| component.name.as_str())
6196        .collect::<Vec<_>>()
6197        .join("::");
6198    let explicit_package_is_empty = explicit_package.is_empty();
6199    let package_name = match (
6200        explicitly_global,
6201        scope.package_name.is_empty(),
6202        explicit_package_is_empty,
6203    ) {
6204        (true, _, _) => explicit_package,
6205        (false, _, true) => scope.package_name.clone(),
6206        (false, true, false) => explicit_package,
6207        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
6208    };
6209    // Same identity-split fallback as `split_cpp_name` (#1093): a template
6210    // specialization's owner class named with no namespace segment of its own
6211    // (`explicit_package` empty) at file scope (`explicitly_global` false)
6212    // with nothing enclosing (`package_name` still empty) has no structural
6213    // signal for its namespace besides an in-scope `using namespace X;`.
6214    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
6215    {
6216        cpp_using_directive_namespace_for_bare_owner(scope)
6217    } else {
6218        package_name
6219    };
6220    let owner_chain = components[owner_start..]
6221        .iter()
6222        .map(|component| component.name.clone())
6223        .collect::<Vec<_>>();
6224    if owner_chain.is_empty() || terminal.name.is_empty() {
6225        return None;
6226    }
6227
6228    Some((
6229        Some(CppMemberOwner::Chain(owner_chain)),
6230        terminal.name,
6231        package_name,
6232    ))
6233}
6234
6235fn canonical_cpp_qualified_component(
6236    mut component: Node<'_>,
6237    source: &str,
6238) -> Option<CppQualifiedNameComponent> {
6239    let mut is_template_id = false;
6240    loop {
6241        match component.kind() {
6242            "template_type" => {
6243                is_template_id = true;
6244                component = component.child_by_field_name("name")?;
6245            }
6246            "dependent_name" => component = component.named_child(0)?,
6247            "identifier"
6248            | "field_identifier"
6249            | "namespace_identifier"
6250            | "type_identifier"
6251            | "operator_name"
6252            | "destructor_name" => {
6253                let name = normalize_cpp_whitespace(node_text(component, source));
6254                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
6255                    name,
6256                    is_template_id,
6257                });
6258            }
6259            _ => component = component.child_by_field_name("name")?,
6260        }
6261    }
6262}
6263
6264fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
6265    match node.kind() {
6266        "identifier"
6267        | "field_identifier"
6268        | "type_identifier"
6269        | "operator_name"
6270        | "destructor_name"
6271        | "qualified_identifier" => node_text(node, source).to_string(),
6272        "function_declarator"
6273        | "pointer_declarator"
6274        | "reference_declarator"
6275        | "parenthesized_declarator"
6276        | "array_declarator"
6277        | "template_function" => node
6278            .child_by_field_name("declarator")
6279            .or_else(|| node.child_by_field_name("name"))
6280            .or_else(|| last_named_child(node))
6281            .map(|child| extract_declarator_name(child, source))
6282            .unwrap_or_else(|| node_text(node, source).to_string()),
6283        _ => node
6284            .child_by_field_name("name")
6285            .map(|child| extract_declarator_name(child, source))
6286            .unwrap_or_else(|| node_text(node, source).to_string()),
6287    }
6288}
6289
6290/// Extract a callable identity only through declaration-shaped AST nodes.
6291/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
6292/// expose the call's parameter list as a false function declarator; accepting
6293/// arbitrary node text there emitted bogus names such as `.*f`.
6294fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
6295    match node.kind() {
6296        "identifier"
6297        | "field_identifier"
6298        | "type_identifier"
6299        | "operator_name"
6300        | "destructor_name"
6301        | "qualified_identifier" => Some(node_text(node, source).to_string()),
6302        "function_declarator"
6303        | "pointer_declarator"
6304        | "reference_declarator"
6305        | "parenthesized_declarator"
6306        | "array_declarator"
6307        | "template_function" => node
6308            .child_by_field_name("declarator")
6309            .or_else(|| node.child_by_field_name("name"))
6310            .and_then(|child| extract_callable_declarator_name(child, source)),
6311        _ => None,
6312    }
6313}
6314
6315fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
6316    match node.kind() {
6317        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
6318            let name = node_text(node, source).trim().to_string();
6319            (!name.is_empty()).then_some(name)
6320        }
6321        _ => node
6322            .child_by_field_name("declarator")
6323            .or_else(|| node.child_by_field_name("name"))
6324            .or_else(|| last_named_child(node))
6325            .and_then(|child| extract_variable_name(child, source)),
6326    }
6327}
6328
6329fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
6330    let count = node.named_child_count();
6331    if count == 0 {
6332        None
6333    } else {
6334        node.named_child(count - 1)
6335    }
6336}
6337
6338fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
6339    let name_node = node.child_by_field_name("name")?;
6340    let name = normalize_cpp_whitespace(node_text(name_node, source));
6341    (!name.is_empty()).then_some(name)
6342}
6343
6344fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
6345    if node.kind() != "declaration" {
6346        return Vec::new();
6347    }
6348    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
6349        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
6350    }) else {
6351        return Vec::new();
6352    };
6353    let Some(declarator) = node.child_by_field_name("declarator") else {
6354        return Vec::new();
6355    };
6356    if node_text(keyword, source) == "using"
6357        && (declarator.kind() != "init_declarator"
6358            || declarator.child_by_field_name("value").is_none())
6359    {
6360        return Vec::new();
6361    }
6362    if node_text(keyword, source) == "typedef"
6363        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
6364    {
6365        return vec![alias_name];
6366    }
6367    extract_typedef_declarator_name(declarator, source)
6368        .into_iter()
6369        .collect()
6370}
6371
6372fn recovered_typedef_error_alias_name(
6373    declaration: Node<'_>,
6374    declarator: Node<'_>,
6375    source: &str,
6376) -> Option<String> {
6377    // An export macro between `class` and its name can make tree-sitter parse
6378    // the recovered class body as a function body. In that shape,
6379    //
6380    //     typedef spi::Filter BASE_CLASS;
6381    //
6382    // becomes a declaration whose `declarator` is the underlying qualified
6383    // type (`spi::Filter`) and whose actual alias name is displaced into the
6384    // following ERROR node. Do not publish the terminal underlying type
6385    // (`Filter`) as a false class-owned alias.
6386    if declarator.kind() != "qualified_identifier" {
6387        return None;
6388    }
6389    let mut cursor = declaration.walk();
6390    let mut errors = declaration
6391        .named_children(&mut cursor)
6392        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
6393    let error = errors.next()?;
6394    if errors.next().is_some() || error.named_child_count() != 1 {
6395        return None;
6396    }
6397    let name = error.named_child(0)?;
6398    if !matches!(
6399        name.kind(),
6400        "identifier" | "field_identifier" | "type_identifier"
6401    ) {
6402        return None;
6403    }
6404    let name = normalize_cpp_whitespace(node_text(name, source));
6405    (!name.is_empty()).then_some(name)
6406}
6407
6408fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
6409    // A function-like token in the type position can make tree-sitter expose
6410    // its argument as a parenthesized declarator. Do not publish that argument
6411    // as an alias. The macro-specific recovery below handles the proven shape.
6412    if fragmented_parenthesized_typedef_type(node).is_some() {
6413        return Vec::new();
6414    }
6415    let has_function_like_macro_type = node
6416        .child_by_field_name("type")
6417        .filter(|type_node| type_node.kind() == "type_identifier")
6418        .is_some_and(|type_node| {
6419            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
6420        });
6421    let mut names = Vec::new();
6422    let mut cursor = node.walk();
6423    for declarator in node.children_by_field_name("declarator", &mut cursor) {
6424        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
6425            continue;
6426        }
6427        if let Some(name) = extract_typedef_declarator_name(declarator, source)
6428            && !names.contains(&name)
6429        {
6430            names.push(name);
6431        }
6432    }
6433    names
6434}
6435
6436struct RecoveredMacroTypedefAlias<'tree> {
6437    name: String,
6438    end_node: Node<'tree>,
6439}
6440
6441/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
6442/// into an identifier expression statement. The uppercase macro token, missing
6443/// typedef terminator, and complete sibling terminator prove this exact shape.
6444fn recovered_macro_typedef_alias<'tree>(
6445    node: Node<'tree>,
6446    source: &str,
6447) -> Option<RecoveredMacroTypedefAlias<'tree>> {
6448    let type_node = fragmented_parenthesized_typedef_type(node)?;
6449    if type_node.kind() != "type_identifier"
6450        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
6451    {
6452        return None;
6453    }
6454
6455    let end_node = node.next_named_sibling()?;
6456    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
6457        return None;
6458    }
6459    let name_node = end_node.named_child(0)?;
6460    if name_node.kind() != "identifier" {
6461        return None;
6462    }
6463    let has_terminator = (0..end_node.child_count()).any(|index| {
6464        end_node
6465            .child(index)
6466            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
6467    });
6468    if !has_terminator {
6469        return None;
6470    }
6471    let name = normalize_cpp_whitespace(node_text(name_node, source));
6472    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
6473}
6474
6475fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
6476    if node.kind() != "type_definition" {
6477        return None;
6478    }
6479    let mut declarator_cursor = node.walk();
6480    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
6481    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
6482        return None;
6483    }
6484    let has_missing_terminator = (0..node.child_count()).any(|index| {
6485        node.child(index)
6486            .is_some_and(|child| child.kind() == ";" && child.is_missing())
6487    });
6488    if !has_missing_terminator {
6489        return None;
6490    }
6491    node.child_by_field_name("type")
6492}
6493
6494fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
6495    match node.kind() {
6496        "identifier" | "field_identifier" | "type_identifier" => {
6497            let name = normalize_cpp_whitespace(node_text(node, source));
6498            (!name.is_empty()).then_some(name)
6499        }
6500        "qualified_identifier" => node
6501            .child_by_field_name("name")
6502            .and_then(|name| extract_typedef_declarator_name(name, source)),
6503        _ => node
6504            .child_by_field_name("declarator")
6505            .or_else(|| node.child_by_field_name("name"))
6506            .or_else(|| last_named_child(node))
6507            .and_then(|child| extract_typedef_declarator_name(child, source)),
6508    }
6509}
6510
6511fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
6512    let name = node
6513        .child_by_field_name("name")
6514        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
6515        .or_else(|| {
6516            let mut cursor = node.walk();
6517            node.named_children(&mut cursor)
6518                .find(|child| {
6519                    matches!(
6520                        child.kind(),
6521                        "identifier" | "field_identifier" | "type_identifier"
6522                    )
6523                })
6524                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
6525        })?;
6526    (!name.is_empty()).then_some(name)
6527}
6528
6529fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
6530    left.id() == right.id()
6531}
6532
6533fn render_cpp_type_signature(
6534    node: Node<'_>,
6535    source: &str,
6536    template_signature: Option<&str>,
6537) -> String {
6538    let text = normalize_cpp_whitespace(node_text(node, source));
6539    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
6540    let rendered = if head.ends_with(';') {
6541        head.to_string()
6542    } else {
6543        format!("{head} {{")
6544    };
6545    if let Some(template_signature) = template_signature {
6546        format!("template {template_signature} {rendered}")
6547    } else {
6548        rendered
6549    }
6550}
6551
6552fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
6553    if let Some(signature) =
6554        render_recovered_macro_qualified_field_signature(node, declarator, source)
6555    {
6556        return signature;
6557    }
6558    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
6559    let prefix = cpp_declaration_prefix(node, source);
6560    let name = extract_variable_name(declarator, source).unwrap_or_default();
6561    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
6562    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
6563        || (prefix.ends_with('&') && raw_suffix == "&")
6564    {
6565        String::new()
6566    } else {
6567        raw_suffix
6568    };
6569
6570    let mut rendered = if suffix.is_empty() {
6571        format!("{prefix} {name}")
6572    } else if suffix.starts_with('*') || suffix.starts_with('&') {
6573        format!("{prefix}{suffix} {name}")
6574    } else if suffix.starts_with('[') || suffix.starts_with('(') {
6575        format!("{prefix} {name}{suffix}")
6576    } else {
6577        format!("{prefix} {suffix}{name}")
6578    };
6579    rendered = collapse_cpp_whitespace(&rendered);
6580
6581    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
6582        format!("{rendered} = {initializer};")
6583    } else if declaration_text.ends_with(';') {
6584        format!("{rendered};")
6585    } else {
6586        rendered
6587    }
6588}
6589
6590fn render_recovered_macro_qualified_field_signature(
6591    node: Node<'_>,
6592    declarator: Node<'_>,
6593    source: &str,
6594) -> Option<String> {
6595    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
6596    if !recovered
6597        .iter()
6598        .any(|candidate| same_node(*candidate, declarator))
6599    {
6600        return None;
6601    }
6602    let pseudo_declarator = node.child_by_field_name("declarator")?;
6603    let mut cursor = node.walk();
6604    let clause = node
6605        .named_children(&mut cursor)
6606        .find(|child| child.kind() == "bitfield_clause")?;
6607    let mut cursor = clause.walk();
6608    let error = clause
6609        .named_children(&mut cursor)
6610        .find(|child| child.kind() == "ERROR")?;
6611    let qualified_type =
6612        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
6613    let prefix = cpp_declaration_prefix(node, source);
6614    let name = extract_variable_name(declarator, source)?;
6615    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
6616    let mut rendered = if suffix.is_empty() {
6617        format!("{prefix} {qualified_type} {name}")
6618    } else {
6619        format!("{prefix} {qualified_type} {suffix} {name}")
6620    };
6621    rendered = collapse_cpp_whitespace(&rendered);
6622
6623    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
6624        Some(format!(
6625            "{rendered} = {};",
6626            normalize_cpp_whitespace(node_text(initializer, source))
6627        ))
6628    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
6629        Some(format!("{rendered} = {initializer};"))
6630    } else {
6631        Some(format!("{rendered};"))
6632    }
6633}
6634
6635fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
6636    match node.kind() {
6637        "pointer_expression" => {
6638            let operator = node
6639                .child_by_field_name("operator")
6640                .or_else(|| node.child(0))
6641                .map(|operator| node_text(operator, source))
6642                .unwrap_or("*");
6643            let argument = node
6644                .child_by_field_name("argument")
6645                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
6646                .unwrap_or_default();
6647            format!("{operator}{argument}")
6648        }
6649        "unary_expression" => {
6650            let operator = node
6651                .child_by_field_name("operator")
6652                .or_else(|| node.child(0))
6653                .map(|operator| node_text(operator, source))
6654                .unwrap_or_default();
6655            let argument = node
6656                .child_by_field_name("argument")
6657                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
6658                .unwrap_or_default();
6659            format!("{operator}{argument}")
6660        }
6661        "identifier" | "field_identifier" => String::new(),
6662        _ => cpp_declarator_suffix_without_name(node, source),
6663    }
6664}
6665
6666fn recovered_macro_qualified_field_initializer<'tree>(
6667    clause: Node<'tree>,
6668    declarator: Node<'tree>,
6669) -> Option<Node<'tree>> {
6670    let mut stack = vec![clause];
6671    while let Some(current) = stack.pop() {
6672        if current.kind() == "assignment_expression"
6673            && current
6674                .child_by_field_name("left")
6675                .is_some_and(|left| same_node(left, declarator))
6676        {
6677            return current.child_by_field_name("right");
6678        }
6679        let mut cursor = current.walk();
6680        stack.extend(current.named_children(&mut cursor));
6681    }
6682    None
6683}
6684
6685fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
6686    let text = node_text(node, source);
6687    let mut cursor = node.walk();
6688    let first_declarator = node.named_children(&mut cursor).find(|child| {
6689        matches!(
6690            child.kind(),
6691            "init_declarator"
6692                | "identifier"
6693                | "field_identifier"
6694                | "pointer_declarator"
6695                | "reference_declarator"
6696                | "array_declarator"
6697                | "function_declarator"
6698        )
6699    });
6700    let prefix = if let Some(first_declarator) = first_declarator {
6701        let end = first_declarator
6702            .start_byte()
6703            .saturating_sub(node.start_byte());
6704        let mut prefix = text.get(..end).unwrap_or(text).to_string();
6705        let declarator_suffix = match first_declarator.kind() {
6706            "init_declarator" => first_declarator
6707                .child_by_field_name("declarator")
6708                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
6709                .unwrap_or_default(),
6710            _ => cpp_declarator_suffix_without_name(first_declarator, source),
6711        };
6712        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
6713            prefix.push_str(&declarator_suffix);
6714        }
6715        return collapse_cpp_whitespace(&prefix)
6716            .trim_end_matches(',')
6717            .trim_end_matches(';')
6718            .trim()
6719            .to_string();
6720    } else {
6721        text
6722    };
6723    collapse_cpp_whitespace(prefix)
6724        .trim_end_matches(',')
6725        .trim_end_matches(';')
6726        .trim()
6727        .to_string()
6728}
6729
6730fn cpp_preserved_initializer(
6731    declaration_node: Node<'_>,
6732    declarator: Node<'_>,
6733    source: &str,
6734) -> Option<String> {
6735    let name = extract_variable_name(declarator, source)?;
6736    let mut cursor = declaration_node.walk();
6737    for child in declaration_node.named_children(&mut cursor) {
6738        if child.kind() != "init_declarator" {
6739            continue;
6740        }
6741        let Some(inner) = child.child_by_field_name("declarator") else {
6742            continue;
6743        };
6744        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
6745            continue;
6746        }
6747        let value = child.child_by_field_name("value")?;
6748        let kind = value.kind();
6749        if matches!(
6750            kind,
6751            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
6752        ) {
6753            return Some(normalize_cpp_whitespace(node_text(value, source)));
6754        }
6755        break;
6756    }
6757    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
6758    let pattern = format!(
6759        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
6760        regex::escape(&name)
6761    );
6762    Regex::new(&pattern)
6763        .ok()
6764        .and_then(|regex| regex.captures(&declaration_text))
6765        .and_then(|captures| captures.get(1))
6766        .map(|value| value.as_str().to_string())
6767}
6768
6769fn render_cpp_function_display_signature_from_node<'tree>(
6770    node: Node<'tree>,
6771    source: &str,
6772    template_signature: Option<&str>,
6773    has_body: bool,
6774    ancestry: &ParentIndex<'tree>,
6775) -> String {
6776    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
6777    let parent_text = node_text(root, source);
6778    let body_local_start = root
6779        .child_by_field_name("body")
6780        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
6781        .unwrap_or(parent_text.len());
6782    let display = parent_text
6783        .get(..body_local_start)
6784        .unwrap_or(parent_text)
6785        .trim()
6786        .trim();
6787    let display = if let Some(template_signature) = template_signature {
6788        if display.starts_with("template ") {
6789            display.to_string()
6790        } else {
6791            format!("template {template_signature} {display}")
6792        }
6793    } else {
6794        display.to_string()
6795    };
6796    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
6797    if has_body {
6798        format!("{display} {{...}}")
6799    } else {
6800        format!("{display};")
6801    }
6802}
6803
6804fn cpp_template_signature(
6805    template_node: Node<'_>,
6806    declaration_child: Node<'_>,
6807    source: &str,
6808) -> Option<String> {
6809    let text = source
6810        .get(template_node.start_byte()..declaration_child.start_byte())
6811        .unwrap_or("");
6812    let text = normalize_cpp_whitespace(text);
6813    let start = text.find('<')?;
6814    let end = text.rfind('>')?;
6815    if end < start {
6816        return None;
6817    }
6818    Some(text[start..=end].to_string())
6819}
6820
6821struct RecoveredFragmentedPartialSpecialization<'tree> {
6822    declaration_node: Node<'tree>,
6823    name: String,
6824    range: Range,
6825    prefix_members: Vec<Node<'tree>>,
6826    member_siblings: Vec<Node<'tree>>,
6827    following_declarations: Vec<Node<'tree>>,
6828}
6829
6830struct RecoveredFragmentedPreprocessorClass<'tree> {
6831    declaration_node: Node<'tree>,
6832    class_node: Node<'tree>,
6833    body: Node<'tree>,
6834    name: String,
6835    range: Range,
6836    tail_members: Vec<Node<'tree>>,
6837    member_siblings: Vec<Node<'tree>>,
6838}
6839
6840/// Recover a class whose preprocessor-fragmented parse closes at an early
6841/// member body and publishes the remaining in-class declarations as siblings
6842/// of the surrounding alternative. Primary classes are admitted only when an
6843/// earlier branch contains the matching bodyless declaration and the class
6844/// node retains the displaced `#endif`. Partial specializations instead carry
6845/// their identity structurally in the `template_type` name and template
6846/// metadata. Retain the original AST nodes and re-own only the siblings through
6847/// the displaced structural `};` terminator.
6848fn recover_fragmented_preprocessor_class<'tree>(
6849    template_node: Node<'tree>,
6850    source: &str,
6851    ancestry: &ParentIndex<'tree>,
6852) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
6853    let alternative = ancestry.parent(template_node)?;
6854    if alternative.kind() != "preproc_else" {
6855        return None;
6856    }
6857    let conditional = alternative.parent()?;
6858    if conditional.kind() != "preproc_if" {
6859        return None;
6860    }
6861    let declaration_node = template_node
6862        .named_children(&mut template_node.walk())
6863        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
6864    let class_node = declaration_node
6865        .named_children(&mut declaration_node.walk())
6866        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
6867    let body = cpp_body_node(class_node)?;
6868    if class_node.end_byte() >= declaration_node.end_byte() {
6869        return None;
6870    }
6871    let name = class_like_name(class_node, source, ancestry)?;
6872    let is_partial_specialization = class_node
6873        .child_by_field_name("name")
6874        .is_some_and(|class_name| class_name.kind() == "template_type");
6875    if is_partial_specialization {
6876        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
6877        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
6878            return None;
6879        }
6880    } else {
6881        if !class_has_displaced_preprocessor_terminator(class_node) {
6882            return None;
6883        }
6884        let matching_other_branch = conditional
6885            .named_children(&mut conditional.walk())
6886            .take_while(|child| !same_node(*child, alternative))
6887            .filter(|child| child.kind() == "template_declaration")
6888            .filter_map(first_class_like_child)
6889            .any(|candidate| {
6890                cpp_body_node(candidate).is_none()
6891                    && class_like_name(candidate, source, ancestry).as_deref()
6892                        == Some(name.as_str())
6893            });
6894        if !matching_other_branch {
6895            return None;
6896        }
6897    }
6898
6899    let mut tail_members = Vec::new();
6900    let mut saw_class = false;
6901    let mut declaration_cursor = declaration_node.walk();
6902    for child in declaration_node.named_children(&mut declaration_cursor) {
6903        if same_node(child, class_node) {
6904            saw_class = true;
6905        } else if saw_class {
6906            tail_members.push(child);
6907        }
6908    }
6909
6910    let mut member_siblings = Vec::new();
6911    let mut saw_template = false;
6912    let mut terminator = None;
6913    for index in 0..alternative.child_count() {
6914        let Some(child) = alternative.child(index) else {
6915            continue;
6916        };
6917        if same_node(child, template_node) {
6918            saw_template = true;
6919            continue;
6920        }
6921        if !saw_template {
6922            continue;
6923        }
6924        if displaced_fragmented_class_terminator(alternative, index) {
6925            terminator = alternative.child(index + 1);
6926            break;
6927        }
6928        if child.is_named() {
6929            member_siblings.push(child);
6930        }
6931    }
6932    let terminator = terminator?;
6933    Some(RecoveredFragmentedPreprocessorClass {
6934        declaration_node,
6935        class_node,
6936        body,
6937        name,
6938        range: Range {
6939            start_byte: class_node.start_byte(),
6940            end_byte: terminator.end_byte(),
6941            start_line: class_node.start_position().row + 1,
6942            end_line: terminator.end_position().row + 1,
6943        },
6944        tail_members,
6945        member_siblings,
6946    })
6947}
6948
6949fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
6950    (0..class_node.child_count()).any(|index| {
6951        class_node.child(index).is_some_and(|child| {
6952            child.kind() == "ERROR"
6953                && (0..child.child_count()).any(|error_index| {
6954                    child
6955                        .child(error_index)
6956                        .is_some_and(|token| token.kind() == "#endif")
6957                })
6958        })
6959    })
6960}
6961
6962/// The real `#endif` that tree-sitter consumed inside an error subtree.
6963///
6964/// A preprocessor directive inside a malformed array bound can cause later
6965/// declarations to remain children of the conditional. The non-missing token
6966/// still gives the exact structured boundary. Ignore nested conditionals and
6967/// select the last error-owned token. Tree-sitter can pair a later outer
6968/// `#endif` with this conditional, so the direct terminator is not necessarily
6969/// missing.
6970pub fn cpp_displaced_preprocessor_terminator<'tree>(
6971    conditional: Node<'tree>,
6972) -> Option<Node<'tree>> {
6973    if !conditional.has_error() {
6974        return None;
6975    }
6976    let has_concrete_direct_terminator = conditional
6977        .child_count()
6978        .checked_sub(1)
6979        .and_then(|index| conditional.child(index))
6980        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
6981    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
6982        // A structured alternative proves that the direct `#endif` closes
6983        // this family. An error-owned terminator inside either branch belongs
6984        // to a damaged nested conditional, not to this one.
6985        return None;
6986    }
6987    let mut displaced = None;
6988    let mut stack = (0..conditional.child_count())
6989        .filter_map(|index| conditional.child(index))
6990        .map(|child| (child, false))
6991        .collect::<Vec<_>>();
6992    while let Some((node, inside_error)) = stack.pop() {
6993        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
6994            continue;
6995        }
6996        if node.kind() == "#endif" && !node.is_missing() && inside_error {
6997            if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
6998                displaced = Some(node);
6999            }
7000            continue;
7001        }
7002        if node != conditional
7003            && matches!(
7004                node.kind(),
7005                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
7006            )
7007        {
7008            continue;
7009        }
7010        let inside_error = inside_error || node.kind() == "ERROR";
7011        for index in 0..node.child_count() {
7012            if let Some(child) = node.child(index) {
7013                stack.push((child, inside_error));
7014            }
7015        }
7016    }
7017    displaced
7018}
7019
7020/// The effective end of a conditional whose real terminator tree-sitter
7021/// displaced into declaration recovery.
7022///
7023/// Most damaged conditionals retain a concrete `#endif` token below an
7024/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
7025/// boundary. A preprocessor family that selects the middle of a declaration
7026/// can lose the directive tokens entirely. In that shape tree-sitter leaves
7027/// the declaration's `typedef` token as the sole child of the immediately
7028/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
7029/// declarator name inside the conditional's first declaration. The declaration
7030/// end is then the smallest structured boundary that contains the whole split
7031/// declaration.
7032#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7033pub struct CppDisplacedPreprocessorBoundary {
7034    pub end_byte: usize,
7035    pub end_line: usize,
7036}
7037
7038pub fn cpp_displaced_preprocessor_boundary(
7039    conditional: Node<'_>,
7040) -> Option<CppDisplacedPreprocessorBoundary> {
7041    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
7042        return Some(CppDisplacedPreprocessorBoundary {
7043            end_byte: terminator.end_byte(),
7044            end_line: terminator.end_position().row + 1,
7045        });
7046    }
7047    if let Some(declaration) = displaced_split_declaration(conditional) {
7048        return Some(CppDisplacedPreprocessorBoundary {
7049            end_byte: declaration.end_byte(),
7050            end_line: declaration.end_position().row + 1,
7051        });
7052    }
7053    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
7054        return Some(CppDisplacedPreprocessorBoundary {
7055            end_byte: terminator.end_byte(),
7056            end_line: terminator.end_position().row + 1,
7057        });
7058    }
7059    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
7060        return Some(CppDisplacedPreprocessorBoundary {
7061            end_byte: terminator.end_byte(),
7062            end_line: terminator.end_position().row + 1,
7063        });
7064    }
7065    None
7066}
7067
7068/// Recover an outer terminator that tree-sitter assigned to a damaged nested
7069/// conditional. This occurs when a split construct such as `extern "C"`
7070/// consumes the nested `#endif` inside an error node: the nested conditional's
7071/// direct terminator is then the outer conditional's real terminator, while
7072/// the outer node ends with a missing token and absorbs later declarations.
7073fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
7074    if !conditional.has_error()
7075        || conditional.child_by_field_name("alternative").is_some()
7076        || conditional
7077            .child(conditional.child_count().saturating_sub(1))
7078            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
7079    {
7080        return None;
7081    }
7082    let mut recovered = None;
7083    for index in 0..conditional.named_child_count() {
7084        let Some(nested) = conditional.named_child(index) else {
7085            continue;
7086        };
7087        if !matches!(
7088            nested.kind(),
7089            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
7090        ) || nested.child_by_field_name("alternative").is_some()
7091        {
7092            continue;
7093        }
7094        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
7095            continue;
7096        };
7097        if direct.kind() != "#endif" || direct.is_missing() {
7098            continue;
7099        }
7100        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
7101            continue;
7102        };
7103        if displaced.end_byte() >= direct.start_byte() {
7104            continue;
7105        }
7106        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
7107            recovered = Some(direct);
7108        }
7109    }
7110    recovered
7111}
7112
7113fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
7114    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
7115        return None;
7116    }
7117    let mut cursor = conditional.walk();
7118    let declarations = conditional
7119        .named_children(&mut cursor)
7120        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
7121        .collect::<Vec<_>>();
7122    let declaration = *declarations.first()?;
7123    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
7124        return None;
7125    }
7126    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
7127    let mut terminator = None;
7128    let mut stack = (0..declaration.child_count())
7129        .filter_map(|index| declaration.child(index))
7130        .filter(|child| child.start_byte() < declarator_start)
7131        .map(|child| (child, false))
7132        .collect::<Vec<_>>();
7133    while let Some((node, inside_error)) = stack.pop() {
7134        let inside_error = inside_error || node.kind() == "ERROR";
7135        if inside_error && node.kind() == "#endif" && !node.is_missing() {
7136            terminator = Some(node);
7137            continue;
7138        }
7139        for index in 0..node.child_count() {
7140            if let Some(child) = node.child(index)
7141                && child.start_byte() < declarator_start
7142            {
7143                stack.push((child, inside_error));
7144            }
7145        }
7146    }
7147    terminator
7148}
7149
7150fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
7151    if !conditional.has_error()
7152        || conditional.child_by_field_name("alternative").is_some()
7153        || conditional
7154            .prev_named_sibling()
7155            .filter(|sibling| {
7156                sibling.kind() == "ERROR"
7157                    && sibling.child_count() == 1
7158                    && sibling
7159                        .child(0)
7160                        .is_some_and(|child| child.kind() == "typedef")
7161            })
7162            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
7163            .is_none()
7164    {
7165        return None;
7166    }
7167    let mut cursor = conditional.walk();
7168    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
7169    let declaration_index = children
7170        .iter()
7171        .position(|child| child.kind() == "declaration" && child.has_error())?;
7172    let declaration = children[declaration_index];
7173    if !children
7174        .iter()
7175        .skip(declaration_index + 1)
7176        .any(|child| child.end_byte() > declaration.end_byte())
7177    {
7178        return None;
7179    }
7180    let declarator = declaration.child_by_field_name("declarator")?;
7181    let mut error_end = None;
7182    let mut names = Vec::new();
7183    let mut stack = vec![declarator];
7184    while let Some(node) = stack.pop() {
7185        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
7186            error_end =
7187                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
7188            continue;
7189        }
7190        if matches!(node.kind(), "identifier" | "type_identifier") {
7191            names.push(node.start_byte());
7192        }
7193        for index in (0..node.named_child_count()).rev() {
7194            if let Some(child) = node.named_child(index) {
7195                stack.push(child);
7196            }
7197        }
7198    }
7199    let error_end = error_end?;
7200    names
7201        .into_iter()
7202        .any(|start| start >= error_end)
7203        .then_some(declaration)
7204}
7205
7206fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
7207    let Some(error) = parent.child(error_index) else {
7208        return false;
7209    };
7210    if error.kind() != "ERROR"
7211        || error.child_count() != 1
7212        || error.child(0).is_none_or(|child| child.kind() != "}")
7213    {
7214        return false;
7215    }
7216    let Some(semicolon) = parent.child(error_index + 1) else {
7217        return false;
7218    };
7219    semicolon.kind() == "expression_statement"
7220        && semicolon.child_count() == 1
7221        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
7222}
7223
7224/// Locate the real end of a class-like declaration when a macro invocation
7225/// without a source semicolon absorbs the class's `};` into its parsed field.
7226/// The grammar then keeps following namespace declarations as later children
7227/// of the same field list. The direct ERROR-plus-semicolon pair proves the
7228/// boundary structurally; no source-text delimiter scan is needed.
7229fn displaced_macro_class_tail(
7230    declaration_node: Node<'_>,
7231    body: Node<'_>,
7232    source: &str,
7233) -> Option<DisplacedMacroClassTail> {
7234    if !matches!(
7235        declaration_node.kind(),
7236        "class_specifier" | "struct_specifier" | "union_specifier"
7237    ) || body.kind() != "field_declaration_list"
7238    {
7239        return None;
7240    }
7241
7242    let child_count = body.named_child_count();
7243    for index in 0..child_count {
7244        let child = body.named_child(index)?;
7245        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
7246            continue;
7247        };
7248        let split_index = index + 1;
7249        if split_index >= child_count {
7250            return None;
7251        }
7252        let mut cursor = body.walk();
7253        if !body
7254            .named_children(&mut cursor)
7255            .skip(split_index)
7256            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
7257        {
7258            return None;
7259        }
7260        return Some(DisplacedMacroClassTail {
7261            split_index,
7262            class_range: Range {
7263                start_byte: declaration_node.start_byte(),
7264                end_byte: terminator.end_byte(),
7265                start_line: declaration_node.start_position().row + 1,
7266                end_line: terminator.end_position().row + 1,
7267            },
7268        });
7269    }
7270    None
7271}
7272
7273fn displaced_macro_field_terminator<'tree>(
7274    field: Node<'tree>,
7275    source: &str,
7276) -> Option<Node<'tree>> {
7277    if field.kind() != "field_declaration" {
7278        return None;
7279    }
7280    let macro_type = field.child_by_field_name("type")?;
7281    if macro_type.kind() != "type_identifier"
7282        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
7283        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
7284    {
7285        return None;
7286    }
7287    for index in 0..field.child_count() {
7288        let error = field.child(index)?;
7289        if error.kind() != "ERROR"
7290            || error.child_count() != 1
7291            || error.child(0).is_none_or(|child| child.kind() != "}")
7292        {
7293            continue;
7294        }
7295        let semicolon = field.child(index + 1)?;
7296        if semicolon.kind() == ";" {
7297            return Some(semicolon);
7298        }
7299    }
7300    None
7301}
7302
7303fn recover_fragmented_partial_specialization<'tree>(
7304    template_node: Node<'tree>,
7305    declaration_child: Node<'tree>,
7306    source: &str,
7307    ancestry: &ParentIndex<'tree>,
7308) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
7309    if declaration_child.kind() != "function_definition" {
7310        return None;
7311    }
7312    let class_node = declaration_child.child_by_field_name("type")?;
7313    if !matches!(
7314        class_node.kind(),
7315        "class_specifier" | "struct_specifier" | "union_specifier"
7316    ) || !class_node
7317        .child_by_field_name("name")
7318        .and_then(|name| direct_identifier_name(name, source))
7319        .is_some_and(|name| cpp_export_macro_token(&name))
7320    {
7321        return None;
7322    }
7323    let declarator = declaration_child.child_by_field_name("declarator")?;
7324    if declarator.kind() != "template_function" {
7325        return None;
7326    }
7327    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
7328    if metadata.specialization_arguments.is_empty() {
7329        return None;
7330    }
7331    let body = declaration_child.child_by_field_name("body")?;
7332    if body.kind() != "compound_statement" {
7333        return None;
7334    }
7335    let complete_prefix = body.named_child(0).filter(|first| {
7336        first.kind() == "labeled_statement"
7337            && first.has_error()
7338            && first
7339                .named_child(first.named_child_count().saturating_sub(1))
7340                .is_some_and(recovered_declaration_has_class_terminator)
7341    });
7342    let complete_body = complete_prefix.is_some();
7343    let mut prefix_members = Vec::new();
7344    if let Some(prefix) = complete_prefix {
7345        prefix_members.push(prefix);
7346    } else {
7347        let mut body_cursor = body.walk();
7348        for child in body.named_children(&mut body_cursor) {
7349            if !is_structurally_valid_fragmented_class_prefix_member(child) {
7350                break;
7351            }
7352            prefix_members.push(child);
7353        }
7354    }
7355    let containing_declarations = template_node.parent()?;
7356    if !matches!(
7357        containing_declarations.kind(),
7358        "declaration_list" | "compound_statement"
7359    ) {
7360        return None;
7361    }
7362    let mut member_siblings = Vec::new();
7363    let mut following_declarations = Vec::new();
7364    let terminator;
7365    if complete_body {
7366        terminator = complete_prefix?;
7367        let mut cursor = body.walk();
7368        let mut after_prefix = false;
7369        for child in body.named_children(&mut cursor) {
7370            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
7371                after_prefix = true;
7372            } else if after_prefix {
7373                following_declarations.push(child);
7374            }
7375        }
7376    } else {
7377        let mut found_template = false;
7378        let mut cursor = containing_declarations.walk();
7379        let mut class_terminator = None;
7380        for child in containing_declarations.children(&mut cursor) {
7381            if same_node(child, template_node) {
7382                found_template = true;
7383                continue;
7384            }
7385            if found_template && child.kind() == "}" {
7386                class_terminator = Some(child);
7387                break;
7388            }
7389            // A namespace can never be a class member: reaching one before the
7390            // terminator proves the class's true close was swallowed upstream
7391            // and this scan has crossed into the enclosing scope, so the
7392            // recovery cannot be bounded -- continuing re-owns the namespace
7393            // block (and its template specializations) as class members under
7394            // a re-appended package, desyncing the fq boundary (#2306).
7395            if found_template && child.kind() == "namespace_definition" {
7396                return None;
7397            }
7398            if found_template && child.is_named() {
7399                member_siblings.push(child);
7400            }
7401        }
7402        terminator = class_terminator?;
7403    }
7404    let name = format!(
7405        "{}<{}>",
7406        metadata.primary_name,
7407        metadata
7408            .specialization_arguments
7409            .iter()
7410            .map(|argument| argument.text.as_str())
7411            .collect::<Vec<_>>()
7412            .join(", ")
7413    );
7414    Some(RecoveredFragmentedPartialSpecialization {
7415        declaration_node: declaration_child,
7416        name,
7417        range: Range {
7418            start_byte: declaration_child.start_byte(),
7419            end_byte: terminator.end_byte(),
7420            start_line: declaration_child.start_position().row + 1,
7421            end_line: terminator.end_position().row + 1,
7422        },
7423        prefix_members,
7424        member_siblings,
7425        following_declarations,
7426    })
7427}
7428
7429fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
7430    if declaration.kind() != "declaration" {
7431        return false;
7432    }
7433    // With an export macro between `class` and its name, tree-sitter folds a
7434    // complete class body into a function-shaped declaration. The class's own
7435    // `};` remains structurally identifiable as a direct ERROR child holding
7436    // `}`, immediately followed by the declaration's direct `;` child.
7437    (0..declaration.child_count().saturating_sub(1)).any(|index| {
7438        let Some(error) = declaration.child(index) else {
7439            return false;
7440        };
7441        error.kind() == "ERROR"
7442            && error.child_count() == 1
7443            && error.child(0).is_some_and(|child| child.kind() == "}")
7444            && declaration
7445                .child(index + 1)
7446                .is_some_and(|child| child.kind() == ";")
7447    })
7448}
7449
7450fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
7451    if node.has_error() {
7452        return false;
7453    }
7454    match node.kind() {
7455        "declaration"
7456        | "field_declaration"
7457        | "alias_declaration"
7458        | "type_definition"
7459        | "static_assert_declaration" => true,
7460        "labeled_statement" => node
7461            .named_child(node.named_child_count().saturating_sub(1))
7462            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
7463        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
7464            matches!(
7465                child.kind(),
7466                "declaration"
7467                    | "field_declaration"
7468                    | "alias_declaration"
7469                    | "type_definition"
7470                    | "function_definition"
7471            )
7472        }),
7473        _ => false,
7474    }
7475}
7476
7477fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
7478    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
7479        .then(|| node.child_by_field_name("declarator"))
7480        .flatten()
7481        .and_then(|declarator| extract_variable_name(declarator, source))
7482}
7483
7484fn cpp_template_metadata<'tree>(
7485    template_node: Node<'tree>,
7486    declaration_child: Node<'tree>,
7487    source: &str,
7488    ancestry: &ParentIndex<'tree>,
7489) -> Option<CppTemplateMetadata> {
7490    let parameters_node = template_node.child_by_field_name("parameters")?;
7491    let name_node = cpp_templated_class_name_node(declaration_child)?;
7492    let primary_node = match name_node.kind() {
7493        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
7494        _ => name_node,
7495    };
7496    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
7497    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
7498        return None;
7499    }
7500
7501    let mut parameter_nodes = Vec::new();
7502    let mut parameter_names = Vec::new();
7503    let mut cursor = parameters_node.walk();
7504    for parameter in parameters_node.named_children(&mut cursor) {
7505        if !matches!(
7506            parameter.kind(),
7507            "type_parameter_declaration"
7508                | "optional_type_parameter_declaration"
7509                | "variadic_type_parameter_declaration"
7510                | "template_template_parameter_declaration"
7511                | "parameter_declaration"
7512                | "optional_parameter_declaration"
7513                | "variadic_parameter_declaration"
7514        ) {
7515            continue;
7516        }
7517        let index = parameter_nodes.len();
7518        // An unnamed parameter still contributes template arity and kind. Use
7519        // an impossible C++ identifier so positional reconciliation can bind
7520        // it without making source expressions refer to a name that was not
7521        // written.
7522        let name = cpp_template_parameter_name(parameter, source)
7523            .unwrap_or_else(|| format!("<anonymous:{index}>"));
7524        parameter_names.push(name);
7525        parameter_nodes.push(parameter);
7526    }
7527    let parameters = parameter_nodes
7528        .into_iter()
7529        .zip(parameter_names.iter().cloned())
7530        .map(|(parameter, name)| CppTemplateParameterMetadata {
7531            name,
7532            kind: cpp_template_parameter_kind(parameter),
7533            variadic: matches!(
7534                parameter.kind(),
7535                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
7536            ),
7537            default: cpp_template_parameter_default_expression(
7538                parameter,
7539                source,
7540                &parameter_names,
7541                ancestry,
7542            ),
7543        })
7544        .collect();
7545    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
7546        Vec::new()
7547    } else {
7548        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
7549            .unwrap_or_default()
7550    };
7551    let alias_target = (declaration_child.kind() == "alias_declaration")
7552        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
7553        .flatten();
7554    Some(CppTemplateMetadata {
7555        primary_name,
7556        primary_fq_name: String::new(),
7557        parameters,
7558        specialization_arguments,
7559        alias_target,
7560    })
7561}
7562
7563fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
7564    match node.kind() {
7565        "class_specifier" | "struct_specifier" | "union_specifier" => {
7566            node.child_by_field_name("name")
7567        }
7568        "function_definition" => {
7569            let declarator = node.child_by_field_name("declarator")?;
7570            if matches!(declarator.kind(), "identifier" | "template_function") {
7571                Some(declarator)
7572            } else {
7573                None
7574            }
7575        }
7576        "alias_declaration" => node.child_by_field_name("name"),
7577        _ => None,
7578    }
7579}
7580
7581fn cpp_template_alias_target<'tree>(
7582    alias: Node<'tree>,
7583    source: &str,
7584    parameter_names: &[String],
7585    ancestry: &ParentIndex<'tree>,
7586) -> Option<CppTemplateAliasTargetMetadata> {
7587    let mut type_node = alias.child_by_field_name("type")?;
7588    while type_node.kind() == "type_descriptor" {
7589        type_node = type_node.child_by_field_name("type")?;
7590    }
7591    let global = type_node.child_by_field_name("scope").is_none()
7592        && type_node.child(0).is_some_and(|child| child.kind() == "::");
7593    let mut components = Vec::new();
7594    cpp_template_target_components(type_node, source, &mut components)?;
7595    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
7596    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
7597        components,
7598        global,
7599        arguments,
7600    })
7601}
7602
7603fn cpp_template_target_components(
7604    node: Node<'_>,
7605    source: &str,
7606    out: &mut Vec<String>,
7607) -> Option<()> {
7608    match node.kind() {
7609        "identifier" | "namespace_identifier" | "type_identifier" => {
7610            out.push(node_text(node, source).to_string());
7611            Some(())
7612        }
7613        "template_type" => {
7614            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
7615        }
7616        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
7617            if let Some(scope) = node.child_by_field_name("scope") {
7618                cpp_template_target_components(scope, source, out)?;
7619            }
7620            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
7621        }
7622        _ => None,
7623    }
7624}
7625
7626fn cpp_template_argument_expressions<'tree>(
7627    mut node: Node<'tree>,
7628    source: &str,
7629    parameter_names: &[String],
7630    ancestry: &ParentIndex<'tree>,
7631) -> Option<Vec<CppTemplateExpression>> {
7632    loop {
7633        match node.kind() {
7634            "template_type" | "template_function" => {
7635                let arguments = node.child_by_field_name("arguments")?;
7636                let mut cursor = arguments.walk();
7637                return Some(
7638                    arguments
7639                        .named_children(&mut cursor)
7640                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
7641                        .map(|argument| {
7642                            cpp_template_expression(argument, source, parameter_names, ancestry)
7643                        })
7644                        .collect(),
7645                );
7646            }
7647            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
7648                node = node
7649                    .child_by_field_name("name")
7650                    .or_else(|| node.child_by_field_name("type"))?;
7651            }
7652            _ => return None,
7653        }
7654    }
7655}
7656
7657fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
7658    let candidate = node
7659        .child_by_field_name("name")
7660        .or_else(|| node.child_by_field_name("declarator"))
7661        .or_else(|| {
7662            let mut cursor = node.walk();
7663            node.named_children(&mut cursor).find(|child| {
7664                matches!(
7665                    child.kind(),
7666                    "identifier" | "type_identifier" | "field_identifier"
7667                )
7668            })
7669        })?;
7670    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
7671    (!name.is_empty()).then_some(name)
7672}
7673
7674fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
7675    match node.kind() {
7676        "type_parameter_declaration"
7677        | "optional_type_parameter_declaration"
7678        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
7679        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
7680        _ => CppTemplateParameterKind::Value,
7681    }
7682}
7683
7684fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
7685    node.child_by_field_name("default_type")
7686        .or_else(|| node.child_by_field_name("default_value"))
7687}
7688
7689fn cpp_template_parameter_default_expression<'tree>(
7690    parameter: Node<'tree>,
7691    source: &str,
7692    parameter_names: &[String],
7693    ancestry: &ParentIndex<'tree>,
7694) -> Option<CppTemplateExpression> {
7695    let default = cpp_template_parameter_default(parameter)?;
7696    let base = cpp_template_expression(default, source, parameter_names, ancestry);
7697    let Some(pointer_error) = parameter.next_named_sibling() else {
7698        return Some(base);
7699    };
7700    let Some(pointer_declarator) =
7701        recovered_abstract_pointer_declarator_term(pointer_error, source)
7702    else {
7703        return Some(base);
7704    };
7705    Some(CppTemplateExpression {
7706        text: format!(
7707            "{}{}",
7708            base.text,
7709            normalize_cpp_whitespace(node_text(pointer_error, source))
7710        ),
7711        term: CppTemplateTerm::Node {
7712            kind: "type_descriptor".to_string(),
7713            children: vec![base.term, pointer_declarator],
7714        },
7715    })
7716}
7717
7718fn recovered_abstract_pointer_declarator_term(
7719    node: Node<'_>,
7720    source: &str,
7721) -> Option<CppTemplateTerm> {
7722    if node.kind() != "ERROR" || node.child_count() == 0 {
7723        return None;
7724    }
7725    let mut children = Vec::new();
7726    for index in 0..node.child_count() {
7727        let child = node.child(index)?;
7728        if child.kind() != "*" {
7729            return None;
7730        }
7731        children.push(CppTemplateTerm::Atom {
7732            kind: "*".to_string(),
7733            text: normalize_cpp_whitespace(node_text(child, source)),
7734        });
7735    }
7736    Some(CppTemplateTerm::Node {
7737        kind: "abstract_pointer_declarator".to_string(),
7738        children,
7739    })
7740}
7741
7742fn cpp_template_expression<'tree>(
7743    node: Node<'tree>,
7744    source: &str,
7745    parameter_names: &[String],
7746    ancestry: &ParentIndex<'tree>,
7747) -> CppTemplateExpression {
7748    let text = normalize_cpp_whitespace(node_text(node, source));
7749    CppTemplateExpression {
7750        text,
7751        term: cpp_template_term(node, source, parameter_names, ancestry),
7752    }
7753}
7754
7755pub fn cpp_template_term<'tree>(
7756    node: Node<'tree>,
7757    source: &str,
7758    parameter_names: &[String],
7759    ancestry: &ParentIndex<'tree>,
7760) -> CppTemplateTerm {
7761    enum Work<'tree> {
7762        Visit(Node<'tree>),
7763        Build { kind: String, child_count: usize },
7764    }
7765
7766    let mut work = vec![Work::Visit(node)];
7767    let mut terms = Vec::new();
7768    while let Some(next) = work.pop() {
7769        match next {
7770            Work::Visit(current) => {
7771                let text = normalize_cpp_whitespace(node_text(current, source));
7772                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
7773                    terms.push(CppTemplateTerm::Parameter(text));
7774                    continue;
7775                }
7776                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
7777                    let mut cursor = current.walk();
7778                    let named = current
7779                        .named_children(&mut cursor)
7780                        .filter(|child| !child.is_extra() && child.kind() != "comment")
7781                        .collect::<Vec<_>>();
7782                    if let [child] = named.as_slice() {
7783                        work.push(Work::Visit(*child));
7784                        continue;
7785                    }
7786                }
7787                if current.child_count() == 0 {
7788                    terms.push(CppTemplateTerm::Atom {
7789                        kind: if matches!(
7790                            current.kind(),
7791                            "identifier"
7792                                | "type_identifier"
7793                                | "field_identifier"
7794                                | "namespace_identifier"
7795                        ) {
7796                            "identifier".to_string()
7797                        } else {
7798                            current.kind().to_string()
7799                        },
7800                        text,
7801                    });
7802                    continue;
7803                }
7804                let children = (0..current.child_count())
7805                    .filter_map(|index| current.child(index))
7806                    .filter(|child| !child.is_extra() && child.kind() != "comment")
7807                    .collect::<Vec<_>>();
7808                work.push(Work::Build {
7809                    kind: current.kind().to_string(),
7810                    child_count: children.len(),
7811                });
7812                work.extend(children.into_iter().rev().map(Work::Visit));
7813            }
7814            Work::Build { kind, child_count } => {
7815                let children = terms.split_off(terms.len() - child_count);
7816                terms.push(CppTemplateTerm::Node { kind, children });
7817            }
7818        }
7819    }
7820    terms.pop().expect("template term traversal emits one root")
7821}
7822
7823fn cpp_template_term_leaf_is_parameter<'tree>(
7824    node: Node<'tree>,
7825    text: &str,
7826    parameter_names: &[String],
7827    ancestry: &ParentIndex<'tree>,
7828) -> bool {
7829    if !parameter_names.iter().any(|parameter| parameter == text) {
7830        return false;
7831    }
7832    !ancestry.parent(node).is_some_and(|parent| {
7833        matches!(
7834            parent.kind(),
7835            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
7836        ) && parent.child_by_field_name("scope").is_some()
7837            && parent.child_by_field_name("name") == Some(node)
7838    })
7839}
7840
7841fn enclosing_cpp_declaration_node<'tree>(
7842    mut node: Node<'tree>,
7843    ancestry: &ParentIndex<'tree>,
7844) -> Option<Node<'tree>> {
7845    loop {
7846        match node.kind() {
7847            "declaration"
7848            | "function_declaration"
7849            | "field_declaration"
7850            | "function_definition" => return Some(node),
7851            _ => node = ancestry.parent(node)?,
7852        }
7853    }
7854}
7855
7856fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
7857    let mut params = Vec::new();
7858    let mut cursor = parameters_node.walk();
7859    for child in parameters_node.children(&mut cursor) {
7860        match child.kind() {
7861            "parameter_declaration" | "optional_parameter_declaration" => {
7862                params.push(cpp_parameter_type(child, source));
7863            }
7864            "variadic_parameter_declaration" => {
7865                params.push(cpp_parameter_type(child, source));
7866            }
7867            "variadic_parameter" | "..." => params.push("...".to_string()),
7868            _ => {}
7869        }
7870    }
7871
7872    if params.is_empty() {
7873        "()".to_string()
7874    } else {
7875        format!("({})", params.join(", "))
7876    }
7877}
7878
7879fn cpp_signature_metadata<'tree>(
7880    signature: String,
7881    function_declarator: Node<'tree>,
7882    source: &str,
7883    ancestry: &ParentIndex<'tree>,
7884) -> SignatureMetadata {
7885    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
7886    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
7887    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
7888    let return_type_identity =
7889        cpp_callable_return_type_identity(function_declarator, source, ancestry);
7890    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
7891        return enrich(
7892            SignatureMetadata::new(signature, Vec::new())
7893                .with_return_type_text(return_type_text)
7894                .with_return_type_identity(return_type_identity),
7895        );
7896    };
7897    let callable_arity = cpp_callable_arity(parameters_node, source);
7898    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
7899    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
7900    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
7901    let Some(relative_start) = signature
7902        .get(search_from..)
7903        .and_then(|suffix| suffix.find(&parameter_text))
7904    else {
7905        return enrich(
7906            SignatureMetadata::new(signature, Vec::new())
7907                .with_callable_arity(callable_arity)
7908                .with_callable_parameter_types(callable_parameter_types)
7909                .with_return_type_text(return_type_text)
7910                .with_return_type_identity(return_type_identity),
7911        );
7912    };
7913    let parameters_start = search_from + relative_start;
7914    let parameters_end = parameters_start + parameter_text.len();
7915    let mut search_start = parameters_start;
7916    let parameters = cpp_parameter_label_nodes(parameters_node)
7917        .into_iter()
7918        .filter_map(|label_node| {
7919            let label = normalize_cpp_whitespace(node_text(label_node, source));
7920            if label.is_empty() || search_start > parameters_end {
7921                return None;
7922            }
7923            let haystack = signature.get(search_start..parameters_end)?;
7924            let relative_start = haystack.find(&label)?;
7925            let start_byte = search_start + relative_start;
7926            let end_byte = start_byte + label.len();
7927            search_start = end_byte;
7928            Some(ParameterMetadata::new(label, start_byte, end_byte))
7929        })
7930        .collect();
7931    enrich(
7932        SignatureMetadata::new(signature, parameters)
7933            .with_callable_arity(callable_arity)
7934            .with_callable_parameter_types(callable_parameter_types)
7935            .with_return_type_text(return_type_text)
7936            .with_return_type_identity(return_type_identity),
7937    )
7938}
7939
7940fn cpp_callable_is_structural_constructor<'tree>(
7941    function_declarator: Node<'tree>,
7942    source: &str,
7943    ancestry: &ParentIndex<'tree>,
7944) -> bool {
7945    let Some(name_node) = function_declarator
7946        .child_by_field_name("declarator")
7947        .or_else(|| function_declarator.child_by_field_name("name"))
7948        .or_else(|| last_named_child(function_declarator))
7949    else {
7950        return false;
7951    };
7952    let Some(callable_name) = direct_identifier_name(name_node, source) else {
7953        return false;
7954    };
7955
7956    let mut current = ancestry.parent(function_declarator);
7957    while let Some(ancestor) = current {
7958        let owner_name = match ancestor.kind() {
7959            "class_specifier" | "struct_specifier" | "union_specifier" => {
7960                class_like_name(ancestor, source, ancestry)
7961            }
7962            "ERROR" => malformed_class_error_owner_name(ancestor, source),
7963            _ => None,
7964        };
7965        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
7966            return true;
7967        }
7968        current = ancestry.parent(ancestor);
7969    }
7970    false
7971}
7972
7973/// Recover the owner name from the direct grammar shape retained when a later
7974/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
7975/// `ERROR` node:
7976///
7977/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
7978///
7979/// Direct-child checks keep this distinct from an unrelated nested class inside
7980/// a broader error region. The closing brace may be displaced past the error
7981/// node, so the opening body token is the available structural boundary.
7982fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
7983    if node.kind() != "ERROR" {
7984        return None;
7985    }
7986    let keyword = node.child(0)?;
7987    if !matches!(keyword.kind(), "class" | "struct" | "union") {
7988        return None;
7989    }
7990    let name_node = node.child(1)?;
7991    let name = direct_identifier_name(name_node, source)?;
7992    let has_body = (2..node.child_count())
7993        .filter_map(|index| node.child(index))
7994        .any(|child| child.kind() == "{");
7995    has_body.then_some(name)
7996}
7997
7998fn cpp_callable_return_type_identity<'tree>(
7999    function_declarator: Node<'tree>,
8000    source: &str,
8001    ancestry: &ParentIndex<'tree>,
8002) -> Option<StructuredTypeIdentity> {
8003    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
8004        return None;
8005    }
8006    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
8007    if let Some((return_type, _)) =
8008        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
8009    {
8010        return cpp_structured_type_identity(return_type, source, &lexical_scope);
8011    }
8012    let mut cursor = function_declarator.walk();
8013    if let Some(trailing) = function_declarator
8014        .named_children(&mut cursor)
8015        .find(|child| child.kind() == "trailing_return_type")
8016        && let Some(type_descriptor) = trailing.named_child(0)
8017    {
8018        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
8019    }
8020
8021    let mut current = function_declarator;
8022    let mut wrappers = Vec::new();
8023    while let Some(parent) = ancestry.parent(current) {
8024        if matches!(
8025            parent.kind(),
8026            "function_definition" | "declaration" | "field_declaration"
8027        ) {
8028            let type_node = parent.child_by_field_name("type")?;
8029            if cpp_export_macro_token(node_text(type_node, source))
8030                && (0..parent.named_child_count()).any(|index| {
8031                    parent
8032                        .named_child(index)
8033                        .is_some_and(|child| child.kind() == "ERROR")
8034                })
8035            {
8036                return None;
8037            }
8038            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
8039            for wrapper in wrappers.into_iter().rev() {
8040                identity = cpp_wrap_structured_type(identity, wrapper)?;
8041            }
8042            return Some(identity);
8043        }
8044        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
8045            || (matches!(
8046                parent.kind(),
8047                "pointer_declarator"
8048                    | "reference_declarator"
8049                    | "array_declarator"
8050                    | "parenthesized_declarator"
8051            ) && parent.named_child_count() == 1
8052                && parent.named_child(0) == Some(current));
8053        if !wraps_current_declarator {
8054            return None;
8055        }
8056        match parent.kind() {
8057            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
8058            "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
8059            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
8060            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
8061            _ => return None,
8062        }
8063        current = parent;
8064    }
8065    None
8066}
8067
8068fn cpp_structured_type_identity(
8069    node: Node<'_>,
8070    source: &str,
8071    lexical_scope: &[String],
8072) -> Option<StructuredTypeIdentity> {
8073    enum Work<'tree> {
8074        Visit(Node<'tree>),
8075        Wrap(CppStructuredTypeWrapper),
8076        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
8077        BuildGeneric { argument_count: usize },
8078    }
8079
8080    let mut work = vec![Work::Visit(node)];
8081    let mut values = Vec::new();
8082    let mut builder = StructuredTypeIdentityBuilder::default();
8083    while let Some(next) = work.pop() {
8084        match next {
8085            Work::Visit(current) => match current.kind() {
8086                "type_descriptor" => {
8087                    let type_node = current
8088                        .child_by_field_name("type")
8089                        .or_else(|| current.named_child(0))?;
8090                    let mut wrappers = Vec::new();
8091                    let mut cursor = current.walk();
8092                    for child in current.named_children(&mut cursor) {
8093                        if child.id() != type_node.id() {
8094                            wrappers.extend(cpp_structured_declarator_wrappers(child));
8095                        }
8096                    }
8097                    work.push(Work::ApplyWrappers(wrappers));
8098                    work.push(Work::Visit(type_node));
8099                }
8100                "pointer_declarator" | "abstract_pointer_declarator" => {
8101                    let child = current
8102                        .child_by_field_name("declarator")
8103                        .or_else(|| current.named_child(0))?;
8104                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
8105                    work.push(Work::Visit(child));
8106                }
8107                "reference_declarator" => {
8108                    let child = current
8109                        .child_by_field_name("declarator")
8110                        .or_else(|| current.named_child(0))?;
8111                    work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
8112                    work.push(Work::Visit(child));
8113                }
8114                "array_declarator" | "abstract_array_declarator" => {
8115                    let child = current
8116                        .child_by_field_name("declarator")
8117                        .or_else(|| current.named_child(0))?;
8118                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
8119                    work.push(Work::Visit(child));
8120                }
8121                "template_type" => {
8122                    let name_node = current.child_by_field_name("name")?;
8123                    let arguments = current
8124                        .child_by_field_name("arguments")
8125                        .map(|arguments_node| {
8126                            let mut cursor = arguments_node.walk();
8127                            arguments_node
8128                                .named_children(&mut cursor)
8129                                .filter(|child| !child.is_extra() && child.kind() != "comment")
8130                                .collect::<Vec<_>>()
8131                        })
8132                        .unwrap_or_default();
8133                    work.push(Work::BuildGeneric {
8134                        argument_count: arguments.len(),
8135                    });
8136                    work.extend(arguments.into_iter().rev().map(Work::Visit));
8137                    work.push(Work::Visit(name_node));
8138                }
8139                "qualified_identifier"
8140                | "scoped_identifier"
8141                | "scoped_type_identifier"
8142                | "type_identifier"
8143                | "field_identifier"
8144                | "identifier"
8145                | "namespace_identifier"
8146                | "primitive_type" => {
8147                    values.push(builder.named(cpp_structured_named_type(
8148                        current,
8149                        source,
8150                        lexical_scope,
8151                    )?)?);
8152                }
8153                _ => {
8154                    let child = current.child_by_field_name("type").or_else(|| {
8155                        (current.named_child_count() == 1)
8156                            .then(|| current.named_child(0))
8157                            .flatten()
8158                    })?;
8159                    work.push(Work::Visit(child));
8160                }
8161            },
8162            Work::Wrap(wrapper) => {
8163                let root = values.pop()?;
8164                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
8165            }
8166            Work::ApplyWrappers(wrappers) => {
8167                let mut root = values.pop()?;
8168                for wrapper in wrappers.into_iter().rev() {
8169                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
8170                }
8171                values.push(root);
8172            }
8173            Work::BuildGeneric { argument_count } => {
8174                let value_count = argument_count.checked_add(1)?;
8175                let start = values.len().checked_sub(value_count)?;
8176                let mut built = values.split_off(start);
8177                let base = built.remove(0);
8178                values.push(builder.generic(base, built)?);
8179            }
8180        }
8181    }
8182    (values.len() == 1)
8183        .then(|| values.pop())
8184        .flatten()
8185        .and_then(|root| builder.finish(root))
8186}
8187
8188fn cpp_structured_named_type(
8189    node: Node<'_>,
8190    source: &str,
8191    lexical_scope: &[String],
8192) -> Option<StructuredTypeName> {
8193    let path = cpp_structured_type_path(node, source)?;
8194    let absolute = node.child_by_field_name("scope").is_none()
8195        && node.child(0).is_some_and(|child| child.kind() == "::");
8196    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
8197}
8198
8199#[derive(Clone, Copy)]
8200enum CppStructuredTypeWrapper {
8201    Pointer,
8202    Reference,
8203    Array,
8204}
8205
8206fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
8207    let mut wrappers = Vec::new();
8208    let mut current = node;
8209    loop {
8210        match current.kind() {
8211            "pointer_declarator" | "abstract_pointer_declarator" => {
8212                wrappers.push(CppStructuredTypeWrapper::Pointer)
8213            }
8214            "reference_declarator" | "abstract_reference_declarator" => {
8215                wrappers.push(CppStructuredTypeWrapper::Reference)
8216            }
8217            "array_declarator" | "abstract_array_declarator" => {
8218                wrappers.push(CppStructuredTypeWrapper::Array)
8219            }
8220            _ => break,
8221        }
8222        let Some(child) = current
8223            .child_by_field_name("declarator")
8224            .or_else(|| current.named_child(0))
8225        else {
8226            break;
8227        };
8228        current = child;
8229    }
8230    wrappers
8231}
8232
8233fn cpp_wrap_structured_type(
8234    identity: StructuredTypeIdentity,
8235    wrapper: CppStructuredTypeWrapper,
8236) -> Option<StructuredTypeIdentity> {
8237    match wrapper {
8238        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
8239        CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
8240        CppStructuredTypeWrapper::Array => identity.wrap_array(),
8241    }
8242}
8243
8244fn cpp_wrap_structured_type_node(
8245    builder: &mut StructuredTypeIdentityBuilder,
8246    inner: StructuredTypeNodeId,
8247    wrapper: CppStructuredTypeWrapper,
8248) -> Option<StructuredTypeNodeId> {
8249    match wrapper {
8250        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
8251        CppStructuredTypeWrapper::Reference => builder.reference(inner),
8252        CppStructuredTypeWrapper::Array => builder.array(inner),
8253    }
8254}
8255
8256fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
8257    let mut path = Vec::new();
8258    let mut stack = vec![node];
8259    while let Some(current) = stack.pop() {
8260        match current.kind() {
8261            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
8262                let component = node_text(current, source).to_string();
8263                if component.is_empty() {
8264                    return None;
8265                }
8266                path.push(component);
8267            }
8268            "template_type" | "dependent_type" => {
8269                stack.push(current.child_by_field_name("name")?);
8270            }
8271            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
8272                stack.push(current.child_by_field_name("name")?);
8273                if let Some(scope) = current.child_by_field_name("scope") {
8274                    stack.push(scope);
8275                }
8276            }
8277            _ => return None,
8278        }
8279    }
8280    (!path.is_empty()).then_some(path)
8281}
8282
8283fn cpp_callable_lexical_scope<'tree>(
8284    node: Node<'tree>,
8285    source: &str,
8286    ancestry: &ParentIndex<'tree>,
8287) -> Vec<String> {
8288    let mut groups = Vec::new();
8289    let mut current = ancestry.parent(node);
8290    while let Some(parent) = current {
8291        if matches!(
8292            parent.kind(),
8293            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
8294        ) && let Some(name_node) = parent.child_by_field_name("name")
8295            && let Some(components) = cpp_structured_type_path(name_node, source)
8296            && !components.is_empty()
8297        {
8298            groups.push(components);
8299        }
8300        current = ancestry.parent(parent);
8301    }
8302    groups.reverse();
8303    groups.into_iter().flatten().collect()
8304}
8305
8306fn cpp_callable_dispatch_extensibility<'tree>(
8307    function_declarator: Node<'tree>,
8308    ancestry: &ParentIndex<'tree>,
8309) -> DispatchExtensibility {
8310    let mut declaration = None;
8311    let mut current = Some(function_declarator);
8312    while let Some(node) = current {
8313        match node.kind() {
8314            "template_declaration"
8315            | "preproc_if"
8316            | "preproc_ifdef"
8317            | "preproc_else"
8318            | "preproc_elif"
8319            | "preproc_call"
8320            | "ERROR" => return DispatchExtensibility::Open,
8321            "declaration" | "field_declaration" | "function_definition" => {
8322                declaration.get_or_insert(node);
8323            }
8324            "translation_unit" => break,
8325            _ => {}
8326        }
8327        current = ancestry.parent(node);
8328    }
8329    let Some(declaration) = declaration else {
8330        return DispatchExtensibility::Open;
8331    };
8332
8333    let mut saw_virtual_boundary = false;
8334    let mut stack = vec![declaration];
8335    while let Some(node) = stack.pop() {
8336        match node.kind() {
8337            "compound_statement" | "field_declaration_list" => continue,
8338            "final" | "final_specifier" => return DispatchExtensibility::Closed,
8339            "virtual"
8340            | "override"
8341            | "virtual_specifier"
8342            | "pure_virtual_clause"
8343            | "template_parameter_list"
8344            | "template_method"
8345            | "template_function"
8346            | "ERROR" => saw_virtual_boundary = true,
8347            _ => {}
8348        }
8349        let mut cursor = node.walk();
8350        stack.extend(node.children(&mut cursor));
8351    }
8352
8353    if saw_virtual_boundary {
8354        DispatchExtensibility::Open
8355    } else {
8356        DispatchExtensibility::Closed
8357    }
8358}
8359
8360fn cpp_callable_linkage<'tree>(
8361    declaration: Node<'tree>,
8362    source: &str,
8363    ancestry: &ParentIndex<'tree>,
8364) -> CallableLinkage {
8365    let mut enclosed_by_class = false;
8366    let mut current = ancestry.parent(declaration);
8367    while let Some(node) = current {
8368        if node.kind() == "namespace_definition"
8369            && node
8370                .child_by_field_name("name")
8371                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8372        {
8373            return CallableLinkage::Internal;
8374        }
8375        if matches!(
8376            node.kind(),
8377            "class_specifier" | "struct_specifier" | "union_specifier"
8378        ) {
8379            if node
8380                .child_by_field_name("name")
8381                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8382            {
8383                return CallableLinkage::Internal;
8384            }
8385            enclosed_by_class = true;
8386        }
8387        if matches!(node.kind(), "function_definition" | "lambda_expression") {
8388            return CallableLinkage::Internal;
8389        }
8390        current = ancestry.parent(node);
8391    }
8392
8393    if enclosed_by_class {
8394        return CallableLinkage::External;
8395    }
8396
8397    let mut cursor = declaration.walk();
8398    if declaration.named_children(&mut cursor).any(|child| {
8399        child.kind() == "storage_class_specifier"
8400            && normalize_cpp_whitespace(node_text(child, source)) == "static"
8401    }) {
8402        CallableLinkage::Internal
8403    } else {
8404        CallableLinkage::External
8405    }
8406}
8407
8408fn cpp_callable_return_type_text<'tree>(
8409    function_declarator: Node<'tree>,
8410    source: &str,
8411    ancestry: &ParentIndex<'tree>,
8412) -> Option<String> {
8413    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
8414        return None;
8415    }
8416    if let Some((return_type, _)) =
8417        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
8418    {
8419        let text = normalize_cpp_whitespace(node_text(return_type, source));
8420        return (!text.is_empty()).then_some(text);
8421    }
8422    let mut cursor = function_declarator.walk();
8423    if let Some(trailing) = function_declarator
8424        .named_children(&mut cursor)
8425        .find(|child| child.kind() == "trailing_return_type")
8426        && let Some(type_descriptor) = trailing.named_child(0)
8427    {
8428        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
8429        if !text.is_empty() {
8430            return Some(text);
8431        }
8432    }
8433
8434    let mut current = function_declarator;
8435    let mut indirection = String::new();
8436    while let Some(parent) = ancestry.parent(current) {
8437        if matches!(
8438            parent.kind(),
8439            "function_definition" | "declaration" | "field_declaration"
8440        ) {
8441            let type_node = parent.child_by_field_name("type")?;
8442            if cpp_export_macro_token(node_text(type_node, source))
8443                && (0..parent.named_child_count()).any(|index| {
8444                    parent
8445                        .named_child(index)
8446                        .is_some_and(|child| child.kind() == "ERROR")
8447                })
8448            {
8449                // Export/decorator macros commonly occupy the grammar's `type`
8450                // field and leave the semantic return type in an ERROR sibling.
8451                // Do not persist the macro token as a return type. The malformed
8452                // declaration does not carry enough structured evidence here.
8453                return None;
8454            }
8455            let base = normalize_cpp_whitespace(node_text(type_node, source));
8456            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
8457        }
8458        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
8459            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
8460                && parent.named_child_count() == 1
8461                && parent.named_child(0) == Some(current));
8462        if wraps_current_declarator {
8463            match parent.kind() {
8464                "pointer_declarator" => indirection.push('*'),
8465                "reference_declarator" => {
8466                    let reference = parent
8467                        .children(&mut parent.walk())
8468                        .find(|child| !child.is_named())
8469                        .map(|child| node_text(child, source))
8470                        .unwrap_or("&");
8471                    indirection.push_str(reference);
8472                }
8473                "init_declarator" | "parenthesized_declarator" => {}
8474                _ => return None,
8475            }
8476            current = parent;
8477            continue;
8478        }
8479        return None;
8480    }
8481    None
8482}
8483
8484fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
8485    let mut required = 0;
8486    let mut total = 0;
8487    let mut repeated = false;
8488    let mut cursor = parameters_node.walk();
8489    for child in parameters_node.children(&mut cursor) {
8490        match child.kind() {
8491            "parameter_declaration" => {
8492                if cpp_parameter_is_explicit_object(child, source) {
8493                    continue;
8494                }
8495                if child.child_by_field_name("declarator").is_none()
8496                    && child
8497                        .child_by_field_name("type")
8498                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
8499                {
8500                    continue;
8501                }
8502                required += 1;
8503                total += 1;
8504            }
8505            "optional_parameter_declaration" => total += 1,
8506            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
8507                repeated = true;
8508            }
8509            _ => {}
8510        }
8511    }
8512    CallableArity::new(required, total, repeated)
8513}
8514
8515fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
8516    parameter
8517        .child_by_field_name("type")
8518        .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
8519        .and_then(|type_node| type_node.child_by_field_name("constraint"))
8520        .is_some_and(|constraint| {
8521            constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
8522        })
8523}
8524
8525/// One entry of a callable's invocation parameter list.
8526///
8527/// The list excludes an explicit object parameter and a lone `void`, so its
8528/// length is the callable's invocation arity. Every derivation of a parameter
8529/// type - the rendered spelling used for overload discrimination and the
8530/// structured identity used by dependency-pack production - starts from this
8531/// same sequence, so the two can never disagree about which parameters exist.
8532#[derive(Clone, Copy)]
8533enum CppParameterSlot<'tree> {
8534    Declared(Node<'tree>),
8535    Ellipsis,
8536}
8537
8538fn cpp_callable_parameter_slots<'tree>(
8539    parameters_node: Node<'tree>,
8540    source: &str,
8541) -> Vec<CppParameterSlot<'tree>> {
8542    let mut slots = Vec::new();
8543    let mut cursor = parameters_node.walk();
8544    for parameter in parameters_node.children(&mut cursor) {
8545        match parameter.kind() {
8546            "parameter_declaration" | "optional_parameter_declaration" => {
8547                if cpp_parameter_is_explicit_object(parameter, source)
8548                    || (parameter.child_by_field_name("declarator").is_none()
8549                        && parameter
8550                            .child_by_field_name("type")
8551                            .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
8552                {
8553                    continue;
8554                }
8555                slots.push(CppParameterSlot::Declared(parameter));
8556            }
8557            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
8558                slots.push(CppParameterSlot::Ellipsis);
8559            }
8560            _ => {}
8561        }
8562    }
8563    slots
8564}
8565
8566fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
8567    cpp_callable_parameter_slots(parameters_node, source)
8568        .into_iter()
8569        .map(|slot| match slot {
8570            CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
8571            CppParameterSlot::Ellipsis => "...".to_string(),
8572        })
8573        .collect()
8574}
8575
8576/// One callable parameter's parser-derived type.
8577///
8578/// A rendered spelling such as `const T&` is a source text, not a type name. A
8579/// consumer that must publish a type into a structured model - a semantic-pack
8580/// type reference, for example - reads this instead.
8581#[derive(Debug, Clone, PartialEq, Eq)]
8582pub enum CppParameterType {
8583    /// The written type reduced to a structured identity. C++ cv-qualifiers
8584    /// have no place in that model and are not represented.
8585    Structured(StructuredTypeIdentity),
8586    /// A `...` pack, which declares no parameter type at all.
8587    Ellipsis,
8588    /// A written type with no structured reduction, such as a macro-obscured,
8589    /// `decltype`-computed, or function-pointer parameter.
8590    Unstructured,
8591}
8592
8593/// The structured type of each invocation parameter, in declaration order.
8594///
8595/// The result is index-parallel with the rendered
8596/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
8597/// callable.
8598pub fn cpp_callable_parameter_type_identities<'tree>(
8599    function_declarator: Node<'tree>,
8600    source: &str,
8601    ancestry: &ParentIndex<'tree>,
8602) -> Vec<CppParameterType> {
8603    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
8604        return Vec::new();
8605    };
8606    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
8607    cpp_callable_parameter_slots(parameters_node, source)
8608        .into_iter()
8609        .map(|slot| match slot {
8610            CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
8611            CppParameterSlot::Declared(parameter) => {
8612                cpp_parameter_type_identity(parameter, source, &lexical_scope)
8613                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
8614            }
8615        })
8616        .collect()
8617}
8618
8619fn cpp_parameter_type_identity(
8620    parameter: Node<'_>,
8621    source: &str,
8622    lexical_scope: &[String],
8623) -> Option<StructuredTypeIdentity> {
8624    let type_node = parameter.child_by_field_name("type")?;
8625    let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
8626    if let Some(declarator) = cpp_parameter_declarator(parameter) {
8627        for wrapper in cpp_structured_declarator_wrappers(declarator)
8628            .into_iter()
8629            .rev()
8630        {
8631            identity = cpp_wrap_structured_type(identity, wrapper)?;
8632        }
8633    }
8634    Some(identity)
8635}
8636
8637/// One callable parameter's comparable shape.
8638///
8639/// [`CppParameterType`] above answers "which type is written here" for a
8640/// structured model and deliberately records no cv-qualifiers, so it reports
8641/// the same value for `f(char *)` and `f(const char *)`. Deciding whether two
8642/// callable declarations declare one function needs the opposite trade: every
8643/// cv-qualifier that C++ counts as part of the parameter type must survive,
8644/// while the two declarations may spell the same type through different
8645/// qualifications. This slot carries that comparand.
8646///
8647/// The result is index-parallel with [`cpp_callable_parameter_type_identities`]
8648/// and with the rendered parameter spellings of the same callable.
8649#[derive(Debug, Clone, PartialEq, Eq)]
8650pub enum CppComparableSlot {
8651    /// A declared parameter reduced to its comparable shape.
8652    Shape(CppComparableParameter),
8653    /// A `...` pack, which declares no parameter type at all.
8654    Ellipsis,
8655    /// A parameter with no comparable reduction, such as a macro-obscured,
8656    /// `decltype`-computed, or function-pointer parameter.
8657    Unstructured,
8658}
8659
8660/// A parameter type as a flat arena of nodes plus a root index.
8661///
8662/// The arena carries the same rationale as [`StructuredTypeIdentity`]: source
8663/// can nest types very deeply, and cloning, comparing or dropping the value
8664/// must not consume the Rust call stack. Nodes are appended in post-order, so
8665/// every child index is smaller than its parent's and the last appended node is
8666/// the root.
8667///
8668/// That post-order append is also what makes the derived `PartialEq` a correct
8669/// structural equality: the builder below is deterministic, so one type shape
8670/// has exactly one arena layout no matter which spelling produced it. Two
8671/// shapes are equal as values iff they are equal as type trees.
8672#[derive(Debug, Clone, PartialEq, Eq)]
8673pub struct CppComparableParameter {
8674    nodes: Vec<CppComparableNode>,
8675    root: usize,
8676}
8677
8678/// One node of a [`CppComparableParameter`] arena.
8679///
8680/// `Reference` and `Array` carry no qualifiers because the grammar writes none
8681/// on them: a reference cannot be cv-qualified in C++, and an array's
8682/// qualifiers belong to its element type. A cv-qualifier written on a generic
8683/// type (`const std::vector<int>`) is recorded on the generic's base leaf,
8684/// which is the only Named node the whole spelling produces.
8685#[derive(Debug, Clone, PartialEq, Eq)]
8686pub enum CppComparableNode {
8687    Named {
8688        name: StructuredTypeName,
8689        primitive: bool,
8690        konst: bool,
8691        volatil: bool,
8692    },
8693    Pointer {
8694        inner: usize,
8695        konst: bool,
8696        volatil: bool,
8697    },
8698    Reference {
8699        inner: usize,
8700    },
8701    Array {
8702        inner: usize,
8703    },
8704    Generic {
8705        base: usize,
8706        arguments: Vec<usize>,
8707    },
8708}
8709
8710impl CppComparableParameter {
8711    pub fn root(&self) -> usize {
8712        self.root
8713    }
8714
8715    pub fn node(&self, index: usize) -> &CppComparableNode {
8716        &self.nodes[index]
8717    }
8718
8719    /// Apply the [dcl.fct]/5 parameter-type adjustments, which hold at the
8720    /// parameter's top level only.
8721    ///
8722    /// A top-level cv-qualifier is discarded, so `f(const int)` and `f(int)`
8723    /// declare one function, and a top-level array type becomes a pointer to
8724    /// its element type, so `f(int[3])` and `f(int *)` do too. The outermost
8725    /// type constructor is this arena's root, which is why both adjustments
8726    /// are one match on it: cv on an inner pointer level, on a pointee, or on
8727    /// an array element keeps distinguishing the type, and an array behind a
8728    /// pointer or reference is not a top-level array.
8729    fn adjust_parameter_top_level(&mut self) {
8730        let root = self.root;
8731        match &mut self.nodes[root] {
8732            CppComparableNode::Named { konst, volatil, .. }
8733            | CppComparableNode::Pointer { konst, volatil, .. } => {
8734                *konst = false;
8735                *volatil = false;
8736            }
8737            CppComparableNode::Array { inner } => {
8738                let inner = *inner;
8739                self.nodes[root] = CppComparableNode::Pointer {
8740                    inner,
8741                    konst: false,
8742                    volatil: false,
8743                };
8744            }
8745            CppComparableNode::Generic { base, .. } => {
8746                let base = *base;
8747                let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
8748                    unreachable!("a comparable generic's base is always a named leaf");
8749                };
8750                *konst = false;
8751                *volatil = false;
8752            }
8753            CppComparableNode::Reference { .. } => {}
8754        }
8755    }
8756}
8757
8758/// The comparable shape of each invocation parameter, in declaration order.
8759///
8760/// The result is index-parallel with
8761/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
8762/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
8763/// must treat as evidence of nothing rather than as agreement.
8764pub fn cpp_comparable_parameter_shapes<'tree>(
8765    function_declarator: Node<'tree>,
8766    source: &str,
8767    ancestry: &ParentIndex<'tree>,
8768) -> Vec<CppComparableSlot> {
8769    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
8770        return Vec::new();
8771    };
8772    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
8773    cpp_callable_parameter_slots(parameters_node, source)
8774        .into_iter()
8775        .map(|slot| match slot {
8776            CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
8777            CppParameterSlot::Declared(parameter) => {
8778                cpp_comparable_parameter(parameter, source, &lexical_scope)
8779                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
8780            }
8781        })
8782        .collect()
8783}
8784
8785fn cpp_comparable_parameter(
8786    parameter: Node<'_>,
8787    source: &str,
8788    lexical_scope: &[String],
8789) -> Option<CppComparableParameter> {
8790    let type_node = parameter.child_by_field_name("type")?;
8791    let levels = match cpp_parameter_declarator(parameter) {
8792        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
8793        None => Vec::new(),
8794    };
8795    let mut shape = cpp_comparable_type_shape(
8796        type_node,
8797        cpp_cv_qualifiers(parameter, source),
8798        levels,
8799        source,
8800        lexical_scope,
8801    )?;
8802    shape.adjust_parameter_top_level();
8803    Some(shape)
8804}
8805
8806/// The `const` and `volatile` qualifiers written as direct named children of
8807/// `node`.
8808///
8809/// The grammar exposes `type_qualifier` as a non-field named child in exactly
8810/// the three places a parameter's qualifiers can be written: on the
8811/// `parameter_declaration` itself (the base type), on a `type_descriptor`
8812/// (inside a template argument list), and on each `pointer_declarator` level
8813/// (the pointer object). Every other qualifier the grammar admits - `restrict`
8814/// and friends - takes no part in C++ type identity, the same filter
8815/// `cpp_parameter_type` applies to the rendered spelling (#1827).
8816fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
8817    let mut qualifiers = CppCvQualifiers::default();
8818    let mut cursor = node.walk();
8819    for child in node.named_children(&mut cursor) {
8820        if child.kind() != "type_qualifier" {
8821            continue;
8822        }
8823        match node_text(child, source) {
8824            "const" => qualifiers.konst = true,
8825            "volatile" => qualifiers.volatil = true,
8826            _ => {}
8827        }
8828    }
8829    qualifiers
8830}
8831
8832#[derive(Clone, Copy, Default)]
8833struct CppCvQualifiers {
8834    konst: bool,
8835    volatil: bool,
8836}
8837
8838impl CppCvQualifiers {
8839    fn union(self, other: Self) -> Self {
8840        Self {
8841            konst: self.konst || other.konst,
8842            volatil: self.volatil || other.volatil,
8843        }
8844    }
8845}
8846
8847/// One pointer, reference or array level a declarator chain adds.
8848#[derive(Clone, Copy)]
8849enum CppComparableLevel {
8850    Pointer { konst: bool, volatil: bool },
8851    Reference,
8852    Array,
8853}
8854
8855/// The levels `declarator` adds, outermost written level first.
8856///
8857/// C++ declarator syntax binds inside out: the level written closest to the
8858/// declared name is the outermost type constructor, and tree-sitter nests it
8859/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
8860/// applies in order to reach "array of pointer to int", and the qualifier of
8861/// `int * const *p` is read on the level it was written next to, the inner
8862/// pointer of the resulting type.
8863///
8864/// A declarator chain that names a function type - a function-pointer
8865/// parameter - has no comparable shape and reports `None`, matching the
8866/// structured identity channel.
8867fn cpp_comparable_declarator_levels(
8868    declarator: Node<'_>,
8869    source: &str,
8870) -> Option<Vec<CppComparableLevel>> {
8871    let mut levels = Vec::new();
8872    let mut current = declarator;
8873    loop {
8874        match current.kind() {
8875            "pointer_declarator" | "abstract_pointer_declarator" => {
8876                let qualifiers = cpp_cv_qualifiers(current, source);
8877                levels.push(CppComparableLevel::Pointer {
8878                    konst: qualifiers.konst,
8879                    volatil: qualifiers.volatil,
8880                });
8881            }
8882            "reference_declarator" | "abstract_reference_declarator" => {
8883                levels.push(CppComparableLevel::Reference);
8884            }
8885            "array_declarator" | "abstract_array_declarator" => {
8886                levels.push(CppComparableLevel::Array);
8887            }
8888            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
8889            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
8890            _ => return None,
8891        }
8892        let Some(next) = cpp_nested_declarator(current) else {
8893            return Some(levels);
8894        };
8895        current = next;
8896    }
8897}
8898
8899/// Reduce one written type to a comparable arena.
8900///
8901/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
8902/// two additions: each visited type node carries the cv-qualifiers written on
8903/// it, and declarator levels arrive as a prepared list rather than being
8904/// rediscovered inside the walk.
8905fn cpp_comparable_type_shape(
8906    type_node: Node<'_>,
8907    qualifiers: CppCvQualifiers,
8908    levels: Vec<CppComparableLevel>,
8909    source: &str,
8910    lexical_scope: &[String],
8911) -> Option<CppComparableParameter> {
8912    enum Work<'tree> {
8913        Visit {
8914            node: Node<'tree>,
8915            qualifiers: CppCvQualifiers,
8916        },
8917        ApplyLevels(Vec<CppComparableLevel>),
8918        BuildGeneric {
8919            argument_count: usize,
8920        },
8921    }
8922
8923    let mut nodes: Vec<CppComparableNode> = Vec::new();
8924    let mut values: Vec<usize> = Vec::new();
8925    let mut work = vec![
8926        Work::ApplyLevels(levels),
8927        Work::Visit {
8928            node: type_node,
8929            qualifiers,
8930        },
8931    ];
8932    while let Some(next) = work.pop() {
8933        match next {
8934            Work::Visit { node, qualifiers } => match node.kind() {
8935                "type_descriptor" => {
8936                    let inner_type = node
8937                        .child_by_field_name("type")
8938                        .or_else(|| node.named_child(0))?;
8939                    let mut cursor = node.walk();
8940                    let declarator = node.child_by_field_name("declarator").or_else(|| {
8941                        node.named_children(&mut cursor).find(|child| {
8942                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
8943                        })
8944                    });
8945                    let levels = match declarator {
8946                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
8947                        None => Vec::new(),
8948                    };
8949                    work.push(Work::ApplyLevels(levels));
8950                    work.push(Work::Visit {
8951                        node: inner_type,
8952                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
8953                    });
8954                }
8955                "sized_type_specifier" => {
8956                    // `unsigned char` is one primitive type whose components are
8957                    // partly unnamed tokens, so the whole specifier is its own
8958                    // name component. Reducing it to the `type` child would make
8959                    // `f(unsigned char)` and `f(char)` compare equal.
8960                    let name = StructuredTypeName::new(
8961                        vec![normalize_cpp_whitespace(node_text(node, source))],
8962                        lexical_scope.to_vec(),
8963                        false,
8964                    )?;
8965                    values.push(cpp_push_comparable_node(
8966                        &mut nodes,
8967                        CppComparableNode::Named {
8968                            name,
8969                            primitive: true,
8970                            konst: qualifiers.konst,
8971                            volatil: qualifiers.volatil,
8972                        },
8973                    ));
8974                }
8975                "qualified_identifier"
8976                | "scoped_identifier"
8977                | "scoped_type_identifier"
8978                | "type_identifier"
8979                | "field_identifier"
8980                | "identifier"
8981                | "namespace_identifier"
8982                | "primitive_type"
8983                | "template_type" => {
8984                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
8985                    values.push(cpp_push_comparable_node(
8986                        &mut nodes,
8987                        CppComparableNode::Named {
8988                            name,
8989                            primitive: node.kind() == "primitive_type",
8990                            konst: qualifiers.konst,
8991                            volatil: qualifiers.volatil,
8992                        },
8993                    ));
8994                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
8995                        let mut cursor = arguments_node.walk();
8996                        let arguments = arguments_node
8997                            .named_children(&mut cursor)
8998                            .filter(|child| !child.is_extra() && child.kind() != "comment")
8999                            .collect::<Vec<_>>();
9000                        work.push(Work::BuildGeneric {
9001                            argument_count: arguments.len(),
9002                        });
9003                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
9004                            node: argument,
9005                            qualifiers: CppCvQualifiers::default(),
9006                        }));
9007                    }
9008                }
9009                _ => {
9010                    let inner = node.child_by_field_name("type").or_else(|| {
9011                        (node.named_child_count() == 1)
9012                            .then(|| node.named_child(0))
9013                            .flatten()
9014                    })?;
9015                    work.push(Work::Visit {
9016                        node: inner,
9017                        qualifiers,
9018                    });
9019                }
9020            },
9021            Work::ApplyLevels(levels) => {
9022                let mut root = values.pop()?;
9023                for level in levels {
9024                    let node = match level {
9025                        CppComparableLevel::Pointer { konst, volatil } => {
9026                            CppComparableNode::Pointer {
9027                                inner: root,
9028                                konst,
9029                                volatil,
9030                            }
9031                        }
9032                        CppComparableLevel::Reference => {
9033                            CppComparableNode::Reference { inner: root }
9034                        }
9035                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
9036                    };
9037                    root = cpp_push_comparable_node(&mut nodes, node);
9038                }
9039                values.push(root);
9040            }
9041            Work::BuildGeneric { argument_count } => {
9042                let value_count = argument_count.checked_add(1)?;
9043                let start = values.len().checked_sub(value_count)?;
9044                let mut built = values.split_off(start);
9045                let base = built.remove(0);
9046                values.push(cpp_push_comparable_node(
9047                    &mut nodes,
9048                    CppComparableNode::Generic {
9049                        base,
9050                        arguments: built,
9051                    },
9052                ));
9053            }
9054        }
9055    }
9056    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
9057    debug_assert_eq!(
9058        root,
9059        nodes.len().saturating_sub(1),
9060        "comparable nodes are appended in post-order, so the root is the last one"
9061    );
9062    Some(CppComparableParameter { nodes, root })
9063}
9064
9065fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
9066    nodes.push(node);
9067    nodes.len() - 1
9068}
9069
9070/// The template argument list of the name `node` terminates in, if any.
9071///
9072/// `std::vector<int>` writes its arguments on the `name` of a qualified
9073/// identifier, so a walk that stopped at the qualified node would reduce
9074/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
9075fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
9076    let mut current = node;
9077    loop {
9078        match current.kind() {
9079            "template_type" => return current.child_by_field_name("arguments"),
9080            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
9081                current = current.child_by_field_name("name")?;
9082            }
9083            _ => return None,
9084        }
9085    }
9086}
9087
9088/// The callable declarator of the declaration that covers `start_byte`.
9089///
9090/// A consumer that holds a declaration's recorded byte position rather than its
9091/// syntax node - external header extraction, for instance - uses this to reach
9092/// the same `function_declarator` the declaration walk read.
9093pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
9094    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
9095    loop {
9096        if matches!(
9097            current.kind(),
9098            "declaration" | "field_declaration" | "function_definition"
9099        ) && let Some(declarator) = current
9100            .child_by_field_name("declarator")
9101            .and_then(extract_function_declarator)
9102        {
9103            return Some(declarator);
9104        }
9105        current = current.parent()?;
9106    }
9107}
9108
9109fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
9110    let mut labels = Vec::new();
9111    let mut cursor = parameters_node.walk();
9112    for child in parameters_node.children(&mut cursor) {
9113        match child.kind() {
9114            "parameter_declaration" | "optional_parameter_declaration" => {
9115                if let Some(name_node) = child
9116                    .child_by_field_name("declarator")
9117                    .and_then(cpp_declarator_label_node)
9118                {
9119                    labels.push(name_node);
9120                } else {
9121                    labels.push(child);
9122                }
9123            }
9124            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
9125                labels.push(child);
9126            }
9127            _ => {}
9128        }
9129    }
9130    labels
9131}
9132
9133fn cpp_signature_search_start<'tree>(
9134    signature: &str,
9135    function_declarator: Node<'tree>,
9136    source: &str,
9137    ancestry: &ParentIndex<'tree>,
9138) -> usize {
9139    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
9140        return 0;
9141    };
9142    let raw = node_text(enclosing, source);
9143    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
9144    let offset = function_declarator
9145        .start_byte()
9146        .saturating_sub(enclosing.start_byte())
9147        .saturating_sub(leading_trim_bytes);
9148    offset.min(signature.len())
9149}
9150
9151fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
9152    match node.kind() {
9153        "identifier" | "field_identifier" => Some(node),
9154        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
9155            .child_by_field_name("declarator")
9156            .or_else(|| last_named_child(node))
9157            .and_then(cpp_declarator_label_node),
9158        "array_declarator" => node
9159            .child_by_field_name("declarator")
9160            .and_then(cpp_declarator_label_node),
9161        "function_declarator" => node
9162            .child_by_field_name("declarator")
9163            .or_else(|| node.child_by_field_name("name"))
9164            .or_else(|| last_named_child(node))
9165            .and_then(cpp_declarator_label_node),
9166        _ => None,
9167    }
9168}
9169
9170fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
9171    let base_type = parameter
9172        .child_by_field_name("type")
9173        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
9174        .unwrap_or_default();
9175    let declarator = cpp_parameter_declarator(parameter);
9176    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
9177    // are discarded, so `f(const int)` and `f(int)` declare one function. A
9178    // qualifier written next to the parameter's type is only top-level when
9179    // the declarator adds no indirection; behind a pointer, reference or array
9180    // declarator the same qualifier belongs to the pointee, referent or
9181    // element and keeps distinguishing the type (#1827).
9182    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
9183    let mut cursor = parameter.walk();
9184    let qualifiers = parameter
9185        .named_children(&mut cursor)
9186        .filter(|child| child.kind() == "type_qualifier")
9187        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
9188        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
9189        .collect::<Vec<_>>()
9190        .join(" ");
9191    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
9192        (true, _) => base_type,
9193        (_, true) => qualifiers,
9194        (false, false) => format!("{qualifiers} {base_type}"),
9195    };
9196    let declarator_suffix = declarator
9197        .map(|node| cpp_declarator_suffix_without_name(node, source))
9198        .unwrap_or_default();
9199
9200    let combined = if type_text.is_empty() {
9201        declarator_suffix
9202    } else if declarator_suffix.is_empty() {
9203        type_text
9204    } else {
9205        format!("{type_text} {declarator_suffix}")
9206    };
9207    normalize_cpp_type_text(&combined)
9208}
9209
9210fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
9211    parameter.child_by_field_name("declarator").or_else(|| {
9212        // Some unnamed prototype parameters expose their abstract declarator
9213        // as a direct named child without the grammar's `declarator` field.
9214        // Recover only the structured abstract-declarator node; the parameter's
9215        // type and qualifiers are distinct children and must not be guessed from
9216        // source text.
9217        let mut cursor = parameter.walk();
9218        parameter
9219            .named_children(&mut cursor)
9220            .find(|child| is_cpp_abstract_declarator(child.kind()))
9221    })
9222}
9223
9224/// Whether a parameter's declarator chain adds indirection - a pointer,
9225/// reference, array or function declarator - to the parameter's written type.
9226pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
9227    let mut current = Some(declarator);
9228    while let Some(node) = current {
9229        if matches!(
9230            node.kind(),
9231            "pointer_declarator"
9232                | "abstract_pointer_declarator"
9233                | "reference_declarator"
9234                | "abstract_reference_declarator"
9235                | "array_declarator"
9236                | "abstract_array_declarator"
9237                | "function_declarator"
9238                | "abstract_function_declarator"
9239        ) {
9240            return true;
9241        }
9242        current = cpp_nested_declarator(node);
9243    }
9244    false
9245}
9246
9247fn is_cpp_abstract_declarator(kind: &str) -> bool {
9248    matches!(
9249        kind,
9250        "abstract_pointer_declarator"
9251            | "abstract_reference_declarator"
9252            | "abstract_array_declarator"
9253            | "abstract_function_declarator"
9254            | "abstract_parenthesized_declarator"
9255    )
9256}
9257
9258fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
9259    node.child_by_field_name("declarator").or_else(|| {
9260        if is_cpp_abstract_declarator(node.kind()) {
9261            let mut cursor = node.walk();
9262            node.named_children(&mut cursor)
9263                .find(|child| is_cpp_abstract_declarator(child.kind()))
9264        } else {
9265            // Named declarators historically use their last named child when
9266            // tree-sitter omits the field. Keep that broad fallback for
9267            // attributed, variadic, and recovered named shapes.
9268            last_named_child(node)
9269        }
9270    })
9271}
9272
9273fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
9274    match node.kind() {
9275        "identifier" | "field_identifier" => String::new(),
9276        "pointer_declarator" | "abstract_pointer_declarator" => {
9277            let inner = cpp_nested_declarator(node)
9278                .map(|child| cpp_declarator_suffix_without_name(child, source))
9279                .unwrap_or_default();
9280            format!("*{inner}")
9281        }
9282        "reference_declarator" | "abstract_reference_declarator" => {
9283            let inner = cpp_nested_declarator(node)
9284                .map(|child| cpp_declarator_suffix_without_name(child, source))
9285                .unwrap_or_default();
9286            let reference = node
9287                .children(&mut node.walk())
9288                .find(|child| matches!(child.kind(), "&" | "&&"))
9289                .map(|child| node_text(child, source))
9290                .unwrap_or("&");
9291            format!("{reference}{inner}")
9292        }
9293        "array_declarator" | "abstract_array_declarator" => {
9294            let inner = cpp_nested_declarator(node)
9295                .map(|child| cpp_declarator_suffix_without_name(child, source))
9296                .unwrap_or_default();
9297            let size = node
9298                .child_by_field_name("size")
9299                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
9300                .unwrap_or_default();
9301            format!("{inner}[{size}]")
9302        }
9303        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
9304            let inner = cpp_nested_declarator(node);
9305            inner
9306                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
9307                .unwrap_or_default()
9308        }
9309        "function_declarator" | "abstract_function_declarator" => {
9310            let inner = cpp_nested_declarator(node)
9311                .map(|child| cpp_declarator_suffix_without_name(child, source))
9312                .unwrap_or_default();
9313            let params = node
9314                .child_by_field_name("parameters")
9315                .map(|child| cpp_parameter_signature(child, source))
9316                .unwrap_or_else(|| "()".to_string());
9317            format!("{inner}{params}")
9318        }
9319        _ => {
9320            let text = normalize_cpp_whitespace(node_text(node, source));
9321            let name = extract_declarator_name(node, source);
9322            if name.is_empty() {
9323                text
9324            } else {
9325                text.replace(&name, "").trim().to_string()
9326            }
9327        }
9328    }
9329}
9330
9331fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
9332    collapse_cpp_whitespace(
9333        suffix
9334            .trim()
9335            .trim_start_matches("->")
9336            .trim_start_matches('{')
9337            .trim_end_matches(';'),
9338    )
9339}
9340
9341pub fn normalize_cpp_whitespace(value: &str) -> String {
9342    collapse_cpp_whitespace(value)
9343}
9344
9345fn normalize_cpp_type_text(value: &str) -> String {
9346    collapse_cpp_whitespace(value)
9347        .replace(", ", ",")
9348        .replace(" <", "<")
9349        .replace("< ", "<")
9350        .replace(" >", ">")
9351}
9352
9353fn collapse_cpp_whitespace(value: &str) -> String {
9354    let mut result = String::new();
9355    let mut prev_space = false;
9356    for ch in value.chars() {
9357        if ch.is_whitespace() {
9358            if !prev_space {
9359                result.push(' ');
9360            }
9361            prev_space = true;
9362        } else {
9363            result.push(ch);
9364            prev_space = false;
9365        }
9366    }
9367    result.trim().to_string()
9368}
9369
9370pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
9371    node_source_text(node, source)
9372}
9373
9374pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
9375    walk_named_tree_preorder(node, true, |node| {
9376        match node.kind() {
9377            "type_identifier" | "identifier" | "qualified_identifier" => {
9378                let text = node_text(node, source).trim();
9379                if !text.is_empty() {
9380                    identifiers.insert(text.to_string());
9381                }
9382            }
9383            _ => {}
9384        }
9385        WalkControl::Continue
9386    });
9387}
9388
9389fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
9390    node.child_by_field_name("body").or_else(|| {
9391        let mut cursor = node.walk();
9392        node.named_children(&mut cursor).find(|child| {
9393            matches!(
9394                child.kind(),
9395                "declaration_list" | "field_declaration_list" | "enumerator_list"
9396            )
9397        })
9398    })
9399}
9400
9401/// Return a class body's actual closing brace when the parser supplied one.
9402///
9403/// A malformed namespace sentinel can leave a class node carrying unrelated
9404/// parser errors even though its own class body is complete.  `has_error()` is
9405/// therefore too coarse an admission predicate for sentinel ownership.  The
9406/// body list, however, exposes the opening and closing punctuation directly;
9407/// a real (non-missing) final `}` proves that the class did not borrow the
9408/// enclosing namespace's close.  Requiring the body to end before its parent
9409/// container also rejects a recovered node whose body swallowed that outer
9410/// boundary.
9411fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
9412    if !matches!(
9413        node.kind(),
9414        "class_specifier" | "struct_specifier" | "union_specifier"
9415    ) {
9416        return None;
9417    }
9418    let body = cpp_body_node(node)?;
9419    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
9420        return None;
9421    }
9422    let open = body.child(0)?;
9423    let close = body.child(body.child_count().checked_sub(1)?)?;
9424    if open.kind() != "{"
9425        || open.is_missing()
9426        || close.kind() != "}"
9427        || close.is_missing()
9428        || close.end_byte() != body.end_byte()
9429        || body.end_byte() > node.end_byte()
9430        || node
9431            .parent()
9432            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
9433    {
9434        return None;
9435    }
9436    Some(close)
9437}
9438
9439fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
9440    if node.kind() == "namespace_definition" {
9441        return true;
9442    }
9443    let mut cursor = node.walk();
9444    node.named_children(&mut cursor)
9445        .any(cpp_contains_namespace_definition)
9446}
9447
9448struct CppNestedNamespaceSentinel<'tree> {
9449    function: Node<'tree>,
9450    body: Node<'tree>,
9451    namespace_components: Vec<String>,
9452}
9453
9454/// Owned structural recovery metadata for a namespace-sentinel region.
9455///
9456/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
9457/// instead of the namespace/class scopes that the declaration visitor restores.
9458/// The inverted usage walk has the original CST, so it needs the same ownership
9459/// evidence without borrowing parser nodes across its file scan.  Keep this
9460/// descriptor deliberately source-range based: callers can match a reference
9461/// node by containment and then resolve its structured type spelling in the
9462/// recovered class scope.
9463#[derive(Debug, Clone)]
9464pub struct CppSentinelRecoveredOwner {
9465    pub range: Range,
9466    /// Start of the qualified owner name (`btree<P>::method`).  A leading
9467    /// return type before this byte is looked up from the namespace; parameters,
9468    /// trailing returns, and the body use the member owner scope.
9469    pub owner_name_start_byte: usize,
9470    /// Number of leading components belonging to the namespace rather than
9471    /// the qualified class owner.  A leading return type is looked up before
9472    /// every owner component, not merely before the innermost class.
9473    pub namespace_component_count: usize,
9474    pub scope_components: Vec<String>,
9475}
9476
9477#[derive(Debug, Clone)]
9478pub struct CppSentinelRecoveredClass {
9479    pub namespace_range: Range,
9480    pub namespace_scope_components: Vec<String>,
9481    pub class_range: Range,
9482    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
9483    pub scope_components: Vec<String>,
9484    /// Qualified out-of-line member definitions owned by this class.  Their
9485    /// ranges may extend beyond `class_range` when the malformed sentinel
9486    /// swallowed the namespace close and left definitions as function siblings.
9487    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
9488}
9489
9490/// Resolve the lexical scope restored for a node in a malformed
9491/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
9492/// outrank class spans, which in turn outrank the surviving namespace body.
9493/// The class ancestor suffix is recovered from the original CST so nested
9494/// members keep their complete `Outer::Inner` owner chain.
9495pub fn cpp_sentinel_recovered_scope_for_node(
9496    node: Node<'_>,
9497    source: &str,
9498    recovered_classes: &[CppSentinelRecoveredClass],
9499) -> Option<Vec<String>> {
9500    let contains =
9501        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
9502    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
9503    for recovered in recovered_classes {
9504        for owner in recovered
9505            .owner_ranges
9506            .iter()
9507            .filter(|owner| contains(owner.range))
9508        {
9509            let replace = best_owner.is_none_or(|existing| {
9510                owner.range.end_byte.saturating_sub(owner.range.start_byte)
9511                    < existing
9512                        .range
9513                        .end_byte
9514                        .saturating_sub(existing.range.start_byte)
9515            });
9516            if replace {
9517                best_owner = Some(owner);
9518            }
9519        }
9520    }
9521    if let Some(owner) = best_owner {
9522        let mut scope = owner.scope_components.clone();
9523        if node.start_byte() < owner.owner_name_start_byte {
9524            scope.truncate(owner.namespace_component_count);
9525        }
9526        return Some(scope);
9527    }
9528
9529    let class = recovered_classes
9530        .iter()
9531        .filter(|recovered| contains(recovered.class_range))
9532        .min_by_key(|recovered| {
9533            recovered
9534                .class_range
9535                .end_byte
9536                .saturating_sub(recovered.class_range.start_byte)
9537        });
9538    let class_scope = class.is_some();
9539    let mut scope = if let Some(class) = class {
9540        class.scope_components.clone()
9541    } else {
9542        let namespace = recovered_classes
9543            .iter()
9544            .filter(|recovered| contains(recovered.namespace_range))
9545            .min_by_key(|recovered| {
9546                recovered
9547                    .namespace_range
9548                    .end_byte
9549                    .saturating_sub(recovered.namespace_range.start_byte)
9550            })?;
9551        let mut scope = namespace.namespace_scope_components.clone();
9552        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
9553        let common_prefix = scope
9554            .iter()
9555            .zip(&parser_namespace)
9556            .take_while(|(recovered, parser)| recovered == parser)
9557            .count();
9558        scope.extend(parser_namespace.into_iter().skip(common_prefix));
9559        scope
9560    };
9561    if class_scope {
9562        let mut ancestor_components = Vec::new();
9563        let mut ancestor = node.parent();
9564        while let Some(current) = ancestor {
9565            if matches!(
9566                current.kind(),
9567                "class_specifier" | "struct_specifier" | "union_specifier"
9568            ) && let Some(name) = current.child_by_field_name("name")
9569                && let Some(name_components) = cpp_name_components(name, source)
9570            {
9571                ancestor_components.push(
9572                    name_components
9573                        .into_iter()
9574                        .map(|component| component.name)
9575                        .collect::<Vec<_>>(),
9576                );
9577            }
9578            ancestor = current.parent();
9579        }
9580        ancestor_components.reverse();
9581        let base_len = scope.len();
9582        for component in ancestor_components.into_iter().flatten() {
9583            if scope.len() >= base_len && scope.last() == Some(&component) {
9584                continue;
9585            }
9586            scope.push(component);
9587        }
9588    }
9589    Some(scope)
9590}
9591
9592struct CppSentinelFragmentedClassTail<'tree> {
9593    class_node: Node<'tree>,
9594    template_node: Option<Node<'tree>>,
9595    name: String,
9596    raw_supertypes: Option<Vec<String>>,
9597    fragmented: FragmentedExportBody,
9598    consumed_start: usize,
9599}
9600
9601struct CppSentinelFragmentedClassErrorPrefix<'tree> {
9602    name: String,
9603    open: Node<'tree>,
9604    raw_supertypes: Option<Vec<String>>,
9605}
9606
9607struct CppSentinelDirectBodyClassRegion {
9608    namespace_components: Vec<String>,
9609    class_start: usize,
9610    class_start_line: usize,
9611    class_close_end: usize,
9612    class_close_line: usize,
9613    name: String,
9614}
9615
9616fn cpp_sentinel_body_class_candidate<'tree>(
9617    child: Node<'tree>,
9618) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
9619    if matches!(
9620        child.kind(),
9621        "class_specifier" | "struct_specifier" | "union_specifier"
9622    ) {
9623        return Some((child, None));
9624    }
9625    if child.kind() != "template_declaration" {
9626        if child.kind() == "declaration" {
9627            return Some((first_class_like_child(child)?, None));
9628        }
9629        return None;
9630    }
9631    let mut cursor = child.walk();
9632    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
9633        if matches!(
9634            candidate.kind(),
9635            "class_specifier" | "struct_specifier" | "union_specifier"
9636        ) {
9637            Some(candidate)
9638        } else if candidate.kind() == "declaration" {
9639            first_class_like_child(candidate)
9640        } else {
9641            None
9642        }
9643    })?;
9644    Some((class_node, Some(child)))
9645}
9646
9647/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
9648/// namespace-sentinel body when a later member macro ends the bogus sentinel
9649/// function before the real class close. The anonymous class/open tokens and
9650/// direct identifier are the structural proof; a retained direct close would
9651/// be an ordinary malformed class rather than the fragmented tail handled here.
9652fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
9653    node: Node<'tree>,
9654    source: &str,
9655) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
9656    let name = malformed_class_error_owner_name(node, source)?;
9657    let mut cursor = node.walk();
9658    let children = node.children(&mut cursor).collect::<Vec<_>>();
9659    let keyword = children.first()?;
9660    let open_index = children.iter().position(|child| child.kind() == "{")?;
9661    if children[open_index + 1..]
9662        .iter()
9663        .any(|child| child.kind() == "}")
9664    {
9665        return None;
9666    }
9667    let raw_supertypes =
9668        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
9669    Some(CppSentinelFragmentedClassErrorPrefix {
9670        name,
9671        open: children[open_index],
9672        raw_supertypes,
9673    })
9674}
9675
9676fn cpp_sentinel_direct_body_class_candidate<'tree>(
9677    child: Node<'tree>,
9678) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
9679    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
9680        return Some(candidate);
9681    }
9682    if child.kind() != "template_declaration" {
9683        return None;
9684    }
9685    let mut cursor = child.walk();
9686    let wrapper = child
9687        .named_children(&mut cursor)
9688        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
9689    Some((first_class_like_child(wrapper)?, Some(child)))
9690}
9691
9692fn cpp_sentinel_direct_namespace_components(
9693    function: Node<'_>,
9694    body: Node<'_>,
9695    source: &str,
9696) -> Option<Vec<String>> {
9697    let mut cursor = function.walk();
9698    let children = function
9699        .named_children(&mut cursor)
9700        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
9701        .collect::<Vec<_>>();
9702    let sentinel_index = children.iter().rposition(|child| {
9703        direct_identifier_name(*child, source)
9704            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
9705    })?;
9706    let mut identifiers = Vec::new();
9707    let mut stack = children[sentinel_index + 1..]
9708        .iter()
9709        .rev()
9710        .copied()
9711        .collect::<Vec<_>>();
9712    while let Some(current) = stack.pop() {
9713        if let Some(name) = direct_identifier_name(current, source) {
9714            identifiers.push(name);
9715            continue;
9716        }
9717        let mut cursor = current.walk();
9718        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
9719        stack.extend(children.into_iter().rev());
9720    }
9721    let [keyword, namespace] = identifiers.as_slice() else {
9722        return None;
9723    };
9724    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
9725        .then(|| vec![namespace.clone()])
9726}
9727
9728fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
9729    let mut sibling = class_semicolon.next_named_sibling();
9730    let namespace_close = loop {
9731        let Some(current) = sibling else {
9732            return false;
9733        };
9734        sibling = current.next_named_sibling();
9735        if current.kind() != "comment" {
9736            break current;
9737        }
9738    };
9739    if !cpp_is_stray_close_brace(namespace_close, source) {
9740        return false;
9741    }
9742    loop {
9743        let Some(current) = sibling else {
9744            return false;
9745        };
9746        sibling = current.next_named_sibling();
9747        if current.kind() == "comment" {
9748            continue;
9749        }
9750        return direct_identifier_name(current, source)
9751            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
9752    }
9753}
9754
9755fn cpp_sentinel_macro_body_class_region<'tree>(
9756    node: Node<'tree>,
9757    source: &str,
9758    ancestry: &ParentIndex<'tree>,
9759) -> Option<CppSentinelDirectBodyClassRegion> {
9760    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
9761        return None;
9762    };
9763    if node.kind() != "function_definition" || !node.has_error() {
9764        return None;
9765    }
9766    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
9767    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
9768    let mut cursor = body.walk();
9769    let candidates = body
9770        .named_children(&mut cursor)
9771        .filter_map(cpp_sentinel_direct_body_class_candidate)
9772        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
9773        .collect::<Vec<_>>();
9774    let [(class_node, template_node)] = candidates.as_slice() else {
9775        return None;
9776    };
9777    let original_body = cpp_body_node(*class_node)?;
9778    let name = class_like_name(*class_node, source, ancestry)?;
9779    if name.is_empty() || cpp_export_macro_token(&name) {
9780        return None;
9781    }
9782
9783    let mut sibling = node.next_named_sibling();
9784    let (class_close_start, class_close_end, class_close_line) = loop {
9785        let current = sibling?;
9786        let next = current.next_named_sibling();
9787        if cpp_is_stray_close_brace(current, source)
9788            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
9789        {
9790            let semicolon = next.expect("checked above");
9791            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
9792                return None;
9793            }
9794            break (
9795                current.start_byte(),
9796                semicolon.end_byte(),
9797                semicolon.end_position().row + 1,
9798            );
9799        }
9800        sibling = next;
9801    };
9802    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
9803    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
9804    let root = tree.root_node();
9805    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
9806    // The region reparse is its own tree, so it needs its own parent index;
9807    // the caller's index answers nothing about these nodes.
9808    let reparsed_ancestry = ParentIndex::new(root);
9809    let reparsed =
9810        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
9811    if reparsed.name != name
9812        || reparsed.declaration_node.start_byte() != class_node.start_byte()
9813        || reparsed.body.start_byte() != original_body.start_byte()
9814        || class_close_start <= reparsed.body.end_byte()
9815        || class_close_end <= class_node.end_byte()
9816    {
9817        return None;
9818    }
9819    Some(CppSentinelDirectBodyClassRegion {
9820        namespace_components,
9821        class_start: reparse_start,
9822        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
9823            node.start_position().row + 1
9824        }),
9825        class_close_end,
9826        class_close_line,
9827        name,
9828    })
9829}
9830
9831/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
9832/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
9833///
9834/// The parser puts the namespace opener and the malformed function in one root
9835/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
9836/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
9837/// the malformed function must begin with an all-caps type, then an ERROR whose
9838/// sole identifier is `namespace`, followed by the inner namespace identifier
9839/// and a compound body; and that body must contain a complete named class or a
9840/// structurally fragmented class prefix. A text reparse cannot prove any of
9841/// those ownership boundaries.
9842fn cpp_nested_namespace_sentinel<'tree>(
9843    node: Node<'tree>,
9844    source: &str,
9845    ancestry: &ParentIndex<'tree>,
9846) -> Option<CppNestedNamespaceSentinel<'tree>> {
9847    if !node.has_error() {
9848        return None;
9849    }
9850
9851    let (function, mut namespace_components) = if node.kind() == "ERROR" {
9852        let mut cursor = node.walk();
9853        let functions = node
9854            .named_children(&mut cursor)
9855            .filter(|child| child.kind() == "function_definition")
9856            .collect::<Vec<_>>();
9857        let [function] = functions.as_slice() else {
9858            return None;
9859        };
9860        if !function.has_error() {
9861            return None;
9862        }
9863        let mut cursor = node.walk();
9864        let children = node.children(&mut cursor).collect::<Vec<_>>();
9865        let function_index = children
9866            .iter()
9867            .position(|child| same_node(*child, *function))?;
9868        let [outer_keyword, outer_name, outer_open] =
9869            children.get(function_index.checked_sub(3)?..function_index)?
9870        else {
9871            return None;
9872        };
9873        if outer_keyword.kind() != "namespace"
9874            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
9875            || outer_open.kind() != "{"
9876        {
9877            return None;
9878        }
9879        (
9880            *function,
9881            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
9882        )
9883    } else if node.kind() == "function_definition" {
9884        let declaration_list = node.parent()?;
9885        let namespace = declaration_list.parent()?;
9886        if declaration_list.kind() != "declaration_list"
9887            || namespace.kind() != "namespace_definition"
9888            || namespace.child_by_field_name("body") != Some(declaration_list)
9889        {
9890            return None;
9891        }
9892        (node, Vec::new())
9893    } else {
9894        return None;
9895    };
9896
9897    let mut cursor = function.walk();
9898    let named = function
9899        .named_children(&mut cursor)
9900        .filter(|child| child.kind() != "comment")
9901        .collect::<Vec<_>>();
9902    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
9903        return None;
9904    };
9905    if first_type.kind() != "type_identifier" {
9906        return None;
9907    }
9908    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
9909    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
9910        return None;
9911    }
9912    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
9913        return None;
9914    }
9915    let inner_keyword = inner_error.named_child(0)?;
9916    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
9917        return None;
9918    }
9919    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
9920        return None;
9921    }
9922    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
9923    if inner_name.is_empty() || body.kind() != "compound_statement" {
9924        return None;
9925    }
9926    namespace_components.push(inner_name);
9927
9928    let mut cursor = body.walk();
9929    let has_complete_class = body.named_children(&mut cursor).any(|child| {
9930        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
9931            cpp_body_node(class_node).is_some()
9932                && class_like_name(class_node, source, ancestry)
9933                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
9934        })
9935    });
9936    if !has_complete_class
9937        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
9938    {
9939        return None;
9940    }
9941
9942    Some(CppNestedNamespaceSentinel {
9943        function,
9944        body: *body,
9945        namespace_components,
9946    })
9947}
9948
9949/// Recognize a namespace-begin sentinel directly beneath the translation unit.
9950///
9951/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
9952/// function whose type is the sentinel, whose declarator is the structured
9953/// qualified name `namespace::a::b`, and whose body contains the namespace
9954/// items. Declaration indexing already reparses this bounded region. The
9955/// inverse scanner retains the original tree, so recover the same namespace
9956/// components from the declarator fields for its lexical-scope metadata.
9957fn cpp_root_namespace_sentinel<'tree>(
9958    node: Node<'tree>,
9959    source: &str,
9960    ancestry: &ParentIndex<'tree>,
9961) -> Option<CppNestedNamespaceSentinel<'tree>> {
9962    if node.kind() != "function_definition"
9963        || !node.has_error()
9964        || node.parent()?.kind() != "translation_unit"
9965    {
9966        return None;
9967    }
9968    let first_type = node.child_by_field_name("type")?;
9969    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
9970    if first_type.kind() != "type_identifier"
9971        || sentinel.is_empty()
9972        || !cpp_export_macro_token(&sentinel)
9973    {
9974        return None;
9975    }
9976    let declarator = node.child_by_field_name("declarator")?;
9977    let body = node.child_by_field_name("body")?;
9978    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
9979        return None;
9980    }
9981    let mut cursor = node.walk();
9982    let named = node
9983        .named_children(&mut cursor)
9984        .filter(|child| child.kind() != "comment")
9985        .collect::<Vec<_>>();
9986    let [named_type, named_declarator, named_body] = named.as_slice() else {
9987        return None;
9988    };
9989    if !same_node(*named_type, first_type)
9990        || !same_node(*named_declarator, declarator)
9991        || !same_node(*named_body, body)
9992    {
9993        return None;
9994    }
9995    let mut declarator_components = Vec::new();
9996    let mut valid_components = true;
9997    walk_named_tree_preorder(declarator, true, |component| {
9998        if !matches!(
9999            component.kind(),
10000            "identifier" | "namespace_identifier" | "type_identifier"
10001        ) {
10002            return WalkControl::Continue;
10003        }
10004        let Some(component) = canonical_cpp_qualified_component(component, source) else {
10005            valid_components = false;
10006            return WalkControl::Break;
10007        };
10008        declarator_components.push(component.name);
10009        WalkControl::SkipChildren
10010    });
10011    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
10012        return None;
10013    }
10014    declarator_components.remove(0);
10015    let namespace_components = declarator_components;
10016    if namespace_components.is_empty()
10017        || namespace_components
10018            .iter()
10019            .any(|component| component.is_empty() || cpp_export_macro_token(component))
10020    {
10021        return None;
10022    }
10023
10024    let mut cursor = body.walk();
10025    let has_complete_class = body.named_children(&mut cursor).any(|child| {
10026        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
10027            cpp_body_node(class_node).is_some()
10028                && class_like_name(class_node, source, ancestry)
10029                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
10030        })
10031    });
10032    if !has_complete_class
10033        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
10034    {
10035        return None;
10036    }
10037
10038    Some(CppNestedNamespaceSentinel {
10039        function: node,
10040        body,
10041        namespace_components,
10042    })
10043}
10044
10045/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
10046/// malformed namespace-sentinel function.  The recovery is deliberately
10047/// structural: the class must be a direct body item, its own class node must be
10048/// erroneous and end before a unique anonymous `}` in the enclosing
10049/// declaration-list, and that namespace's next sibling must be a standalone
10050/// `;`.  The complete interior must pass the existing member-shaped reparse
10051/// gate. This avoids source brace scans and does not borrow a close from an
10052/// unrelated later declaration.
10053fn cpp_sentinel_fragmented_class_tail<'tree>(
10054    function: Node<'tree>,
10055    body: Node<'tree>,
10056    source: &str,
10057    ancestry: &ParentIndex<'tree>,
10058) -> Option<CppSentinelFragmentedClassTail<'tree>> {
10059    let mut cursor = body.walk();
10060    let candidates = body
10061        .named_children(&mut cursor)
10062        .filter_map(|child| {
10063            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
10064                let class_body = cpp_body_node(class_node)?;
10065                if !class_node.has_error() {
10066                    return None;
10067                }
10068                let name = class_like_name(class_node, source, ancestry)?;
10069                let raw_supertypes =
10070                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
10071                        .then(|| extract_cpp_supertypes(class_node, source));
10072                return Some((
10073                    class_node,
10074                    template_node,
10075                    name,
10076                    class_body,
10077                    class_body.start_byte().checked_add(1)?,
10078                    raw_supertypes,
10079                ));
10080            }
10081            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
10082            Some((
10083                child,
10084                None,
10085                prefix.name,
10086                prefix.open,
10087                prefix.open.end_byte(),
10088                prefix.raw_supertypes,
10089            ))
10090        })
10091        .collect::<Vec<_>>();
10092    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
10093        candidates.as_slice()
10094    else {
10095        return None;
10096    };
10097    if name.is_empty() || cpp_export_macro_token(name) {
10098        return None;
10099    }
10100
10101    let (close, semicolon) =
10102        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
10103
10104    let reparse_end = close.start_byte();
10105    if *reparse_start >= reparse_end {
10106        return None;
10107    }
10108    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
10109    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
10110        return None;
10111    }
10112    let class_range = Range {
10113        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
10114        end_byte: semicolon.end_byte(),
10115        start_line: template_node.map_or(class_node.start_position().row, |node| {
10116            node.start_position().row
10117        }) + 1,
10118        end_line: semicolon.end_position().row + 1,
10119    };
10120    Some(CppSentinelFragmentedClassTail {
10121        class_node: *class_node,
10122        template_node: *template_node,
10123        name: name.clone(),
10124        raw_supertypes: raw_supertypes.clone(),
10125        fragmented: FragmentedExportBody {
10126            reparse_start: *reparse_start,
10127            reparse_end,
10128            class_range,
10129        },
10130        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
10131    })
10132}
10133
10134/// Recover the class and out-of-line owner scopes from every malformed
10135/// namespace-sentinel region in `root`.
10136///
10137/// This is the shared structural counterpart to
10138/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
10139/// reuses the visitor's sentinel/class admission predicates instead of parsing
10140/// source text a second time.  The returned values own only ranges and names, so
10141/// they can be retained by an inverted usage scan after the tree borrow ends.
10142pub fn cpp_sentinel_recovered_classes(
10143    root: Node<'_>,
10144    source: &str,
10145) -> Vec<CppSentinelRecoveredClass> {
10146    if !root.has_error() {
10147        return Vec::new();
10148    }
10149    // This scan owns its walk of `root`, so it owns the parent index that walk
10150    // asks its ancestor questions through. Built after the error gate: a clean
10151    // tree returns without paying for one.
10152    let ancestry = ParentIndex::new(root);
10153    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
10154    let mut stack = vec![root];
10155    while let Some(current) = stack.pop() {
10156        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
10157            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
10158        {
10159            let namespace_components = cpp_sentinel_recovered_namespace_components(
10160                recovered.function,
10161                &recovered.namespace_components,
10162                source,
10163            );
10164            let fragmented = cpp_sentinel_fragmented_class_tail(
10165                recovered.function,
10166                recovered.body,
10167                source,
10168                &ancestry,
10169            );
10170            let mut class_candidates = Vec::new();
10171            let mut cursor = recovered.body.walk();
10172            for (class_node, template_node) in recovered
10173                .body
10174                .named_children(&mut cursor)
10175                .filter_map(cpp_sentinel_body_class_candidate)
10176            {
10177                let Some(name) = class_like_name(class_node, source, &ancestry) else {
10178                    continue;
10179                };
10180                if name.is_empty() || cpp_export_macro_token(&name) {
10181                    continue;
10182                }
10183                let is_fragmented = fragmented
10184                    .as_ref()
10185                    .is_some_and(|tail| same_node(tail.class_node, class_node));
10186                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
10187                    continue;
10188                }
10189                let class_range = if is_fragmented {
10190                    fragmented
10191                        .as_ref()
10192                        .map(|tail| tail.fragmented.class_range)
10193                        .expect("fragmented class range is present when class matches")
10194                } else {
10195                    cpp_declaration_range(template_node.unwrap_or(class_node))
10196                };
10197                class_candidates.push((class_range, name));
10198            }
10199            if let Some(fragmented) = fragmented
10200                .as_ref()
10201                .filter(|tail| tail.class_node.kind() == "ERROR")
10202            {
10203                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
10204            }
10205
10206            let mut owner_ranges =
10207                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
10208            cpp_sentinel_extend_unique_owner_ranges(
10209                &mut owner_ranges,
10210                cpp_sentinel_recovered_sibling_owner_ranges(
10211                    recovered.function,
10212                    &namespace_components,
10213                    source,
10214                ),
10215            );
10216            for (class_range, name) in class_candidates {
10217                push_cpp_sentinel_recovered_class(
10218                    &mut recovered_classes,
10219                    cpp_declaration_range(recovered.body),
10220                    &namespace_components,
10221                    class_range,
10222                    name,
10223                    &owner_ranges,
10224                );
10225            }
10226
10227            if let Some(declaration_list) = recovered
10228                .function
10229                .parent()
10230                .filter(|parent| parent.kind() == "declaration_list")
10231            {
10232                let outer_namespace =
10233                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
10234                push_cpp_sentinel_sibling_classes(
10235                    &mut recovered_classes,
10236                    declaration_list,
10237                    recovered.function,
10238                    &outer_namespace,
10239                    source,
10240                    &ancestry,
10241                );
10242            }
10243        } else if let Some(region) =
10244            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
10245        {
10246            let namespace_components = cpp_sentinel_recovered_namespace_components(
10247                current,
10248                &region.namespace_components,
10249                source,
10250            );
10251            let owner_container = current
10252                .parent()
10253                .filter(|parent| parent.kind() == "declaration_list")
10254                .unwrap_or(current);
10255            let owner_ranges =
10256                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
10257            push_cpp_sentinel_recovered_class(
10258                &mut recovered_classes,
10259                cpp_declaration_range(owner_container),
10260                &namespace_components,
10261                Range {
10262                    start_byte: region.class_start,
10263                    end_byte: region.class_close_end,
10264                    start_line: region.class_start_line,
10265                    end_line: region.class_close_line,
10266                },
10267                region.name,
10268                &owner_ranges,
10269            );
10270        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
10271            // A generic sentinel-prefixed class can be reduced as a malformed
10272            // function/ERROR without the explicit `namespace X` token pair.
10273            // Reuse the declaration visitor's bounded reparse and retain only
10274            // the recovered class identity/range here.
10275            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
10276                region;
10277            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
10278                continue;
10279            };
10280            let root = tree.root_node();
10281            let template_node = cpp_sentinel_reparsed_leading_template(root);
10282            // A region reparse is its own tree and needs its own parent index.
10283            let reparsed_ancestry = ParentIndex::new(root);
10284            let Some(reparsed_class) =
10285                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
10286            else {
10287                continue;
10288            };
10289            let class_node = reparsed_class.declaration_node;
10290            let name = reparsed_class.name;
10291            let namespace_components =
10292                cpp_sentinel_recovered_namespace_components(current, &[], source);
10293            let owner_container = current
10294                .parent()
10295                .filter(|parent| parent.kind() == "declaration_list")
10296                .unwrap_or(current);
10297            let mut owner_ranges =
10298                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
10299            cpp_sentinel_extend_unique_owner_ranges(
10300                &mut owner_ranges,
10301                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
10302            );
10303            push_cpp_sentinel_recovered_class(
10304                &mut recovered_classes,
10305                cpp_declaration_range(owner_container),
10306                &namespace_components,
10307                Range {
10308                    start_byte: class_start,
10309                    end_byte: close_end,
10310                    start_line: class_node.start_position().row + 1,
10311                    end_line: class_node.end_position().row + 1,
10312                },
10313                name,
10314                &owner_ranges,
10315            );
10316            if owner_container.kind() == "declaration_list" {
10317                push_cpp_sentinel_sibling_classes(
10318                    &mut recovered_classes,
10319                    owner_container,
10320                    current,
10321                    &namespace_components,
10322                    source,
10323                    &ancestry,
10324                );
10325            }
10326        }
10327
10328        let mut cursor = current.walk();
10329        stack.extend(current.named_children(&mut cursor));
10330    }
10331    // A shallower sentinel can expose nested classes as apparent namespace
10332    // siblings even after a deeper sentinel proves that a containing class
10333    // owns their ranges. Drop those shadow descriptors; scope recovery starts
10334    // from the proven containing class and appends parser-visible class
10335    // ancestors, preserving the full `Outer::Inner` chain.
10336    let shadowed = recovered_classes
10337        .iter()
10338        .map(|candidate| {
10339            recovered_classes.iter().any(|container| {
10340                container.class_range.start_byte <= candidate.class_range.start_byte
10341                    && container.class_range.end_byte >= candidate.class_range.end_byte
10342                    && container.class_range != candidate.class_range
10343                    && container.namespace_scope_components.len()
10344                        > candidate.namespace_scope_components.len()
10345                    && container
10346                        .namespace_scope_components
10347                        .starts_with(&candidate.namespace_scope_components)
10348            })
10349        })
10350        .collect::<Vec<_>>();
10351    let mut index = 0usize;
10352    recovered_classes.retain(|_| {
10353        let keep = !shadowed[index];
10354        index += 1;
10355        keep
10356    });
10357    recovered_classes
10358}
10359
10360/// A flat sentinel can swallow the first class while leaving later classes and
10361/// their out-of-line definitions as ordinary declaration-list siblings.  Once
10362/// the malformed class proves the sentinel envelope, retain those structurally
10363/// complete sibling classes under the same surviving namespace so every member
10364/// owner in the region uses one recovery contract.
10365fn push_cpp_sentinel_sibling_classes<'tree>(
10366    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
10367    declaration_list: Node<'tree>,
10368    sentinel_node: Node<'tree>,
10369    namespace_components: &[String],
10370    source: &str,
10371    ancestry: &ParentIndex<'tree>,
10372) {
10373    let owner_ranges =
10374        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
10375    let namespace_range = cpp_declaration_range(declaration_list);
10376    let mut cursor = declaration_list.walk();
10377    for (class_node, template_node) in declaration_list
10378        .named_children(&mut cursor)
10379        .filter(|child| !same_node(*child, sentinel_node))
10380        .filter_map(cpp_sentinel_body_class_candidate)
10381    {
10382        let Some(name) = class_like_name(class_node, source, ancestry) else {
10383            continue;
10384        };
10385        if name.is_empty()
10386            || cpp_export_macro_token(&name)
10387            || cpp_complete_class_body_close(class_node).is_none()
10388        {
10389            continue;
10390        }
10391        push_cpp_sentinel_recovered_class(
10392            recovered_classes,
10393            namespace_range,
10394            namespace_components,
10395            cpp_declaration_range(template_node.unwrap_or(class_node)),
10396            name,
10397            &owner_ranges,
10398        );
10399    }
10400}
10401
10402fn push_cpp_sentinel_recovered_class(
10403    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
10404    namespace_range: Range,
10405    namespace_components: &[String],
10406    class_range: Range,
10407    name: String,
10408    owner_ranges: &[CppSentinelRecoveredOwner],
10409) {
10410    let mut scope_components = namespace_components.to_vec();
10411    scope_components.push(name);
10412    let owner_ranges = owner_ranges
10413        .iter()
10414        .filter(|owner| owner.scope_components.starts_with(&scope_components))
10415        .cloned()
10416        .collect::<Vec<_>>();
10417    if recovered_classes.iter().any(|existing| {
10418        existing.class_range == class_range && existing.scope_components == scope_components
10419    }) {
10420        return;
10421    }
10422    recovered_classes.push(CppSentinelRecoveredClass {
10423        namespace_range,
10424        namespace_scope_components: namespace_components.to_vec(),
10425        class_range,
10426        scope_components,
10427        owner_ranges,
10428    });
10429}
10430
10431fn cpp_sentinel_recovered_namespace_components(
10432    function: Node<'_>,
10433    recovered_components: &[String],
10434    source: &str,
10435) -> Vec<String> {
10436    let mut ancestor_components = Vec::new();
10437    let mut ancestor = function.parent();
10438    while let Some(current) = ancestor {
10439        if current.kind() == "namespace_definition"
10440            && let Some(name_node) = current.child_by_field_name("name")
10441            && let Some(components) = cpp_name_components(name_node, source)
10442        {
10443            ancestor_components.push(
10444                components
10445                    .into_iter()
10446                    .map(|component| component.name)
10447                    .collect::<Vec<_>>(),
10448            );
10449        }
10450        ancestor = current.parent();
10451    }
10452    ancestor_components.reverse();
10453    let mut ancestors = ancestor_components
10454        .into_iter()
10455        .flatten()
10456        .collect::<Vec<_>>();
10457
10458    let overlap = (0..=ancestors.len().min(recovered_components.len()))
10459        .rev()
10460        .find(|length| {
10461            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
10462        })
10463        .unwrap_or(0);
10464    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
10465    ancestors
10466}
10467
10468fn cpp_sentinel_recovered_owner_ranges(
10469    body: Node<'_>,
10470    namespace_components: &[String],
10471    source: &str,
10472) -> Vec<CppSentinelRecoveredOwner> {
10473    let mut owners = Vec::new();
10474    walk_named_tree_preorder(body, true, |node| {
10475        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
10476    });
10477    owners
10478}
10479
10480fn cpp_sentinel_collect_owner_range(
10481    node: Node<'_>,
10482    namespace_components: &[String],
10483    source: &str,
10484    owners: &mut Vec<CppSentinelRecoveredOwner>,
10485) -> WalkControl {
10486    if node.kind() != "function_definition" {
10487        return WalkControl::Continue;
10488    }
10489    let Some(function_declarator) = extract_function_declarator(node) else {
10490        return WalkControl::Continue;
10491    };
10492    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
10493        return WalkControl::Continue;
10494    };
10495    let Some(mut components) = cpp_name_components(name_node, source) else {
10496        return WalkControl::Continue;
10497    };
10498    if components.len() <= 1 {
10499        return WalkControl::Continue;
10500    }
10501    components.pop();
10502    let mut owner_components = components
10503        .into_iter()
10504        .map(|component| component.name)
10505        .collect::<Vec<_>>();
10506    let overlap = (0..=namespace_components.len().min(owner_components.len()))
10507        .rev()
10508        .find(|length| {
10509            owner_components[..*length]
10510                == namespace_components[namespace_components.len().saturating_sub(*length)..]
10511        })
10512        .unwrap_or(0);
10513    let mut scope_components = namespace_components.to_vec();
10514    scope_components.extend(owner_components.drain(overlap..));
10515    if scope_components.len() <= namespace_components.len() {
10516        return WalkControl::Continue;
10517    }
10518    let range = cpp_declaration_range(node);
10519    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
10520        existing.range == range && existing.scope_components == scope_components
10521    }) {
10522        owners.push(CppSentinelRecoveredOwner {
10523            range,
10524            owner_name_start_byte: name_node.start_byte(),
10525            namespace_component_count: namespace_components.len(),
10526            scope_components,
10527        });
10528    }
10529    WalkControl::Continue
10530}
10531
10532fn cpp_sentinel_extend_unique_owner_ranges(
10533    owners: &mut Vec<CppSentinelRecoveredOwner>,
10534    additional: Vec<CppSentinelRecoveredOwner>,
10535) {
10536    for owner in additional {
10537        if !owners.iter().any(|existing| {
10538            existing.range == owner.range && existing.scope_components == owner.scope_components
10539        }) {
10540            owners.push(owner);
10541        }
10542    }
10543}
10544
10545fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
10546    if node.kind() != "ERROR" || node.named_child_count() != 1 {
10547        return false;
10548    }
10549    let Some(end_name) = node.named_child(0) else {
10550        return false;
10551    };
10552    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
10553        return false;
10554    }
10555    let mut cursor = node.walk();
10556    node.children(&mut cursor)
10557        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
10558}
10559
10560/// Collect owner definitions that the malformed sentinel left as later
10561/// declaration-list siblings. Parser-visible namespace siblings are a hard
10562/// boundary: their declarations must keep their own lexical namespace.
10563fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
10564    parent: Node<'_>,
10565    sentinel_node: Node<'_>,
10566    namespace_components: &[String],
10567    source: &str,
10568) -> Vec<CppSentinelRecoveredOwner> {
10569    let mut owners = Vec::new();
10570    let mut after_sentinel = false;
10571    let mut cursor = parent.walk();
10572    for child in parent.named_children(&mut cursor) {
10573        if !after_sentinel {
10574            if same_node(child, sentinel_node) {
10575                after_sentinel = true;
10576            }
10577            continue;
10578        }
10579        walk_named_tree_preorder(child, true, |node| {
10580            if node.kind() == "namespace_definition" {
10581                return WalkControl::SkipChildren;
10582            }
10583            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
10584        });
10585    }
10586    owners
10587}
10588
10589/// Collect owner definitions after a malformed namespace, stopping only at
10590/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
10591/// enclosing container is not trusted to belong to the recovered namespace.
10592fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
10593    parent: Node<'_>,
10594    sentinel_node: Node<'_>,
10595    namespace_components: &[String],
10596    source: &str,
10597) -> Option<Vec<CppSentinelRecoveredOwner>> {
10598    let mut owners = Vec::new();
10599    let mut after_namespace = false;
10600    let mut cursor = parent.walk();
10601    for child in parent.named_children(&mut cursor) {
10602        if !after_namespace {
10603            if same_node(child, sentinel_node) {
10604                after_namespace = true;
10605            }
10606            continue;
10607        }
10608        if cpp_sentinel_namespace_end(child, source) {
10609            return Some(owners);
10610        }
10611        walk_named_tree_preorder(child, true, |node| {
10612            if node.kind() == "namespace_definition" {
10613                return WalkControl::SkipChildren;
10614            }
10615            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
10616        });
10617    }
10618    None
10619}
10620
10621fn cpp_sentinel_recovered_sibling_owner_ranges(
10622    sentinel_node: Node<'_>,
10623    namespace_components: &[String],
10624    source: &str,
10625) -> Vec<CppSentinelRecoveredOwner> {
10626    let Some(declaration_list) = sentinel_node
10627        .parent()
10628        .filter(|parent| parent.kind() == "declaration_list")
10629    else {
10630        return Vec::new();
10631    };
10632    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
10633        declaration_list,
10634        sentinel_node,
10635        namespace_components,
10636        source,
10637    );
10638
10639    let Some(namespace) = declaration_list
10640        .parent()
10641        .filter(|parent| parent.kind() == "namespace_definition")
10642    else {
10643        return owners;
10644    };
10645    let Some(outer_parent) = namespace.parent() else {
10646        return owners;
10647    };
10648    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
10649        outer_parent,
10650        namespace,
10651        namespace_components,
10652        source,
10653    ) {
10654        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
10655    }
10656    owners
10657}
10658
10659fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
10660    let mut current = function_declarator.child_by_field_name("declarator")?;
10661    loop {
10662        if matches!(
10663            current.kind(),
10664            "qualified_identifier"
10665                | "scoped_identifier"
10666                | "scoped_type_identifier"
10667                | "identifier"
10668                | "field_identifier"
10669                | "operator_name"
10670                | "destructor_name"
10671                | "literal_operator_name"
10672        ) {
10673            return Some(current);
10674        }
10675        current = current
10676            .child_by_field_name("declarator")
10677            .or_else(|| current.child_by_field_name("name"))
10678            .or_else(|| last_named_child(current))?;
10679    }
10680}
10681
10682fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
10683    match node.kind() {
10684        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
10685            let mut components = match node.child_by_field_name("scope") {
10686                Some(scope) => cpp_name_components(scope, source)?,
10687                None => Vec::new(),
10688            };
10689            let name = node.child_by_field_name("name")?;
10690            components.push(canonical_cpp_qualified_component(name, source)?);
10691            Some(components)
10692        }
10693        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
10694    }
10695}
10696
10697fn cpp_sentinel_fragment_boundary<'tree>(
10698    function: Node<'tree>,
10699    class_node: Node<'tree>,
10700    class_body: Node<'tree>,
10701    source: &str,
10702) -> Option<(Node<'tree>, Node<'tree>)> {
10703    let declaration_list = function.parent()?;
10704    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
10705        return None;
10706    }
10707    let namespace = declaration_list.parent()?;
10708    if namespace.kind() != "namespace_definition"
10709        || namespace.child_by_field_name("body") != Some(declaration_list)
10710    {
10711        return None;
10712    }
10713    let mut cursor = declaration_list.walk();
10714    let closes = declaration_list
10715        .children(&mut cursor)
10716        .filter(|child| {
10717            !child.is_named()
10718                && child.kind() == "}"
10719                && child.start_byte() >= function.end_byte()
10720                && child.start_byte() > class_node.end_byte()
10721                && child.start_byte() > class_body.start_byte()
10722        })
10723        .collect::<Vec<_>>();
10724    let [close] = closes.as_slice() else {
10725        return None;
10726    };
10727    let semicolon = namespace.next_named_sibling()?;
10728    if !cpp_is_stray_semicolon(semicolon, source)
10729        || close.end_byte() != namespace.end_byte()
10730        || semicolon.start_byte() < namespace.end_byte()
10731    {
10732        return None;
10733    }
10734    Some((*close, semicolon))
10735}
10736
10737/// Detect the bogus declaration/function tree that tree-sitter recovers for a
10738/// region prefixed by an object-like macro sentinel the parser cannot see
10739/// (issue #941), and return the byte range `[start, end)` of the swallowed
10740/// declaration interior to reparse.
10741///
10742/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
10743/// `function_definition` whose first non-comment named child is the sentinel
10744/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
10745/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
10746/// the real items.
10747/// `start` is the end of the sentinel identifier -- everything after it is the
10748/// genuine source. `end` is the node's end, extended across any trailing empty
10749/// `;` statement the mis-parse displaced past the node (the class/struct closing
10750/// semicolon), so the reparse sees a complete, brace-balanced item.
10751///
10752/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
10753/// node (`has_error`). Unknown annotation/export macros can make a real callable
10754/// error-recovered even though tree-sitter still preserves its declarator, so a
10755/// preserved callable is admitted only when a displaced class keyword precedes
10756/// that declarator. The clean-reparse-to-items gate in
10757/// `cpp_reparsed_items_are_indexable` is the final arbiter.
10758/// Return the reparse start and, when present, the structurally recovered class
10759/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
10760/// separately from the reparse start because an opaque template-declaration
10761/// macro may precede it.
10762fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
10763    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
10764    {
10765        return None;
10766    }
10767    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
10768    // retain a valid function declarator despite the unknown export macro making
10769    // the outer node erroneous. Remember that declarator for the ordering gate
10770    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
10771    // class keyword precedes a spurious callable assembled from a later member.
10772    let mut declarator_cursor = node.walk();
10773    let preserved_callable = node
10774        .children_by_field_name("declarator", &mut declarator_cursor)
10775        .find_map(extract_function_declarator);
10776    // Leading documentation comments are attached to the malformed
10777    // `function_definition` as named children.  They are not part of the
10778    // sentinel prefix, so select the first non-comment child structurally
10779    // rather than requiring the sentinel to be child zero.  This is the shape
10780    // emitted for nlohmann/json's `basic_json`: its class documentation comment
10781    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
10782    // envelope otherwise ends at the first nested union.
10783    let mut cursor = node.walk();
10784    let first = node
10785        .named_children(&mut cursor)
10786        .find(|child| child.kind() != "comment")?;
10787    if first.kind() != "type_identifier" {
10788        return None;
10789    }
10790    let sentinel = normalize_cpp_whitespace(node_text(first, source));
10791    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
10792        return None;
10793    }
10794    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
10795    // makes the trailing sentinel of one region and the leading sentinel of the
10796    // next both land as bare macro-token identifiers ahead of the real content.
10797    // Advance past every leading macro-token identifier so the reparse begins at
10798    // genuine source rather than another sentinel that would re-form the bogus
10799    // shape and fail the reparse gate.
10800    let mut start = first.end_byte();
10801    let mut after_first = false;
10802    let mut cursor = node.walk();
10803    for child in node.named_children(&mut cursor) {
10804        if !after_first {
10805            if same_node(child, first) {
10806                after_first = true;
10807            }
10808            continue;
10809        }
10810        if matches!(child.kind(), "identifier" | "type_identifier")
10811            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
10812        {
10813            start = child.end_byte();
10814        } else {
10815            break;
10816        }
10817    }
10818    // An additional opaque template-declaration macro before a class can be
10819    // folded into the bogus function's qualified declarator.  In that shape
10820    // the macro is not a direct sibling we can skip above; tree-sitter exposes
10821    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
10822    // Reparse from that keyword (or a real preceding `template` keyword) so the
10823    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
10824    // a class nested in a genuine sentinel-wrapped namespace lies after the
10825    // body opening and must not change the established region start.
10826    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
10827    let mut class_start = None;
10828    let mut template_start = None;
10829    let mut stack = vec![node];
10830    while let Some(current) = stack.pop() {
10831        if current.start_byte() >= prefix_end {
10832            continue;
10833        }
10834        if matches!(
10835            current.kind(),
10836            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
10837        ) {
10838            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
10839                "class" | "struct" | "union" | "enum" => {
10840                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
10841                        seen.min(current.start_byte())
10842                    }));
10843                }
10844                "template" => {
10845                    template_start =
10846                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
10847                            seen.min(current.start_byte())
10848                        }));
10849                }
10850                _ => {}
10851            }
10852        }
10853        let mut cursor = current.walk();
10854        stack.extend(current.children(&mut cursor));
10855    }
10856    if preserved_callable.is_some_and(|callable| {
10857        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
10858    }) {
10859        return None;
10860    }
10861    if let Some(class_start) = class_start {
10862        start = template_start
10863            .filter(|template_start| *template_start < class_start)
10864            .unwrap_or(class_start);
10865    }
10866    Some((start, class_start))
10867}
10868
10869/// Locate a sentinel-prefixed class whose malformed declaration was split across
10870/// root-level siblings. The true class close is represented structurally as a
10871/// lone `}` error followed by the class's displaced `;`; nested method/body
10872/// errors are not direct siblings of the sentinel node and therefore cannot
10873/// satisfy this pair.
10874fn cpp_sentinel_macro_class_region<'tree>(
10875    node: Node<'tree>,
10876    source: &str,
10877) -> Option<(usize, usize, usize, usize, usize, usize)> {
10878    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
10879        return None;
10880    };
10881    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
10882        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
10883        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
10884    if class_start >= body_open_start {
10885        return None;
10886    }
10887    let sibling_close = {
10888        let mut sibling = node.next_named_sibling();
10889        let mut found = None;
10890        while let Some(current) = sibling {
10891            let next = current.next_named_sibling();
10892            if cpp_is_stray_close_brace(current, source)
10893                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
10894            {
10895                let semicolon = next.expect("checked above");
10896                found = Some((
10897                    current.start_byte(),
10898                    semicolon.end_byte(),
10899                    semicolon.end_position().row + 1,
10900                ));
10901                break;
10902            }
10903            sibling = next;
10904        }
10905        found
10906    };
10907    // A stray `};` sibling is this class's close only when the bounded reparse
10908    // agrees the first body-bearing class ENDS there. When the malformed
10909    // envelope swallowed the class's true close, the scan can promote a much
10910    // later scope's close instead -- in protobuf-generated headers
10911    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
10912    // `struct TableStruct_*` paired with the first message class's `};`, making
10913    // the recovered "class body" span whole `namespace {}` blocks and minting
10914    // namespace-scope classes as nested members of the recovered class, which
10915    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
10916    // (#2275). On disagreement, fall through to the suffix-reparse boundary
10917    // below, which derives the close from the class node's own balanced body
10918    // range.
10919    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
10920        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
10921            return false;
10922        };
10923        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
10924        // A region reparse is its own tree and needs its own parent index.
10925        let reparsed_ancestry = ParentIndex::new(tree.root_node());
10926        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
10927            tree.root_node(),
10928            template_node,
10929            source,
10930            &reparsed_ancestry,
10931        ) else {
10932            return false;
10933        };
10934        let body = reparsed_class.body;
10935        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
10936    });
10937    let (class_close_start, class_close_end, class_close_line) =
10938        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
10939            (class_close_start, class_close_end, class_close_line)
10940        } else {
10941            // When the malformed envelope itself is an ERROR, tree-sitter can
10942            // leave the class's balanced close in the source while promoting
10943            // all following members to siblings. Reparse the complete suffix
10944            // and use the first body-bearing class node's own field range as
10945            // the partition boundary. This keeps balancing in tree-sitter and
10946            // preserves the source's original byte offsets.
10947            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
10948            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
10949            // A region reparse is its own tree and needs its own parent index.
10950            let reparsed_ancestry = ParentIndex::new(tree.root_node());
10951            let reparsed_class = cpp_sentinel_reparsed_class(
10952                tree.root_node(),
10953                template_node,
10954                source,
10955                &reparsed_ancestry,
10956            )?;
10957            let body = reparsed_class.body;
10958            let class_close_end = body.end_byte();
10959            let class_close_start = class_close_end.checked_sub(1)?;
10960            let class_close_line = body.end_position().row + 1;
10961            (class_close_start, class_close_end, class_close_line)
10962        };
10963    if class_close_start <= class_start {
10964        return None;
10965    }
10966
10967    // Reparse only far enough to expose the class body opening. This is a
10968    // structured check that the candidate really begins with a body-bearing
10969    // class-like item; the original malformed tree cannot provide that node.
10970    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
10971    let class_root = tree.root_node();
10972    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
10973    // A region reparse is its own tree and needs its own parent index.
10974    let reparsed_ancestry = ParentIndex::new(class_root);
10975    let reparsed_class =
10976        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
10977    let body = reparsed_class.body;
10978    // The class body opening must agree with the malformed wrapper's structured
10979    // body field. This rejects an inner nested class while permitting later
10980    // members to remain fragmented as root-level siblings in the bounded parse.
10981    if body.start_byte() != body_open_start {
10982        return None;
10983    }
10984    let body_start = body.start_byte().checked_add(1)?;
10985    (body_start < class_close_start).then_some((
10986        reparse_start,
10987        class_start,
10988        body_start,
10989        class_close_start,
10990        class_close_end,
10991        class_close_line,
10992    ))
10993}
10994
10995/// Find the `{` token immediately following the class/struct/union/enum token
10996/// at `class_start` in the malformed tree. The token is anonymous in the C++
10997/// grammar, so this deliberately walks all children (not only named children)
10998/// and relies on sibling structure rather than source-text searching.
10999fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
11000    let mut stack = vec![node];
11001    while let Some(current) = stack.pop() {
11002        if current.start_byte() == class_start
11003            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
11004        {
11005            let mut sibling = current.next_sibling();
11006            while let Some(candidate) = sibling {
11007                if candidate.kind() == "{" {
11008                    return Some(candidate.start_byte());
11009                }
11010                sibling = candidate.next_sibling();
11011            }
11012        }
11013        let mut cursor = current.walk();
11014        stack.extend(current.children(&mut cursor));
11015    }
11016    None
11017}
11018
11019/// The class body that tree-sitter displaced out of a sentinel-prefixed
11020/// declaration and left as the malformed node's next sibling.
11021///
11022/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
11023/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
11024/// the last child of that `ERROR` and its `{` opens a sibling
11025/// `compound_statement` instead. The body is still the malformed tree's own
11026/// structured token, which is what the caller's `body.start_byte() !=
11027/// body_open_start` agreement check needs; it just is not reachable by walking
11028/// forward from the class token inside the node.
11029fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
11030    node.next_named_sibling()
11031        .filter(|sibling| sibling.kind() == "compound_statement")
11032}
11033
11034fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
11035    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
11036    let mut end = if class_start.is_some() {
11037        cpp_macro_prefixed_class_end(source, start)?
11038    } else {
11039        node.end_byte()
11040    };
11041    if class_start.is_none()
11042        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
11043    {
11044        end = end.max(namespace_end);
11045    }
11046    let mut sibling = node.next_named_sibling();
11047    while let Some(current) = sibling {
11048        if !cpp_is_stray_semicolon(current, source) {
11049            break;
11050        }
11051        end = current.end_byte();
11052        sibling = current.next_named_sibling();
11053    }
11054    (start < end).then_some((start, end))
11055}
11056
11057/// Extend a sentinel reparse through a following namespace that tree-sitter
11058/// flattened into the sentinel node's sibling list.
11059///
11060/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
11061/// unknown macro becomes a false function return type and consumes the first
11062/// namespace body. A second `namespace detail` then loses its enclosing node:
11063/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
11064/// attaches its declarations to the surrounding error tree. Reparse from that
11065/// structured keyword so tree-sitter, rather than a source-text brace scan,
11066/// supplies the complete namespace boundary.
11067fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
11068    let mut sibling = node.next_sibling();
11069    let keyword = loop {
11070        let candidate = sibling?;
11071        sibling = candidate.next_sibling();
11072        if candidate.kind() != "comment" {
11073            break candidate;
11074        }
11075    };
11076    if keyword.kind() != "namespace" {
11077        return None;
11078    }
11079    let name = loop {
11080        let candidate = sibling?;
11081        sibling = candidate.next_sibling();
11082        if candidate.kind() != "comment" {
11083            break candidate;
11084        }
11085    };
11086    if cpp_namespace_name_components(name, source).is_empty() {
11087        return None;
11088    }
11089    let open = loop {
11090        let candidate = sibling?;
11091        sibling = candidate.next_sibling();
11092        if candidate.kind() != "comment" {
11093            break candidate;
11094        }
11095    };
11096    if open.kind() != "{" {
11097        return None;
11098    }
11099
11100    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
11101    let root = tree.root_node();
11102    let mut cursor = root.walk();
11103    let namespace = root
11104        .named_children(&mut cursor)
11105        .find(|candidate| candidate.kind() != "comment")?;
11106    (namespace.kind() == "namespace_definition"
11107        && namespace.start_byte() == keyword.start_byte()
11108        && namespace.child_by_field_name("body").is_some())
11109    .then_some(namespace.end_byte())
11110}
11111
11112/// Parse the source suffix beginning at a structurally recovered class/template
11113/// keyword and return the end of its first body-bearing class item.  The parser,
11114/// rather than a brace scanner, owns nested-body balancing.  This is needed when
11115/// the original error tree truncates the class and scatters later members as
11116/// top-level siblings.
11117fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
11118    let tree = cpp_reparse_region_items(source, start, source.len())?;
11119    let root = tree.root_node();
11120    let mut cursor = root.walk();
11121    for item in root.named_children(&mut cursor) {
11122        if item.end_byte() <= start || item.kind() == "comment" {
11123            continue;
11124        }
11125        let mut stack = vec![item];
11126        while let Some(current) = stack.pop() {
11127            if matches!(
11128                current.kind(),
11129                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
11130            ) && cpp_body_node(current).is_some()
11131            {
11132                return Some(current.end_byte());
11133            }
11134            let mut cursor = current.walk();
11135            stack.extend(current.named_children(&mut cursor));
11136        }
11137        // The recovered prefix is required to begin with the class item.  If
11138        // the first real item is something else, fail closed rather than skip
11139        // arbitrary source looking for a later class.
11140        return None;
11141    }
11142    None
11143}
11144
11145/// An empty `;` statement: the displaced closing semicolon of a struct/class that
11146/// the sentinel mis-parse split off past the bogus function node.
11147fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
11148    node.kind() == "expression_statement"
11149        && node.named_child_count() == 0
11150        && node_text(node, source).trim() == ";"
11151}
11152
11153/// Recover the real field name when a leading object-like annotation macro
11154/// displaces a qualified type into tree-sitter's bit-field recovery shape.
11155///
11156/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
11157/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
11158/// `bitfield_clause` containing an error plus an assignment.  The assignment's
11159/// left field is the only structured declaration name in that malformed tail.
11160/// A real bit-field is excluded by the all-caps macro type and required error.
11161fn recovered_macro_qualified_field_declarators<'tree>(
11162    node: Node<'tree>,
11163    source: &str,
11164) -> Option<Vec<Node<'tree>>> {
11165    if node.kind() != "field_declaration" {
11166        return None;
11167    }
11168    let macro_type = node.child_by_field_name("type")?;
11169    if macro_type.kind() != "type_identifier"
11170        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11171    {
11172        return None;
11173    }
11174    let pseudo_declarator = node.child_by_field_name("declarator")?;
11175    if pseudo_declarator.kind() != "field_identifier" {
11176        return None;
11177    }
11178    let mut cursor = node.walk();
11179    let clause = node
11180        .named_children(&mut cursor)
11181        .find(|child| child.kind() == "bitfield_clause")?;
11182    if !(0..clause.named_child_count()).any(|index| {
11183        clause
11184            .named_child(index)
11185            .is_some_and(|child| child.kind() == "ERROR")
11186    }) {
11187        return None;
11188    }
11189    let mut recovered = Vec::new();
11190    let mut stack = vec![clause];
11191    while let Some(current) = stack.pop() {
11192        if current.kind() == "assignment_expression"
11193            && let Some(left) = current.child_by_field_name("left")
11194            && extract_variable_name(left, source).is_some()
11195        {
11196            recovered.push(left);
11197            break;
11198        }
11199        let mut cursor = current.walk();
11200        stack.extend(current.named_children(&mut cursor));
11201    }
11202    if recovered.is_empty() {
11203        return None;
11204    }
11205    let mut cursor = node.walk();
11206    recovered.extend(
11207        node.children_by_field_name("declarator", &mut cursor)
11208            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
11209    );
11210    Some(recovered)
11211}
11212
11213/// Recover a macro-qualified constructor that tree-sitter represents as one
11214/// field declaration. The constructor call remains inside the direct recovery
11215/// error, while each member initializer becomes a false function declarator.
11216/// The class owner proves the constructor name and lets the caller ignore those
11217/// initializer declarators.
11218fn recovered_macro_qualified_constructor_call<'tree>(
11219    node: Node<'tree>,
11220    class_name: &str,
11221    source: &str,
11222) -> Option<Node<'tree>> {
11223    if node.kind() != "field_declaration" {
11224        return None;
11225    }
11226    let macro_type = node.child_by_field_name("type")?;
11227    if macro_type.kind() != "type_identifier"
11228        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11229    {
11230        return None;
11231    }
11232    let mut cursor = node.walk();
11233    let bitfield = node
11234        .named_children(&mut cursor)
11235        .find(|child| child.kind() == "bitfield_clause")?;
11236    let error = bitfield
11237        .named_child(0)
11238        .filter(|child| child.kind() == "ERROR")?;
11239    let mut stack = vec![error];
11240    while let Some(current) = stack.pop() {
11241        if current.kind() == "call_expression"
11242            && current
11243                .child_by_field_name("function")
11244                .is_some_and(|function| node_text(function, source) == class_name)
11245            && current
11246                .child_by_field_name("arguments")
11247                .is_some_and(|arguments| arguments.kind() == "argument_list")
11248        {
11249            return Some(current);
11250        }
11251        let mut cursor = current.walk();
11252        stack.extend(current.named_children(&mut cursor));
11253    }
11254    None
11255}
11256
11257/// Recover a macro-qualified member function declaration that tree-sitter
11258/// represents as a pseudo-field. An object-like export macro before a qualified
11259/// return type can displace the namespace and type into an ERROR/bitfield
11260/// recovery, leaving the callable as a structured `call_expression`.
11261///
11262/// The caller must route this shape before ordinary declarator classification;
11263/// otherwise the displaced namespace identifier is published as a field.
11264fn recovered_macro_qualified_function_call<'tree>(
11265    node: Node<'tree>,
11266    source: &str,
11267) -> Option<Node<'tree>> {
11268    if node.kind() != "field_declaration" {
11269        return None;
11270    }
11271    let macro_type = node.child_by_field_name("type")?;
11272    if macro_type.kind() != "type_identifier"
11273        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11274    {
11275        return None;
11276    }
11277    let declarator = node.child_by_field_name("declarator")?;
11278    if declarator.kind() != "field_identifier" {
11279        return None;
11280    }
11281    let mut cursor = node.walk();
11282    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11283    if !named.iter().any(|child| {
11284        child.kind() == "storage_class_specifier"
11285            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
11286    }) {
11287        return None;
11288    }
11289    let bitfield = named
11290        .iter()
11291        .find(|child| child.kind() == "bitfield_clause")?;
11292    let mut bitfield_cursor = bitfield.walk();
11293    let payload = bitfield
11294        .named_children(&mut bitfield_cursor)
11295        .collect::<Vec<_>>();
11296    let [displaced_error, call] = payload.as_slice() else {
11297        return None;
11298    };
11299    if displaced_error.kind() != "ERROR"
11300        || displaced_error.named_child_count() != 1
11301        || displaced_error
11302            .named_child(0)
11303            .is_none_or(|child| child.kind() != "identifier")
11304        || call.kind() != "call_expression"
11305        || call
11306            .child_by_field_name("function")
11307            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
11308        || call
11309            .child_by_field_name("arguments")
11310            .is_none_or(|arguments| arguments.kind() != "argument_list")
11311    {
11312        return None;
11313    }
11314    Some(*call)
11315}
11316
11317fn recovered_macro_qualified_function_parameters(
11318    arguments: Node<'_>,
11319    source: &str,
11320) -> Option<(String, Vec<String>)> {
11321    if arguments.kind() != "argument_list" {
11322        return None;
11323    }
11324    let mut cursor = arguments.walk();
11325    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
11326    if named.is_empty() {
11327        return Some(("()".to_string(), Vec::new()));
11328    }
11329    let mut types = Vec::new();
11330    let mut labels = Vec::new();
11331    let mut index = 0;
11332    while index < named.len() {
11333        let parameter_type = named[index];
11334        let parameter_name = named.get(index + 1).copied()?;
11335        if !matches!(
11336            parameter_type.kind(),
11337            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
11338        ) || parameter_name.kind() != "ERROR"
11339            || parameter_name.named_child_count() != 1
11340            || parameter_name
11341                .named_child(0)
11342                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
11343        {
11344            return None;
11345        }
11346        let parameter_name = parameter_name.named_child(0)?;
11347        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
11348        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
11349        index += 2;
11350    }
11351    Some((format!("({})", types.join(", ")), labels))
11352}
11353
11354/// Recognize the phantom field tree-sitter emits for a macro-qualified
11355/// function return type.  For example,
11356/// `static API result_type ThresholdForSmallA() { ... }` can become a
11357/// `field_declaration` (`API` as the type and `result_type` as a field name)
11358/// followed by a clean `function_definition` for `ThresholdForSmallA`.
11359///
11360/// Keep this predicate entirely tied to the CST envelope: the type must be an
11361/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
11362/// the declaration must carry a missing semicolon rather than a real one, and
11363/// the immediate named sibling must expose a function declarator.  A real
11364/// macro-decorated field with an explicit semicolon therefore remains a field.
11365pub fn recovered_macro_return_type_node<'tree>(
11366    node: Node<'tree>,
11367    source: &str,
11368) -> Option<Node<'tree>> {
11369    if node.kind() != "field_declaration" {
11370        return None;
11371    }
11372    let macro_type = node.child_by_field_name("type")?;
11373    if macro_type.kind() != "type_identifier"
11374        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11375    {
11376        return None;
11377    }
11378    let declarator = node.child_by_field_name("declarator")?;
11379    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
11380        return None;
11381    }
11382    let mut has_missing_semicolon = false;
11383    let mut has_real_semicolon = false;
11384    for index in 0..node.child_count() {
11385        let Some(child) = node.child(index) else {
11386            continue;
11387        };
11388        if child.kind() != ";" {
11389            continue;
11390        }
11391        if child.is_missing() {
11392            has_missing_semicolon = true;
11393        } else {
11394            has_real_semicolon = true;
11395        }
11396    }
11397    if !has_missing_semicolon || has_real_semicolon {
11398        return None;
11399    }
11400    let mut next = node.next_named_sibling();
11401    while next.is_some_and(|sibling| sibling.kind() == "comment") {
11402        next = next.and_then(|sibling| sibling.next_named_sibling());
11403    }
11404    let next = next?;
11405    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
11406        return None;
11407    }
11408    let function_declarator = next.child_by_field_name("declarator")?;
11409    extract_function_declarator(function_declarator).map(|_| declarator)
11410}
11411
11412/// Whether `name` is a type parameter of a template declaration that lexically
11413/// encloses `node`. The malformed macro-return field uses the parameter name as
11414/// its pseudo-declarator; preserving that field is necessary to publish a
11415/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
11416/// ancestors instead of interpreting source text so nested templates and
11417/// parser-recovered regions retain their real lexical scopes.
11418pub(crate) fn cpp_active_template_type_parameter<'tree>(
11419    node: Node<'tree>,
11420    name: &str,
11421    source: &str,
11422    ancestry: &ParentIndex<'tree>,
11423) -> bool {
11424    let mut ancestor = ancestry.parent(node);
11425    while let Some(current) = ancestor {
11426        if current.kind() == "template_declaration"
11427            && let Some(parameters) = current.child_by_field_name("parameters")
11428        {
11429            let mut cursor = parameters.walk();
11430            if parameters.named_children(&mut cursor).any(|parameter| {
11431                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
11432                    && cpp_template_parameter_name(parameter, source)
11433                        .is_some_and(|parameter_name| parameter_name == name)
11434            }) {
11435                return true;
11436            }
11437        }
11438        ancestor = ancestry.parent(current);
11439    }
11440    false
11441}
11442
11443/// Reparse the region `[start, end)` of `source` as C++, confined to the region
11444/// via included ranges so every reparsed node keeps its original byte offset and
11445/// line number. The existing visitors read node text from the original source,
11446/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
11447/// `parse_rust_region_tree` technique.
11448fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
11449    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
11450}
11451
11452fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
11453    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
11454        return None;
11455    }
11456    let semicolon = node.next_sibling()?;
11457    if semicolon.kind() != ";" || semicolon.is_missing() {
11458        return None;
11459    }
11460    let row = node.start_position().row;
11461    let mut start = node.start_byte();
11462    let mut sibling = node.prev_sibling();
11463    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
11464        if previous.kind() == ";" {
11465            break;
11466        }
11467        start = previous.start_byte();
11468        sibling = previous.prev_sibling();
11469    }
11470    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
11471}
11472
11473fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
11474    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
11475        return false;
11476    }
11477    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
11478        return false;
11479    }
11480    let Some(declarator) = (if node.kind() == "function_definition" {
11481        node.child_by_field_name("declarator")
11482            .and_then(extract_function_declarator)
11483    } else {
11484        node.named_child(0)
11485            .filter(|child| child.kind() == "function_declarator")
11486    }) else {
11487        return false;
11488    };
11489    let Some(name) = cpp_function_declarator_name_node(declarator) else {
11490        return false;
11491    };
11492    declarator.start_byte() == node.start_byte()
11493        && name.kind() == "identifier"
11494        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
11495}
11496
11497/// Reparse a fragmented class-body interior while preserving its original byte
11498/// and line offsets. Unlike an included-range translation-unit parse, a padded
11499/// prefix keeps C++ preprocessor directives after an access label in the same
11500/// recovery shape tree-sitter produces for a complete class body.
11501fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
11502    let bytes = source.as_bytes();
11503    let prefix = bytes.get(..start)?;
11504    let interior = bytes.get(start..end)?;
11505    let mut padded = Vec::with_capacity(end);
11506    padded.extend(
11507        prefix
11508            .iter()
11509            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
11510    );
11511    padded.extend_from_slice(interior);
11512    let padded = String::from_utf8(padded).ok()?;
11513    let mut parser = Parser::new();
11514    parser
11515        .set_language(&tree_sitter_cpp::LANGUAGE.into())
11516        .ok()?;
11517    parser.parse(&padded, None)
11518}
11519
11520/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
11521/// reparsed interior is indexed only when every top-level named node is a
11522/// well-formed C++ item (or a comment) and at least one real item is present.
11523/// Expression/statement soup surfaces as a top-level `ERROR` or
11524/// `expression_statement`, neither of which is an item kind, so it is rejected.
11525///
11526/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
11527/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
11528/// swallowed by a preceding dangling sentinel) reparses to a real
11529/// `namespace_definition` whose body still holds a bogus `function_definition`,
11530/// so the subtree legitimately carries an error. Container items are admitted
11531/// even with an internal error; the inner bogus function is recovered recursively
11532/// when `visit_function_definition` walks it. Each recursion strips at least one
11533/// leading sentinel, so the region strictly shrinks and recovery terminates.
11534///
11535/// A top-level `function_definition` is the one place we stay strict: it is
11536/// admitted only when it is clean or is itself a sentinel candidate. A function
11537/// that has an error and is not a sentinel is a real callable with a broken body,
11538/// so we refuse the whole reparse and let the ordinary path handle it (preserving
11539/// its real return type rather than re-deriving an implicit one).
11540fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
11541    let mut cursor = root.walk();
11542    let mut saw_item = false;
11543    for child in root.named_children(&mut cursor) {
11544        match child.kind() {
11545            "comment" => {}
11546            "function_definition" => {
11547                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
11548                    return false;
11549                }
11550                saw_item = true;
11551            }
11552            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
11553            _ => return false,
11554        }
11555    }
11556    saw_item
11557}
11558
11559/// Robustness gate for a reparsed fragmented multiple-base export class body
11560/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
11561/// kinds a class body produces when reparsed at translation-unit scope: the
11562/// access-specifier label preceding the first member surfaces as a
11563/// `labeled_statement` wrapping that member, and members surface as
11564/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
11565/// Statement or expression soup surfaces as other top-level kinds and is rejected,
11566/// so only a genuinely member-shaped body is ever re-owned as members; anything
11567/// ambiguous falls back to indexing the class alone.
11568fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
11569    if node.kind() != "ERROR" {
11570        return false;
11571    }
11572    let mut stack = Vec::new();
11573    let mut saw_function_declarator = false;
11574    let mut cursor = node.walk();
11575    for child in node.named_children(&mut cursor) {
11576        stack.push(child);
11577    }
11578    while let Some(current) = stack.pop() {
11579        match current.kind() {
11580            // Tree-sitter may wrap adjacent copy-control declarations in a
11581            // nested ERROR. Keep descending only through ERROR wrappers; the
11582            // actual declaration payload must be a function_declarator.
11583            "ERROR" => {
11584                let mut cursor = current.walk();
11585                stack.extend(current.named_children(&mut cursor));
11586            }
11587            "function_declarator" => saw_function_declarator = true,
11588            _ => return false,
11589        }
11590    }
11591    saw_function_declarator
11592}
11593
11594fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
11595    if node.kind() != "ERROR" {
11596        return false;
11597    }
11598    let mut cursor = node.walk();
11599    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11600    let [explicit, constructor_error, destructor] = named.as_slice() else {
11601        return false;
11602    };
11603    let Some(constructor) = constructor_error.named_child(0) else {
11604        return false;
11605    };
11606    let Some(constructor_name) =
11607        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
11608    else {
11609        return false;
11610    };
11611    let Some(destructor_name) =
11612        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
11613    else {
11614        return false;
11615    };
11616    let Some(destroyed_type) = destructor_name.named_child(0) else {
11617        return false;
11618    };
11619    explicit.kind() == "explicit_function_specifier"
11620        && constructor_error.kind() == "ERROR"
11621        && constructor_error.named_child_count() == 1
11622        && constructor.kind() == "function_declarator"
11623        && constructor_name.kind() == "identifier"
11624        && destructor.kind() == "function_declarator"
11625        && destructor_name.kind() == "destructor_name"
11626        && destroyed_type.kind() == "identifier"
11627        && node_text(constructor_name, source) == node_text(destroyed_type, source)
11628}
11629
11630fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
11631    if node.kind() != "compound_statement" {
11632        return false;
11633    }
11634    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
11635        return false;
11636    };
11637    if prefix.kind() == "labeled_statement"
11638        && prefix.named_child(0).is_some_and(|label| {
11639            matches!(
11640                node_text(label, source).trim(),
11641                "public" | "private" | "protected"
11642            )
11643        })
11644    {
11645        return prefix.named_children(&mut prefix.walk()).any(|child| {
11646            child.kind() == "declaration"
11647                && child.has_error()
11648                && child
11649                    .named_children(&mut child.walk())
11650                    .any(cpp_reparsed_member_error_is_indexable)
11651        });
11652    }
11653    // A malformed constructor initializer can be split into a declaration
11654    // followed by its compound body when the class prefix already contains
11655    // realistic members. Keep this admission tied to that exact structured
11656    // declaration/error/body chain rather than accepting arbitrary blocks.
11657    prefix.kind() == "declaration"
11658        && prefix.has_error()
11659        && prefix
11660            .named_children(&mut prefix.walk())
11661            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
11662}
11663
11664fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
11665    if !cpp_reparsed_member_error_is_indexable(node) {
11666        return false;
11667    }
11668    let Some(preproc) = node.next_named_sibling() else {
11669        return false;
11670    };
11671    preproc.kind() == "preproc_if"
11672        && preproc.has_error()
11673        && preproc
11674            .named_children(&mut preproc.walk())
11675            .any(|child| child.kind() == "expression_statement" && child.has_error())
11676        && preproc
11677            .next_named_sibling()
11678            .is_some_and(|body| body.kind() == "compound_statement")
11679}
11680
11681/// Return a function body whose braces and ownership are explicit in the
11682/// reparsed class-member tree. An error below a real function envelope is
11683/// recoverable by the ordinary function visitor; a missing/deferred body is
11684/// not, because accepting it would let statement soup masquerade as a member.
11685fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
11686    if node.kind() != "function_definition" {
11687        return None;
11688    }
11689    let body = node.child_by_field_name("body")?;
11690    if body.kind() != "compound_statement" {
11691        return None;
11692    }
11693    let open = body.child(0)?;
11694    let close = body.child(body.child_count().checked_sub(1)?)?;
11695    if open.kind() != "{"
11696        || open.is_missing()
11697        || close.kind() != "}"
11698        || close.is_missing()
11699        || close.end_byte() != body.end_byte()
11700        || body.end_byte() != node.end_byte()
11701    {
11702        return None;
11703    }
11704    Some(body)
11705}
11706
11707fn cpp_reparsed_member_function_errors_are_in_body(
11708    node: Node<'_>,
11709    body: Node<'_>,
11710    source: &str,
11711) -> bool {
11712    let mut cursor = node.walk();
11713    node.children(&mut cursor).all(|child| {
11714        same_node(child, body)
11715            || cpp_reparsed_member_attribute_error(child, source)
11716            || cpp_reparsed_member_signature_identifier_errors(child)
11717            || (!child.has_error() && !child.is_error() && !child.is_missing())
11718    })
11719}
11720
11721/// A complete callable can still carry parser errors in its signature when a
11722/// project annotation is not part of the C++ grammar (`nonneg int`,
11723/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
11724/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
11725/// leaves inside the already-proven callable envelope; structured statements,
11726/// literals, missing tokens, and other malformed signature payload remain
11727/// rejected.
11728fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
11729    if !node.has_error() && !node.is_error() && !node.is_missing() {
11730        return false;
11731    }
11732    let mut stack = vec![node];
11733    let mut saw_error = false;
11734    while let Some(current) = stack.pop() {
11735        if current.is_missing() {
11736            return false;
11737        }
11738        if current.kind() == "ERROR" {
11739            saw_error = true;
11740            let mut cursor = current.walk();
11741            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
11742            if children
11743                .iter()
11744                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
11745            {
11746                return false;
11747            }
11748            stack.extend(children);
11749            continue;
11750        }
11751        let mut cursor = current.walk();
11752        stack.extend(current.children(&mut cursor));
11753    }
11754    saw_error
11755}
11756
11757fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
11758    node.kind() == "ERROR"
11759        && node.named_child_count() == 1
11760        && node.named_child(0).is_some_and(|attribute| {
11761            attribute.kind() == "identifier"
11762                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
11763        })
11764}
11765
11766/// A C++ attribute placed between a member's declarator and body can make
11767/// tree-sitter expose the callable as
11768/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
11769/// Keep this admission tied to that exact node geometry. In particular, an
11770/// arbitrary ERROR or identifier before a compound statement is not enough.
11771fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
11772    let Some(body) = cpp_reparsed_member_function_body(node) else {
11773        return false;
11774    };
11775    let mut cursor = node.walk();
11776    let named = node
11777        .named_children(&mut cursor)
11778        .filter(|child| child.kind() != "comment")
11779        .collect::<Vec<_>>();
11780    let [type_node, error, attribute, body_node] = named.as_slice() else {
11781        return false;
11782    };
11783    if !same_node(*body_node, body)
11784        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
11785        || attribute.kind() != "identifier"
11786        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
11787        || error.kind() != "ERROR"
11788        || error.named_child_count() != 1
11789    {
11790        return false;
11791    }
11792    error
11793        .named_child(0)
11794        .is_some_and(cpp_reparsed_attribute_callable_declarator)
11795}
11796
11797fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
11798    cpp_structured_type_path(node, source).is_some()
11799        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
11800}
11801
11802fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11803    let Some(body) = cpp_reparsed_member_function_body(node) else {
11804        return false;
11805    };
11806    let mut cursor = node.walk();
11807    let named = node
11808        .named_children(&mut cursor)
11809        .filter(|child| child.kind() != "comment")
11810        .collect::<Vec<_>>();
11811    let [friend, return_error, declarator, body_node] = named.as_slice() else {
11812        return false;
11813    };
11814    let Some(return_type) = return_error.named_child(0) else {
11815        return false;
11816    };
11817    same_node(*body_node, body)
11818        && friend.kind() == "type_identifier"
11819        && node_text(*friend, source) == "friend"
11820        && return_error.kind() == "ERROR"
11821        && return_error.named_child_count() == 1
11822        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
11823        && extract_function_declarator(*declarator)
11824            .and_then(cpp_function_declarator_name_node)
11825            .is_some()
11826}
11827
11828fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11829    let Some(body) = cpp_reparsed_member_function_body(node) else {
11830        return false;
11831    };
11832    let mut cursor = node.walk();
11833    let named = node
11834        .named_children(&mut cursor)
11835        .filter(|child| child.kind() != "comment")
11836        .collect::<Vec<_>>();
11837    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
11838        return false;
11839    };
11840    let Some(return_type) = return_error.named_child(0) else {
11841        return false;
11842    };
11843    same_node(*body_node, body)
11844        && prefix
11845            .iter()
11846            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
11847        && attribute.kind() == "type_identifier"
11848        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
11849        && return_error.kind() == "ERROR"
11850        && return_error.named_child_count() == 1
11851        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
11852        && extract_function_declarator(*declarator)
11853            .and_then(cpp_function_declarator_name_node)
11854            .is_some()
11855}
11856
11857/// An included-range reparse that begins inside a malformed class can merge an
11858/// access label and following template member. Tree-sitter then emits the label
11859/// as the `template_type` name, the template parameter list as its arguments,
11860/// an ERROR-wrapped return type, the callable declarator, and its complete
11861/// body. Admit only that exact structured displacement.
11862fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11863    let Some(body) = cpp_reparsed_member_function_body(node) else {
11864        return false;
11865    };
11866    let mut cursor = node.walk();
11867    let named = node
11868        .named_children(&mut cursor)
11869        .filter(|child| child.kind() != "comment")
11870        .collect::<Vec<_>>();
11871    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
11872        return false;
11873    };
11874    let Some(template_name) = template_type.child_by_field_name("name") else {
11875        return false;
11876    };
11877    let Some(arguments) = template_type.child_by_field_name("arguments") else {
11878        return false;
11879    };
11880    let Some(return_type) = return_error.named_child(0) else {
11881        return false;
11882    };
11883    let mut cursor = template_type.walk();
11884    let template_errors = template_type
11885        .named_children(&mut cursor)
11886        .filter(|child| child.kind() == "ERROR")
11887        .collect::<Vec<_>>();
11888    let [comment_error] = template_errors.as_slice() else {
11889        return false;
11890    };
11891    let mut cursor = comment_error.walk();
11892    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
11893    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
11894        return false;
11895    };
11896    same_node(*body_node, body)
11897        && template_type.kind() == "template_type"
11898        && template_name.kind() == "type_identifier"
11899        && matches!(
11900            node_text(template_name, source).trim(),
11901            "public" | "private" | "protected"
11902        )
11903        && arguments.kind() == "template_argument_list"
11904        && arguments.named_child_count() > 0
11905        && !arguments.has_error()
11906        && !colon.is_named()
11907        && colon.kind() == ":"
11908        && comments.iter().all(|child| child.kind() == "comment")
11909        && !template_keyword.is_named()
11910        && template_keyword.kind() == "template"
11911        && return_error.kind() == "ERROR"
11912        && return_error.named_child_count() == 1
11913        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
11914        && extract_function_declarator(*declarator)
11915            .and_then(cpp_function_declarator_name_node)
11916            .is_some()
11917}
11918
11919/// Return the constructor declaration tree-sitter can merge into an access
11920/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
11921/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
11922/// declaration's apparent type; the callable name must still exactly match the
11923/// recovered class, so unrelated labeled statements are never re-owned.
11924fn cpp_reparsed_preprocessor_constructor<'tree>(
11925    node: Node<'tree>,
11926    class_name: &str,
11927    source: &str,
11928) -> Option<Node<'tree>> {
11929    if node.kind() != "labeled_statement" {
11930        return None;
11931    }
11932    let mut cursor = node.walk();
11933    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11934    let [label, directive_error, declaration] = named.as_slice() else {
11935        return None;
11936    };
11937    if label.kind() != "statement_identifier"
11938        || !matches!(
11939            node_text(*label, source),
11940            "public" | "private" | "protected"
11941        )
11942        || directive_error.kind() != "ERROR"
11943        || directive_error.child_count() != 1
11944        || directive_error
11945            .child(0)
11946            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
11947        || declaration.kind() != "declaration"
11948        || declaration.named_child_count() != 2
11949    {
11950        return None;
11951    }
11952    let apparent_type = declaration.child_by_field_name("type")?;
11953    if apparent_type.kind() != "type_identifier"
11954        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
11955    {
11956        return None;
11957    }
11958    let declarator = declaration.child_by_field_name("declarator")?;
11959    let function = extract_function_declarator(declarator)?;
11960    let name = cpp_function_declarator_name_node(function)?;
11961    (node_text(name, source) == class_name).then_some(*declaration)
11962}
11963
11964fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
11965    if extract_function_declarator(node)
11966        .and_then(cpp_function_declarator_name_node)
11967        .is_some()
11968    {
11969        return true;
11970    }
11971    node.kind() == "init_declarator"
11972        && node
11973            .child_by_field_name("declarator")
11974            .is_some_and(|declarator| declarator.kind() == "identifier")
11975        && node
11976            .child_by_field_name("value")
11977            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
11978}
11979
11980/// Return true for the constrained/attribute form that tree-sitter splits into
11981/// an ERROR declaration, a preprocessor `requires` clause, and a following
11982/// compound statement. The three nodes must remain immediate named siblings;
11983/// this deliberately does not search source text or skip unrelated statements.
11984fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
11985    if node.kind() != "ERROR" || node.named_child_count() != 3 {
11986        return false;
11987    }
11988    let mut cursor = node.walk();
11989    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11990    let [type_node, function_declarator, attribute] = named.as_slice() else {
11991        return false;
11992    };
11993    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
11994        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
11995        || attribute.kind() != "identifier"
11996        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
11997    {
11998        return false;
11999    }
12000    let Some(preproc) =
12001        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
12002    else {
12003        return false;
12004    };
12005    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
12006        .filter(|sibling| sibling.kind() == "compound_statement")
12007    else {
12008        return false;
12009    };
12010    let Some(open) = body.child(0) else {
12011        return false;
12012    };
12013    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
12014        return false;
12015    };
12016    let Some(condition) = preproc.child_by_field_name("condition") else {
12017        return false;
12018    };
12019    let mut cursor = preproc.walk();
12020    let payload = preproc
12021        .named_children(&mut cursor)
12022        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
12023        .collect::<Vec<_>>();
12024    let [requires_statement] = payload.as_slice() else {
12025        return false;
12026    };
12027    let requires_clause = requires_statement.named_child(0);
12028
12029    open.kind() == "{"
12030        && !open.is_missing()
12031        && close.kind() == "}"
12032        && !close.is_missing()
12033        && close.end_byte() == body.end_byte()
12034        && requires_statement.kind() == "expression_statement"
12035        && requires_statement.named_child_count() == 1
12036        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
12037}
12038
12039fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
12040    let mut sibling = node.next_named_sibling();
12041    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
12042        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
12043    }
12044    sibling
12045}
12046
12047fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
12048    let mut sibling = node.prev_named_sibling();
12049    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
12050        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
12051    }
12052    sibling
12053}
12054
12055fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
12056    let Some(preproc) =
12057        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
12058    else {
12059        return false;
12060    };
12061    let Some(error) =
12062        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
12063    else {
12064        return false;
12065    };
12066    cpp_reparsed_attribute_requires_error(error, source)
12067}
12068
12069fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
12070    node: Node<'tree>,
12071    source: &str,
12072) -> Option<Node<'tree>> {
12073    if node.kind() != "ERROR" {
12074        return None;
12075    }
12076    let mut cursor = node.walk();
12077    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12078    let [parameter, macro_name, message] = named.as_slice() else {
12079        return None;
12080    };
12081    let parameter_name = parameter.named_child(0)?;
12082    (parameter.kind() == "type_parameter_declaration"
12083        && parameter_name.kind() == "type_identifier"
12084        && macro_name.kind() == "type_identifier"
12085        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
12086        && message.kind() == "string_literal")
12087        .then_some(parameter_name)
12088}
12089
12090/// Recognize the alternate constraint-macro prefix where tree-sitter retains
12091/// the complete qualified constraint as a fourth child instead of moving it
12092/// into the following function. Keep the gate tied to a two-type template
12093/// constraint that names the declared type parameter.
12094fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
12095    node: Node<'tree>,
12096    source: &str,
12097) -> Option<Node<'tree>> {
12098    if node.kind() != "ERROR" {
12099        return None;
12100    }
12101    let mut cursor = node.walk();
12102    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12103    let [parameter, macro_name, message, constraint] = named.as_slice() else {
12104        return None;
12105    };
12106    let parameter_name = parameter.named_child(0)?;
12107    let constraint_scope = constraint.child_by_field_name("scope")?;
12108    let constraint_template = constraint.child_by_field_name("name")?;
12109    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
12110    let mut argument_cursor = constraint_arguments.walk();
12111    let constraint_types = constraint_arguments
12112        .named_children(&mut argument_cursor)
12113        .collect::<Vec<_>>();
12114    if parameter.kind() != "type_parameter_declaration"
12115        || parameter_name.kind() != "type_identifier"
12116        || macro_name.kind() != "type_identifier"
12117        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
12118        || message.kind() != "string_literal"
12119        || constraint.kind() != "qualified_identifier"
12120        || constraint_scope.kind() != "namespace_identifier"
12121        || !matches!(
12122            constraint_template.kind(),
12123            "template_function" | "template_type"
12124        )
12125        || !matches!(constraint_types.as_slice(), [left, right]
12126            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
12127        || constraint_arguments.has_error()
12128    {
12129        return None;
12130    }
12131    let parameter_text = node_text(parameter_name, source);
12132    let mut stack = constraint_types;
12133    while let Some(current) = stack.pop() {
12134        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
12135            return Some(parameter_name);
12136        }
12137        let mut cursor = current.walk();
12138        stack.extend(current.named_children(&mut cursor));
12139    }
12140    None
12141}
12142
12143fn cpp_reparsed_template_macro_companion_is_indexable(
12144    node: Node<'_>,
12145    parameter_name: Node<'_>,
12146    source: &str,
12147) -> bool {
12148    let Some(body) = cpp_reparsed_member_function_body(node) else {
12149        return false;
12150    };
12151    let mut cursor = node.walk();
12152    let named = node
12153        .named_children(&mut cursor)
12154        .filter(|child| child.kind() != "comment")
12155        .collect::<Vec<_>>();
12156    let [
12157        constraint,
12158        close_error,
12159        storage,
12160        return_error,
12161        declarator,
12162        body_node,
12163    ] = named.as_slice()
12164    else {
12165        return false;
12166    };
12167    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
12168        return false;
12169    };
12170    let Some(constraint_template) = constraint.child_by_field_name("name") else {
12171        return false;
12172    };
12173    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
12174        return false;
12175    };
12176    let Some(return_type) = return_error.named_child(0) else {
12177        return false;
12178    };
12179    let mut cursor = constraint_arguments.walk();
12180    let constraint_types = constraint_arguments
12181        .named_children(&mut cursor)
12182        .collect::<Vec<_>>();
12183    same_node(*body_node, body)
12184        && constraint.kind() == "qualified_identifier"
12185        && constraint_scope.kind() == "namespace_identifier"
12186        && constraint_template.kind() == "template_type"
12187        && matches!(constraint_types.as_slice(), [left, right]
12188            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
12189        && !constraint_arguments.has_error()
12190        && close_error.kind() == "ERROR"
12191        && close_error.named_child_count() == 0
12192        && storage.kind() == "storage_class_specifier"
12193        && return_error.kind() == "ERROR"
12194        && return_error.named_child_count() == 1
12195        && return_type.kind() == "identifier"
12196        && node_text(return_type, source) == node_text(parameter_name, source)
12197        && extract_function_declarator(*declarator)
12198            .and_then(cpp_function_declarator_name_node)
12199            .is_some()
12200}
12201
12202fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
12203    node: Node<'tree>,
12204    parameter_name: Node<'_>,
12205    source: &str,
12206) -> Option<Node<'tree>> {
12207    let body = cpp_reparsed_member_function_body(node)?;
12208    let constraint = node.child_by_field_name("type")?;
12209    let constraint_template = constraint.child_by_field_name("name")?;
12210    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
12211    let mut argument_cursor = constraint_arguments.walk();
12212    let constraint_types = constraint_arguments
12213        .named_children(&mut argument_cursor)
12214        .collect::<Vec<_>>();
12215    if constraint.kind() != "qualified_identifier"
12216        || constraint_template.kind() != "template_type"
12217        || !matches!(constraint_types.as_slice(), [left, right]
12218            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
12219        || constraint_arguments.has_error()
12220        || node
12221            .child_by_field_name("body")
12222            .is_none_or(|candidate| !same_node(candidate, body))
12223    {
12224        return None;
12225    }
12226
12227    let mut cursor = node.walk();
12228    let recovery_errors = node
12229        .named_children(&mut cursor)
12230        .filter(|child| child.kind() == "ERROR")
12231        .collect::<Vec<_>>();
12232    if !recovery_errors
12233        .iter()
12234        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
12235        || !recovery_errors.iter().all(|error| {
12236            error.named_child_count() == 0
12237                || cpp_reparsed_constraint_macro_error(*error, source)
12238                || (error.named_child_count() == 1
12239                    && error
12240                        .named_child(0)
12241                        .is_some_and(|child| child.kind() == "function_declarator"))
12242        })
12243    {
12244        return None;
12245    }
12246
12247    let parameter_text = node_text(parameter_name, source);
12248    let mut declarators = node
12249        .child_by_field_name("declarator")
12250        .and_then(extract_function_declarator)
12251        .into_iter()
12252        .collect::<Vec<_>>();
12253    for error in recovery_errors {
12254        let mut stack = vec![error];
12255        while let Some(current) = stack.pop() {
12256            if current.kind() == "function_declarator" {
12257                declarators.push(current);
12258            }
12259            let mut cursor = current.walk();
12260            stack.extend(current.named_children(&mut cursor));
12261        }
12262    }
12263    declarators.into_iter().find(|declarator| {
12264        cpp_function_declarator_name_node(*declarator)
12265            .is_some_and(|name| name.kind() == "identifier")
12266            && declarator
12267                .child_by_field_name("parameters")
12268                .is_some_and(|parameters| {
12269                    parameters
12270                        .named_children(&mut parameters.walk())
12271                        .filter_map(|parameter| parameter.child_by_field_name("type"))
12272                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
12273                })
12274    })
12275}
12276
12277fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
12278    node: Node<'_>,
12279    parameter_name: Node<'_>,
12280    source: &str,
12281) -> bool {
12282    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
12283}
12284
12285fn cpp_reparsed_template_macro_function_companion_is_indexable(
12286    node: Node<'_>,
12287    parameter_name: Node<'_>,
12288    source: &str,
12289) -> bool {
12290    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
12291        return false;
12292    }
12293    let Some(return_type) = node.child_by_field_name("type") else {
12294        return false;
12295    };
12296    let Some(function_declarator) = node
12297        .child_by_field_name("declarator")
12298        .and_then(extract_function_declarator)
12299    else {
12300        return false;
12301    };
12302    if cpp_function_declarator_name_node(function_declarator).is_none()
12303        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
12304    {
12305        return false;
12306    }
12307    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
12308        return false;
12309    };
12310    let parameter_text = node_text(parameter_name, source);
12311    parameters
12312        .named_children(&mut parameters.walk())
12313        .any(|parameter| {
12314            parameter
12315                .child_by_field_name("type")
12316                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
12317        })
12318}
12319
12320fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
12321    if node.kind() != "ERROR" {
12322        return false;
12323    }
12324    let mut stack = vec![node];
12325    while let Some(current) = stack.pop() {
12326        let macro_shape = match current.kind() {
12327            "call_expression" => current
12328                .child_by_field_name("function")
12329                .zip(current.child_by_field_name("arguments")),
12330            "init_declarator" => current
12331                .child_by_field_name("declarator")
12332                .zip(current.child_by_field_name("value")),
12333            _ => None,
12334        };
12335        if let Some((name, arguments)) = macro_shape
12336            && name.kind() == "identifier"
12337            && arguments.kind() == "argument_list"
12338            && arguments.named_child_count() >= 2
12339            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
12340        {
12341            return true;
12342        }
12343        let mut cursor = current.walk();
12344        stack.extend(current.named_children(&mut cursor));
12345    }
12346    false
12347}
12348
12349fn cpp_recovered_template_macro_constructor<'tree>(
12350    node: Node<'tree>,
12351    source: &str,
12352) -> Option<(Node<'tree>, Node<'tree>)> {
12353    let mut prefix = node.prev_named_sibling()?;
12354    while prefix.kind() == "comment" {
12355        prefix = prefix.prev_named_sibling()?;
12356    }
12357    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
12358    let parameter = parameter_name
12359        .parent()
12360        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
12361    let declarator =
12362        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
12363    Some((declarator, parameter))
12364}
12365
12366fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
12367    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
12368        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
12369            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
12370                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
12371                    function,
12372                    parameter_name,
12373                    source,
12374                )
12375        });
12376    }
12377    let Some(parameter_name) =
12378        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
12379    else {
12380        return false;
12381    };
12382    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
12383        cpp_reparsed_template_macro_function_companion_is_indexable(
12384            function,
12385            parameter_name,
12386            source,
12387        )
12388    })
12389}
12390
12391fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
12392    let function_name = node
12393        .child_by_field_name("declarator")
12394        .and_then(extract_function_declarator)
12395        .and_then(cpp_function_declarator_name_node);
12396    if let Some(body) = cpp_reparsed_member_function_body(node)
12397        && function_name.is_some()
12398        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
12399    {
12400        return true;
12401    }
12402    cpp_reparsed_attribute_member_function(node, source)
12403        || cpp_reparsed_friend_function_is_indexable(node, source)
12404        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
12405        || cpp_reparsed_access_template_function_is_indexable(node, source)
12406        || cpp_recovered_template_macro_constructor(node, source).is_some()
12407}
12408
12409/// Recognize the three top-level nodes produced when an unknown attribute
12410/// macro separates an inline member's declarator from its body in a reparsed
12411/// class interior: an errorful declaration with a missing semicolon, the macro
12412/// call expression, and the complete compound body. Their adjacency and exact
12413/// structured shapes prove one recoverable member envelope; arbitrary calls or
12414/// blocks do not pass this gate.
12415fn cpp_reparsed_macro_attribute_member_sequence(
12416    children: &[Node<'_>],
12417    index: usize,
12418    source: &str,
12419) -> bool {
12420    let Some(prefix) = children.get(index).copied() else {
12421        return false;
12422    };
12423    let declaration = if prefix.kind() == "labeled_statement" {
12424        prefix
12425            .named_child(prefix.named_child_count().saturating_sub(1))
12426            .filter(|child| child.kind() == "declaration")
12427    } else {
12428        (prefix.kind() == "declaration").then_some(prefix)
12429    };
12430    let Some(declaration) = declaration else {
12431        return false;
12432    };
12433    if !declaration.has_error()
12434        || declaration
12435            .child_by_field_name("declarator")
12436            .and_then(extract_function_declarator)
12437            .and_then(cpp_function_declarator_name_node)
12438            .is_none()
12439    {
12440        return false;
12441    }
12442    let Some(attribute_statement) = children.get(index + 1).copied() else {
12443        return false;
12444    };
12445    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
12446        .then(|| attribute_statement.named_child(0))
12447        .flatten()
12448        .filter(|child| child.kind() == "call_expression")
12449    else {
12450        return false;
12451    };
12452    let Some(attribute_name) = attribute_call
12453        .child_by_field_name("function")
12454        .filter(|function| function.kind() == "identifier")
12455        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
12456    else {
12457        return false;
12458    };
12459    if !cpp_export_macro_token(&attribute_name) {
12460        return false;
12461    }
12462    let Some(body) = children.get(index + 2).copied() else {
12463        return false;
12464    };
12465    body.kind() == "compound_statement"
12466        && body.child(0).is_some_and(|open| open.kind() == "{")
12467        && body
12468            .child(body.child_count().saturating_sub(1))
12469            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
12470        && declaration.end_byte() <= attribute_statement.start_byte()
12471        && attribute_statement.end_byte() <= body.start_byte()
12472}
12473
12474fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
12475    let mut cursor = root.walk();
12476    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
12477    let mut saw_member = false;
12478    let mut index = 0;
12479    while index < children.len() {
12480        let child = children[index];
12481        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
12482            saw_member = true;
12483            index += 3;
12484            continue;
12485        }
12486        if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
12487            let Some(tree) = cpp_reparse_fragmented_class_body(
12488                source,
12489                fragmented.reparse_start,
12490                fragmented.reparse_end,
12491            ) else {
12492                return false;
12493            };
12494            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
12495                return false;
12496            }
12497            saw_member = true;
12498            index += 1;
12499            while index < children.len()
12500                && children[index].end_byte() <= fragmented.class_range.end_byte
12501            {
12502                index += 1;
12503            }
12504            continue;
12505        }
12506        match child.kind() {
12507            "comment" => {}
12508            "labeled_statement" => saw_member = true,
12509            "function_definition" => {
12510                if child.has_error()
12511                    && !cpp_reparsed_member_function_is_indexable(child, source)
12512                    && cpp_sentinel_macro_region(child, source).is_none()
12513                {
12514                    return false;
12515                }
12516                saw_member = true;
12517            }
12518            "ERROR"
12519                if (cpp_reparsed_member_error_is_indexable(child)
12520                    || cpp_reparsed_adjacent_copy_control_error(child, source))
12521                    && (child
12522                        .next_named_sibling()
12523                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
12524                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
12525            {
12526                saw_member = true;
12527            }
12528            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
12529                saw_member = true;
12530            }
12531            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
12532                saw_member = true;
12533            }
12534            "expression_statement"
12535                if cpp_is_stray_semicolon(child, source)
12536                    && child.prev_named_sibling().is_some_and(|error| {
12537                        cpp_reparsed_member_error_is_indexable(error)
12538                            || cpp_reparsed_adjacent_copy_control_error(error, source)
12539                    }) =>
12540            {
12541                saw_member = true;
12542            }
12543            "compound_statement"
12544                if cpp_reparsed_constructor_body_is_indexable(child, source)
12545                    || cpp_reparsed_attribute_requires_body(child, source) =>
12546            {
12547                saw_member = true;
12548            }
12549            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
12550            _ => return false,
12551        }
12552        index += 1;
12553    }
12554    saw_member
12555}
12556
12557/// Detect the malformed constructor shape that tree-sitter exposes as an
12558/// access-label statement followed by initializer-looking declarations. The
12559/// declarations are not class members: visiting their `location(loc)` and
12560/// `string(s)` function declarators would publish synthetic functions. The
12561/// export-class fallback keeps the original sibling nodes and therefore avoids
12562/// this parser artifact. The returned range identifies the real constructor
12563/// header, which can be reparsed independently as a structured declarator.
12564fn cpp_reparsed_synthetic_initializer_constructor_range(
12565    root: Node<'_>,
12566    class_name: &str,
12567    source: &str,
12568    constructor_end: usize,
12569) -> Option<std::ops::Range<usize>> {
12570    let mut stack = {
12571        let mut cursor = root.walk();
12572        root.named_children(&mut cursor).collect::<Vec<_>>()
12573    };
12574    while let Some(current) = stack.pop() {
12575        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
12576            current,
12577            class_name,
12578            source,
12579            constructor_end,
12580        ) {
12581            return Some(range);
12582        }
12583        if current.kind() == "ERROR" {
12584            let mut cursor = current.walk();
12585            stack.extend(current.named_children(&mut cursor));
12586        }
12587    }
12588    None
12589}
12590
12591/// Recover an inline constructor that a function-like export macro makes
12592/// tree-sitter merge with the following overload. In the reparsed class-body
12593/// region, the access label wraps one declaration whose ERROR contains the
12594/// constructor declarator and its base-initializer/body, while the declaration's
12595/// ordinary declarator is the following overload. Every boundary below comes
12596/// from that CST; no source syntax is reparsed by hand.
12597fn cpp_reparsed_merged_inline_constructor<'tree>(
12598    root: Node<'tree>,
12599    class_name: &str,
12600    source: &str,
12601) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
12602    let mut stack = vec![root];
12603    while let Some(current) = stack.pop() {
12604        if current.kind() != "labeled_statement" {
12605            let mut cursor = current.walk();
12606            stack.extend(current.named_children(&mut cursor));
12607            continue;
12608        }
12609        let declaration = current
12610            .named_children(&mut current.walk())
12611            .find(|child| child.kind() == "declaration")?;
12612        if declaration
12613            .child_by_field_name("type")
12614            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
12615        {
12616            continue;
12617        }
12618        let following = declaration
12619            .child_by_field_name("declarator")
12620            .and_then(extract_function_declarator)
12621            .and_then(cpp_function_declarator_name_node);
12622        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
12623            continue;
12624        }
12625        let mut declaration_cursor = declaration.walk();
12626        let Some(error) = declaration
12627            .named_children(&mut declaration_cursor)
12628            .find(|child| child.kind() == "ERROR")
12629        else {
12630            continue;
12631        };
12632        let mut error_cursor = error.walk();
12633        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
12634        let Some(constructor) = error_children.iter().copied().find(|child| {
12635            child.kind() == "function_declarator"
12636                && cpp_function_declarator_name_node(*child)
12637                    .is_some_and(|name| node_text(name, source).trim() == class_name)
12638        }) else {
12639            continue;
12640        };
12641        let Some(body) = error_children.iter().copied().find_map(|child| {
12642            (child.kind() == "init_declarator")
12643                .then(|| child.child_by_field_name("value"))
12644                .flatten()
12645                .filter(|value| value.kind() == "initializer_list")
12646        }) else {
12647            continue;
12648        };
12649        if constructor.end_byte() > body.start_byte() {
12650            continue;
12651        }
12652        return Some((constructor.start_byte()..body.end_byte(), body));
12653    }
12654    None
12655}
12656
12657fn cpp_reparsed_synthetic_initializer_constructor(
12658    node: Node<'_>,
12659    class_name: &str,
12660    source: &str,
12661    constructor_end: usize,
12662) -> Option<std::ops::Range<usize>> {
12663    if node.kind() != "labeled_statement" {
12664        return None;
12665    }
12666    let mut cursor = node.walk();
12667    let named = node
12668        .named_children(&mut cursor)
12669        .filter(|child| child.kind() != "comment")
12670        .collect::<Vec<_>>();
12671    let label = named.first()?;
12672    if label.kind() != "statement_identifier"
12673        || !matches!(
12674            node_text(*label, source).trim(),
12675            "public" | "private" | "protected"
12676        )
12677    {
12678        return None;
12679    }
12680    let call_error_index = named.iter().position(|child| {
12681        if child.kind() != "ERROR" {
12682            return false;
12683        }
12684        let mut stack = vec![*child];
12685        while let Some(current) = stack.pop() {
12686            if current.kind() == "call_expression"
12687                && current
12688                    .child_by_field_name("function")
12689                    .is_some_and(|function| {
12690                        function.kind() == "identifier"
12691                            && node_text(function, source).trim() == class_name
12692                    })
12693            {
12694                return true;
12695            }
12696            let mut cursor = current.walk();
12697            stack.extend(current.named_children(&mut cursor));
12698        }
12699        false
12700    })?;
12701    let constructor_call = {
12702        let mut stack = vec![named[call_error_index]];
12703        let mut found = None;
12704        while let Some(current) = stack.pop() {
12705            if current.kind() == "call_expression"
12706                && current
12707                    .child_by_field_name("function")
12708                    .is_some_and(|function| {
12709                        function.kind() == "identifier"
12710                            && node_text(function, source).trim() == class_name
12711                    })
12712            {
12713                found = Some(current);
12714                break;
12715            }
12716            let mut cursor = current.walk();
12717            stack.extend(current.named_children(&mut cursor));
12718        }
12719        found
12720    };
12721    let constructor_call = constructor_call?;
12722    named.iter().skip(call_error_index + 1).find(|child| {
12723        child.kind() == "declaration" && child.has_error() && {
12724            let mut cursor = child.walk();
12725            child.named_children(&mut cursor).any(|declarator| {
12726                declarator.kind() == "init_declarator"
12727                    && declarator
12728                        .child_by_field_name("declarator")
12729                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
12730                    && declarator
12731                        .child_by_field_name("value")
12732                        .is_some_and(|value| value.kind() == "initializer_list")
12733            })
12734        }
12735    })?;
12736    Some(constructor_call.start_byte()..constructor_end)
12737}
12738
12739fn cpp_reparsed_exact_constructor_declarator<'tree>(
12740    root: Node<'tree>,
12741    start: usize,
12742    class_name: &str,
12743    source: &str,
12744) -> Option<Node<'tree>> {
12745    let mut candidate = None;
12746    let mut stack = vec![root];
12747    while let Some(current) = stack.pop() {
12748        if current.kind() == "function_declarator"
12749            && current.start_byte() == start
12750            && cpp_function_declarator_name_node(current)
12751                .is_some_and(|name| node_text(name, source).trim() == class_name)
12752        {
12753            if candidate.is_some() {
12754                return None;
12755            }
12756            candidate = Some(current);
12757            continue;
12758        }
12759        let mut cursor = current.walk();
12760        stack.extend(current.named_children(&mut cursor));
12761    }
12762    candidate
12763}
12764
12765fn cpp_is_indexable_item_kind(kind: &str) -> bool {
12766    matches!(
12767        kind,
12768        "namespace_definition"
12769            | "class_specifier"
12770            | "struct_specifier"
12771            | "union_specifier"
12772            | "enum_specifier"
12773            | "function_definition"
12774            | "template_declaration"
12775            | "declaration"
12776            | "field_declaration"
12777            | "alias_declaration"
12778            | "static_assert_declaration"
12779            | "type_definition"
12780            | "using_declaration"
12781            | "linkage_specification"
12782            | "preproc_def"
12783            | "preproc_function_def"
12784            | "preproc_include"
12785            | "preproc_if"
12786            | "preproc_ifdef"
12787            | "preproc_call"
12788    )
12789}
12790
12791#[cfg(test)]
12792mod tests {
12793    use super::*;
12794    use crate::adapter::parse_cpp_file;
12795    use brokk_bifrost_core::analyzer::parsed_file::{
12796        finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
12797        start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
12798    };
12799    use std::fmt::Write;
12800
12801    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
12802        let mut parser = tree_sitter::Parser::new();
12803        parser
12804            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12805            .unwrap();
12806        let tree = parser.parse(source, None).unwrap();
12807        let file = ProjectFile::new(std::env::temp_dir(), name);
12808        parse_cpp_file(&file, source, &tree)
12809    }
12810
12811    #[test]
12812    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
12813        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
12814        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
12815        let mut macros = parsed
12816            .declarations()
12817            .iter()
12818            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
12819            .collect::<Vec<_>>();
12820        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
12821
12822        assert_eq!(macros.len(), 2, "{macros:#?}");
12823        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
12824        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
12825        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
12826        assert_eq!(
12827            parsed.declaration_ranges(macros[1])[0].start_byte,
12828            source.rfind("#define VALUE 2").expect("second definition")
12829        );
12830    }
12831
12832    #[test]
12833    fn identifies_export_macro_class_base_displaced_into_declarator() {
12834        let source = r#"#define PROJECT_API_
12835namespace project {
12836namespace internal {
12837template <typename T>
12838class Base {};
12839}
12840template <typename T>
12841class Wrapper;
12842template <>
12843class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
12844}
12845"#;
12846        let mut parser = tree_sitter::Parser::new();
12847        parser
12848            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12849            .unwrap();
12850        let tree = parser.parse(source, None).unwrap();
12851        let start = source.find("internal::Base<int>").expect("base");
12852        let mut base = tree
12853            .root_node()
12854            .descendant_for_byte_range(start, start + 8)
12855            .expect("base syntax");
12856        while base.kind() != "qualified_identifier" {
12857            base = base.parent().expect("qualified base ancestor");
12858        }
12859        assert!(
12860            is_recovered_exported_class_base_type_node(base, source),
12861            "{}",
12862            tree.root_node().to_sexp()
12863        );
12864    }
12865
12866    #[test]
12867    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
12868        let source = r#"namespace control {
12869template <typename T>
12870class AnySpan;
12871template <typename T>
12872class ABSL_ATTRIBUTE_VIEW AnySpan {
12873 public:
12874  int begin() const;
12875};
12876}
12877
12878namespace absl {
12879ABSL_NAMESPACE_BEGIN
12880template <typename T>
12881class ABSL_ATTRIBUTE_VIEW Span {
12882 public:
12883  int begin() const;
12884  int back() const;
12885};
12886
12887int begin();
12888int back();
12889}
12890"#;
12891        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
12892        let declarations = parsed.declarations();
12893        assert!(
12894            declarations
12895                .iter()
12896                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
12897        );
12898        for method in ["begin", "back"] {
12899            assert!(declarations.iter().any(|unit| {
12900                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
12901            }));
12902            assert!(
12903                declarations.iter().any(|unit| {
12904                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
12905                })
12906            );
12907        }
12908        assert!(
12909            declarations
12910                .iter()
12911                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
12912        );
12913        assert!(
12914            declarations
12915                .iter()
12916                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
12917        );
12918        assert!(
12919            declarations
12920                .iter()
12921                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
12922        );
12923    }
12924
12925    #[test]
12926    fn explicit_global_member_definition_has_canonical_package_boundary() {
12927        let source = r#"
12928namespace arangodb::aql {
12929class ExecutionPlan {
12930 public:
12931  template<class... Args> Node* createNode(Args&&... args);
12932};
12933}
12934
12935template<class... Args>
12936Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
12937"#;
12938        let parsed = parse_cpp_declarations(source, "global-member.cpp");
12939
12940        assert!(parsed.declarations().iter().any(|unit| {
12941            unit.is_function()
12942                && unit.package_name() == "arangodb::aql"
12943                && unit.short_name() == "ExecutionPlan.createNode"
12944                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
12945        }));
12946    }
12947
12948    #[test]
12949    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
12950        let source = r#"
12951#ifndef TINYXML2_INCLUDED
12952#define TINYXML2_INCLUDED
12953namespace tinyxml2 {
12954class TINYXML2_LIB XMLUtil {
12955 public:
12956  static const char* SkipWhiteSpace(const char* p) {
12957    while (*p) {
12958      if (*p == ' ') {
12959        ++p;
12960      }
12961    }
12962    return p;
12963  }
12964  static bool StringEqual(const char* p, const char* q) {
12965    return p == q;
12966  }
12967  class TINYXML2_LIB Helper {
12968   public:
12969    void Touch();
12970  };
12971  static void ToStr(int value, char* buffer);
12972 private:
12973  static const char* writeBoolTrue;
12974};
12975
12976class TINYXML2_LIB XMLNode {
12977 public:
12978  virtual XMLNode* ShallowClone() const = 0;
12979  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
12980};
12981}
12982#endif
12983"#;
12984        let mut parser = tree_sitter::Parser::new();
12985        parser
12986            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12987            .unwrap();
12988        let tree = parser.parse(source, None).unwrap();
12989        let mut boundary_found = false;
12990        walk_named_tree_preorder(tree.root_node(), true, |node| {
12991            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
12992                && name == "XMLUtil"
12993            {
12994                boundary_found = fragmented_export_sibling_class_boundary(node, source)
12995                    .and_then(|boundary| {
12996                        recover_exported_class_function_definition(boundary, source)
12997                    })
12998                    .is_some_and(|(_, name, _)| name == "XMLNode");
12999            }
13000            WalkControl::Continue
13001        });
13002        assert!(
13003            boundary_found,
13004            "fixture must exercise the recovered sibling boundary"
13005        );
13006
13007        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
13008        assert!(
13009            parsed
13010                .declarations()
13011                .iter()
13012                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
13013            "{:#?}",
13014            parsed.declarations()
13015        );
13016        assert!(
13017            parsed
13018                .declarations()
13019                .iter()
13020                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
13021            "{:#?}",
13022            parsed.declarations()
13023        );
13024        assert!(parsed.declarations().iter().any(|unit| {
13025            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
13026        }));
13027        assert!(
13028            parsed
13029                .declarations()
13030                .iter()
13031                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
13032        );
13033        assert!(
13034            parsed
13035                .declarations()
13036                .iter()
13037                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
13038        );
13039    }
13040
13041    #[test]
13042    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
13043        // Clang's diagnostic suite intentionally contains this ill-formed
13044        // spelling. The analyzer must retain the parser's explicit-global AST
13045        // boundary instead of constructing `cwg311::::cwg311::X`.
13046        let parsed = parse_cpp_declarations(
13047            r#"
13048namespace cwg311 {
13049namespace X { namespace Y {} }
13050namespace ::cwg311::X {}
13051}
13052"#,
13053            "explicit-global-namespace.cpp",
13054        );
13055
13056        assert!(parsed.declarations().iter().any(|unit| {
13057            unit.kind() == CodeUnitType::Module
13058                && unit.short_name() == "cwg311::X"
13059                && unit.fq_name() == "cwg311::X"
13060        }));
13061        assert!(
13062            parsed
13063                .declarations()
13064                .iter()
13065                .all(|unit| !unit.short_name().contains("::::")),
13066            "recovered namespace names must not retain empty scope components: {:#?}",
13067            parsed.declarations()
13068        );
13069    }
13070
13071    #[test]
13072    fn repeated_scope_separator_does_not_create_empty_function_owner() {
13073        let scope = ScopeInfo {
13074            package_name: "X".to_string(),
13075            module: None,
13076            class_unit: None,
13077            template_signature: None,
13078            template_metadata: None,
13079            declarations_are_fields: false,
13080            recovered_specialization_member_scope: false,
13081            visible_using_namespaces: Vec::new(),
13082        };
13083
13084        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
13085
13086        assert!(owner.is_none());
13087        assert_eq!(name, "doit");
13088        assert_eq!(package, "X");
13089    }
13090
13091    #[test]
13092    fn trailing_decltype_expression_is_not_a_function_declarator() {
13093        let source = r#"
13094namespace boost { namespace detail {
13095#if ! defined(BOOST_NO_SFINAE_EXPR) && \
13096    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
13097    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
13098#define BOOST_THREAD_PROVIDES_INVOKE
13099#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
13100template <class Fp, class A0, class ...Args>
13101inline auto
13102invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
13103       BOOST_THREAD_RV_REF(Args) ...args)
13104    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
13105{
13106    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
13107}
13108#endif
13109#endif
13110}}
13111"#;
13112        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
13113
13114        assert!(
13115            parsed
13116                .declarations()
13117                .iter()
13118                .all(|unit| unit.short_name() != ".*f")
13119        );
13120    }
13121
13122    fn find_class_named<'tree>(
13123        root: Node<'tree>,
13124        source: &str,
13125        expected_name: &str,
13126    ) -> Option<Node<'tree>> {
13127        let mut stack = vec![root];
13128        while let Some(node) = stack.pop() {
13129            if node.kind() == "class_specifier"
13130                && node
13131                    .child_by_field_name("name")
13132                    .is_some_and(|name| node_text(name, source) == expected_name)
13133            {
13134                return Some(node);
13135            }
13136            let mut cursor = node.walk();
13137            stack.extend(node.named_children(&mut cursor));
13138        }
13139        None
13140    }
13141
13142    #[test]
13143    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
13144        let source = r#"EXPORT void definition(struct Value value) {}
13145EXPORT void prototype(struct Value value);
13146"#;
13147        let mut parser = tree_sitter::Parser::new();
13148        parser
13149            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13150            .unwrap();
13151        let tree = parser.parse(source, None).unwrap();
13152        let root = tree.root_node();
13153        let mut cursor = root.walk();
13154        let callables = root
13155            .named_children(&mut cursor)
13156            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
13157            .collect::<Vec<_>>();
13158
13159        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
13160        for callable in callables {
13161            assert!(callable.has_error(), "fixture must exercise error recovery");
13162            assert!(
13163                cpp_sentinel_macro_parts(callable, source).is_none(),
13164                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
13165            );
13166        }
13167    }
13168
13169    #[test]
13170    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
13171        let source = r#"namespace absl {
13172ABSL_NAMESPACE_BEGIN
13173// Generate a floating-point variate conforming to a Beta distribution:
13174template <typename RealType = double>
13175class beta_distribution {
13176 public:
13177  using result_type = RealType;
13178
13179
13180  beta_distribution() : beta_distribution(1) {}
13181
13182  explicit beta_distribution(result_type alpha, result_type beta = 1)
13183      : param_(alpha, beta) {}
13184
13185  explicit beta_distribution(const param_type& p) : param_(p) {}
13186
13187  void reset() {}
13188
13189  // Generating functions
13190  template <typename URBG>
13191  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
13192    return (*this)(g, param_);
13193  }
13194
13195};
13196ABSL_NAMESPACE_END
13197}  // namespace absl
13198"#;
13199        let mut parser = tree_sitter::Parser::new();
13200        parser
13201            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13202            .unwrap();
13203        let tree = parser.parse(source, None).unwrap();
13204        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
13205        let body = namespace
13206            .child_by_field_name("body")
13207            .expect("fixture namespace body");
13208        let sentinel = body.named_child(0).expect("sentinel envelope");
13209        let callable = sentinel
13210            .child_by_field_name("declarator")
13211            .and_then(extract_function_declarator)
13212            .and_then(cpp_function_declarator_name_node)
13213            .expect("preserved callable name");
13214
13215        assert_eq!(sentinel.kind(), "function_definition");
13216        assert_eq!(callable.kind(), "operator_name");
13217        assert!(
13218            cpp_sentinel_macro_parts(sentinel, source).is_some(),
13219            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
13220        );
13221    }
13222
13223    #[test]
13224    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
13225        let source = r#"namespace absl {
13226ABSL_NAMESPACE_BEGIN
13227// absl::discrete_distribution
13228//
13229// A discrete distribution produces random integers i, where 0 <= i < n
13230template <typename IntType = int>
13231class discrete_distribution {
13232 public:
13233  using result_type = IntType;
13234  class param_type {
13235   public:
13236    param_type() { init(); }
13237    template <typename InputIterator>
13238    explicit param_type(InputIterator begin, InputIterator end)
13239        : p_(begin, end) {
13240      init();
13241    }
13242  };
13243  discrete_distribution() : param_() {}
13244  explicit discrete_distribution(const param_type& p) : param_(p) {}
13245};
13246ABSL_NAMESPACE_END
13247}  // namespace absl
13248"#;
13249        let mut parser = tree_sitter::Parser::new();
13250        parser
13251            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13252            .unwrap();
13253        let tree = parser.parse(source, None).unwrap();
13254        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
13255        let body = namespace
13256            .child_by_field_name("body")
13257            .expect("fixture namespace body");
13258        let sentinel = body.named_child(0).expect("sentinel envelope");
13259        let callable = sentinel
13260            .child_by_field_name("declarator")
13261            .and_then(extract_function_declarator)
13262            .and_then(cpp_function_declarator_name_node)
13263            .expect("preserved callable name");
13264
13265        assert_eq!(sentinel.kind(), "function_definition");
13266        assert_eq!(callable.kind(), "identifier");
13267        assert!(
13268            cpp_sentinel_macro_parts(sentinel, source).is_some(),
13269            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
13270        );
13271    }
13272
13273    #[test]
13274    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
13275        let source = r#"
13276#define CPPCHECKLIB
13277class Library {
13278    struct Container {
13279        CPPCHECKLIB static std::string toString(Yield yield);
13280        CPPCHECKLIB static std::string toString(Action action);
13281    };
13282};
13283"#;
13284        let mut parser = tree_sitter::Parser::new();
13285        parser
13286            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13287            .unwrap();
13288        let tree = parser.parse(source, None).unwrap();
13289        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
13290        let parsed = parse_cpp_file(&file, source, &tree);
13291        assert!(
13292            parsed
13293                .declarations()
13294                .iter()
13295                .all(|unit| unit.fq_name() != "Library$Container.std"),
13296            "the qualified return-type namespace must not become a field: {:#?}",
13297            parsed.declarations()
13298        );
13299        for expected in ["(Yield)", "(Action)"] {
13300            assert!(
13301                parsed.declarations().iter().any(|unit| {
13302                    unit.is_function()
13303                        && unit.fq_name() == "Library$Container.toString"
13304                        && unit.signature() == Some(expected)
13305                }),
13306                "recovered toString overload {expected} is missing: {:#?}",
13307                parsed.declarations()
13308            );
13309        }
13310    }
13311
13312    #[test]
13313    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
13314        let source = r#"
13315#define SIMPLECPP_LIB
13316namespace simplecpp {
13317using TokenString = std::string;
13318struct Location { int line{}; };
13319class SIMPLECPP_LIB Token {
13320  TokenString prefix;
13321  void prefix_method() {}
13322 public:
13323  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
13324      whitespaceahead(wsahead), location(loc), string(s)
13325      // The comment must not hide the constructor body from recovery.
13326      {
13327      flags();
13328  }
13329  TokenString string;
13330  bool whitespaceahead;
13331  Location location;
13332  Token *previous{};
13333 private:
13334  void flags() {
13335      whitespaceahead = true;
13336  }
13337};
13338}
13339"#;
13340        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
13341
13342        let location_fields = parsed
13343            .declarations()
13344            .iter()
13345            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
13346            .collect::<Vec<_>>();
13347        assert_eq!(
13348            location_fields.len(),
13349            1,
13350            "location should have one class-owned declaration: {:#?}",
13351            parsed.declarations()
13352        );
13353        assert!(
13354            location_fields[0].is_field(),
13355            "location has wrong kind: {:#?}",
13356            parsed.declarations()
13357        );
13358        assert!(
13359            parsed.declarations().iter().all(|unit| {
13360                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
13361            })
13362        );
13363        assert!(
13364            parsed.declarations().iter().all(|unit| {
13365                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
13366            })
13367        );
13368        assert!(
13369            parsed
13370                .declarations()
13371                .iter()
13372                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
13373        );
13374        assert!(
13375            parsed
13376                .declarations()
13377                .iter()
13378                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
13379            "the recovered class must retain its constructor: {:#?}",
13380            parsed.declarations()
13381        );
13382        assert!(
13383            parsed
13384                .declarations()
13385                .iter()
13386                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
13387        );
13388        assert!(parsed.declarations().iter().any(|unit| {
13389            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
13390        }));
13391        let constructor = parsed
13392            .declarations()
13393            .iter()
13394            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
13395            .expect("recovered constructor");
13396        let constructor_start = source.find("Token(const").expect("constructor start");
13397        let constructor_end = source
13398            .get(
13399                ..source
13400                    .find("  TokenString string;")
13401                    .expect("constructor end"),
13402            )
13403            .expect("constructor slice")
13404            .trim_end()
13405            .len();
13406        assert!(
13407            parsed
13408                .navigation_ranges
13409                .get(constructor)
13410                .is_some_and(|ranges| {
13411                    ranges.iter().any(|range| {
13412                        range.start_byte == constructor_start && range.end_byte == constructor_end
13413                    })
13414                }),
13415            "constructor navigation must span the full body: {:#?}",
13416            parsed.navigation_ranges
13417        );
13418        assert_eq!(
13419            parsed
13420                .signature_metadata
13421                .get(constructor)
13422                .and_then(|metadata| metadata.first())
13423                .and_then(SignatureMetadata::callable_linkage),
13424            Some(CallableLinkage::External)
13425        );
13426        let token_class = parsed
13427            .declarations()
13428            .iter()
13429            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
13430            .expect("recovered Token class");
13431        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
13432        assert!(
13433            parsed
13434                .navigation_ranges
13435                .get(token_class)
13436                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
13437            "class navigation must include the terminating semicolon: {:#?}",
13438            parsed.navigation_ranges
13439        );
13440    }
13441
13442    #[test]
13443    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
13444        let source = r#"
13445#define SIMPLECPP_LIB
13446namespace simplecpp {
13447using TokenString = std::string;
13448class Macro;
13449struct Location {
13450  unsigned int fileIndex{};
13451  unsigned int line{};
13452  unsigned int col{};
13453};
13454struct Output {
13455  int type;
13456};
13457class SIMPLECPP_LIB Token {
13458 public:
13459  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
13460      whitespaceahead(wsahead), location(loc), string(s) {
13461      flags();
13462  }
13463  Token(const Token &tok) :
13464      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
13465      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
13466      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
13467  Token &operator=(const Token &tok) = delete;
13468  const TokenString& str() const { return string; }
13469  void setstr(const std::string &s) { string = s; flags(); }
13470  bool isOneOf(const char ops[]) const;
13471  TokenString macro;
13472  char op;
13473  bool comment;
13474  bool name;
13475  bool number;
13476  bool whitespaceahead;
13477  Location location;
13478  Token *previous{};
13479  Token *next{};
13480 private:
13481  void flags() {
13482      name = !string.empty();
13483      comment = false;
13484      number = false;
13485      op = 0;
13486  }
13487  TokenString string;
13488};
13489}
13490struct Following {
13491  int type;
13492};
13493class SIMPLECPP_LIB Later {
13494 public:
13495  Later(int value) : value(value) {}
13496  int value;
13497};
13498"#;
13499        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
13500        assert!(
13501            parsed
13502                .declarations()
13503                .iter()
13504                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
13505        );
13506        assert!(
13507            !parsed
13508                .declarations()
13509                .iter()
13510                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
13511        );
13512        assert!(
13513            parsed
13514                .declarations()
13515                .iter()
13516                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
13517        );
13518        assert!(
13519            !parsed
13520                .declarations()
13521                .iter()
13522                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
13523        );
13524        assert!(
13525            parsed
13526                .declarations()
13527                .iter()
13528                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
13529        );
13530        assert!(
13531            parsed
13532                .declarations()
13533                .iter()
13534                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
13535        );
13536        assert!(
13537            parsed
13538                .declarations()
13539                .iter()
13540                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
13541        );
13542        assert!(
13543            parsed
13544                .declarations()
13545                .iter()
13546                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
13547        );
13548        assert!(
13549            parsed
13550                .declarations()
13551                .iter()
13552                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
13553        );
13554        assert!(
13555            parsed
13556                .declarations()
13557                .iter()
13558                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
13559        );
13560        assert!(parsed.declarations().iter().all(|unit| {
13561            !matches!(
13562                unit.fq_name().as_str(),
13563                "simplecpp.Token.Following" | "simplecpp.Token.Later"
13564            )
13565        }));
13566        assert!(
13567            !parsed
13568                .declarations()
13569                .iter()
13570                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
13571            "the following struct must remain outside the recovered Token class"
13572        );
13573    }
13574
13575    #[test]
13576    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
13577        let source = r#"
13578#define SIMPLECPP_LIB
13579namespace {
13580namespace simplecpp {
13581using TokenString = std::string;
13582struct Location { int line{}; };
13583class SIMPLECPP_LIB HiddenToken {
13584 public:
13585  HiddenToken(const TokenString &s, const Location &loc) :
13586      location(loc), string(s) {
13587      flags();
13588  }
13589  TokenString string;
13590  Location location;
13591  HiddenToken *previous{};
13592 private:
13593  void flags() {}
13594};
13595}
13596}
13597"#;
13598        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
13599        let constructor = parsed
13600            .declarations()
13601            .iter()
13602            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
13603            .expect("recovered anonymous-namespace constructor");
13604        assert_eq!(
13605            parsed
13606                .signature_metadata
13607                .get(constructor)
13608                .and_then(|metadata| metadata.first())
13609                .and_then(SignatureMetadata::callable_linkage),
13610            Some(CallableLinkage::Internal)
13611        );
13612    }
13613
13614    #[test]
13615    fn macro_qualified_static_field_keeps_real_declarator() {
13616        let source = r#"#define JSON_INLINE_VARIABLE
13617struct Reader {
13618static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
13619static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
13620static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
13621};"#;
13622        let mut parser = tree_sitter::Parser::new();
13623        parser
13624            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13625            .unwrap();
13626        let tree = parser.parse(source, None).unwrap();
13627        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
13628        let parsed = parse_cpp_file(&file, source, &tree);
13629        for expected in [
13630            "Reader.npos",
13631            "Reader.other",
13632            "Reader.pointer",
13633            "Reader.reference",
13634        ] {
13635            assert!(
13636                parsed
13637                    .declarations()
13638                    .iter()
13639                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
13640                "real macro-decorated field {expected} is missing: {:#?}",
13641                parsed.declarations()
13642            );
13643        }
13644        assert!(
13645            parsed
13646                .declarations()
13647                .iter()
13648                .all(|unit| unit.fq_name() != "Reader.std"),
13649            "qualified type prefix became a pseudo-field: {:#?}",
13650            parsed.declarations()
13651        );
13652        let root = tree.root_node();
13653        let mut stack = vec![root];
13654        let mut signatures = Vec::new();
13655        while let Some(current) = stack.pop() {
13656            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
13657            {
13658                signatures.extend(
13659                    declarators
13660                        .into_iter()
13661                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
13662                );
13663            }
13664            let mut cursor = current.walk();
13665            stack.extend(current.named_children(&mut cursor));
13666        }
13667        signatures.sort();
13668        assert_eq!(
13669            signatures,
13670            [
13671                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
13672                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
13673                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
13674                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
13675            ]
13676        );
13677    }
13678
13679    fn member_function_linkage(source: &str) -> CallableLinkage {
13680        let mut parser = tree_sitter::Parser::new();
13681        parser
13682            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13683            .unwrap();
13684        let tree = parser.parse(source, None).unwrap();
13685        let ancestry = ParentIndex::new(tree.root_node());
13686        let mut stack = vec![tree.root_node()];
13687        while let Some(node) = stack.pop() {
13688            if node.kind() == "function_definition" {
13689                let mut current = node.parent();
13690                while let Some(parent) = current {
13691                    if matches!(
13692                        parent.kind(),
13693                        "class_specifier" | "struct_specifier" | "union_specifier"
13694                    ) {
13695                        return cpp_callable_linkage(node, source, &ancestry);
13696                    }
13697                    current = parent.parent();
13698                }
13699            }
13700            let mut cursor = node.walk();
13701            stack.extend(node.named_children(&mut cursor));
13702        }
13703        panic!("fixture has no member function definition");
13704    }
13705
13706    #[test]
13707    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
13708        assert_eq!(
13709            member_function_linkage("struct Named { int method() { return 1; } };"),
13710            CallableLinkage::External
13711        );
13712        assert_eq!(
13713            member_function_linkage(
13714                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
13715            ),
13716            CallableLinkage::Internal
13717        );
13718        assert_eq!(
13719            member_function_linkage("struct { int method() { return 1; } } instance;"),
13720            CallableLinkage::Internal
13721        );
13722        assert_eq!(
13723            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
13724            CallableLinkage::Internal
13725        );
13726    }
13727
13728    #[test]
13729    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
13730        let source = r#"
13731#ifndef PROTON_VALUE_HPP
13732#define PROTON_VALUE_HPP
13733namespace proton {
13734namespace internal {
13735class value_base {
13736  protected:
13737    internal::data& data();
13738    internal::data data_;
13739  friend class codec::encoder;
13740  friend class codec::decoder;
13741};
13742}
13743class value : public internal::value_base, private internal::comparable<value> {
13744  private:
13745    template<class T, class U=void> struct assignable :
13746        public std::enable_if<codec::is_encodable<T>::value, U> {};
13747    template<class U> struct assignable<value, U> {};
13748  public:
13749    PN_CPP_EXTERN value();
13750    PN_CPP_EXTERN value(const value&);
13751    PN_CPP_EXTERN value& operator=(const value&);
13752    PN_CPP_EXTERN value(value&&);
13753    PN_CPP_EXTERN value& operator=(value&&);
13754    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
13755    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
13756        codec::encoder e(*this);
13757        e << x;
13758        return *this;
13759    }
13760    PN_CPP_EXTERN type_id type() const;
13761    PN_CPP_EXTERN bool empty() const;
13762    PN_CPP_EXTERN void clear();
13763    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
13764    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
13765  friend PN_CPP_EXTERN void swap(value&, value&);
13766  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
13767  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
13768  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
13769    value(pn_data_t* d);
13770    void reset(pn_data_t* d = 0);
13771};
13772}
13773#endif
13774"#;
13775        let mut parser = tree_sitter::Parser::new();
13776        parser
13777            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13778            .unwrap();
13779        let tree = parser.parse(source, None).unwrap();
13780        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
13781        let parsed = parse_cpp_file(&file, source, &tree);
13782        let macro_constructors = parsed
13783            .signature_metadata
13784            .iter()
13785            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
13786            .flat_map(|(_, metadata)| metadata)
13787            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
13788            .collect::<Vec<_>>();
13789
13790        assert_eq!(
13791            macro_constructors.len(),
13792            3,
13793            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
13794            parsed.declarations()
13795        );
13796        assert!(
13797            macro_constructors.iter().all(|metadata| {
13798                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
13799            }),
13800            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
13801        );
13802    }
13803
13804    #[test]
13805    fn recovered_export_class_typedef_uses_displaced_alias_name() {
13806        let source = r#"
13807namespace spi {
13808class Filter {
13809public:
13810    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
13811};
13812}
13813namespace filter {
13814class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
13815{
13816public:
13817    typedef spi::Filter BASE_CLASS;
13818    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
13819    BEGIN_LOG4CXX_CAST_MAP()
13820    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
13821    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
13822    END_LOG4CXX_CAST_MAP()
13823    FilterDecision decide() const;
13824};
13825}
13826"#;
13827        let mut parser = tree_sitter::Parser::new();
13828        parser
13829            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13830            .unwrap();
13831        let tree = parser.parse(source, None).unwrap();
13832        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
13833        let parsed = parse_cpp_file(&file, source, &tree);
13834        assert!(
13835            parsed.declarations().iter().any(|unit| {
13836                unit.is_class()
13837                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
13838                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
13839            }),
13840            "the displaced typedef alias must retain its declared name: {:#?}",
13841            parsed.declarations()
13842        );
13843        assert!(
13844            parsed
13845                .declarations()
13846                .iter()
13847                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
13848            "the qualified underlying type must not become a false nested alias: {:#?}",
13849            parsed.declarations()
13850        );
13851    }
13852
13853    #[test]
13854    fn exported_single_base_recovery_uses_displaced_class_name() {
13855        let source = r#"
13856class CORE_EXPORT QgsPoint : public AbstractGeometry
13857{
13858    Q_GADGET
13859
13860    Q_PROPERTY( double x READ x WRITE setX )
13861    Q_PROPERTY( double y READ y WRITE setY )
13862    Q_PROPERTY( double z READ z WRITE setZ )
13863    Q_PROPERTY( double m READ m WRITE setM )
13864
13865  public:
13866#ifndef SIP_RUN
13867    QgsPoint(
13868      double x = std::numeric_limits<double>::quiet_NaN(),
13869      double y = std::numeric_limits<double>::quiet_NaN(),
13870      double z = std::numeric_limits<double>::quiet_NaN(),
13871      double m = std::numeric_limits<double>::quiet_NaN(),
13872      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
13873    );
13874#else
13875    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 )];
13876    % MethodCode
13877    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
13878    {
13879      int state;
13880      sipIsErr = 0;
13881      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
13882      if ( !sipIsErr )
13883      {
13884        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
13885      }
13886      sipReleaseType( p, sipType_QgsPointXY, state );
13887    }
13888    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
13889    {
13890      int state;
13891      sipIsErr = 0;
13892
13893      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
13894      if ( !sipIsErr )
13895      {
13896        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
13897      }
13898      sipReleaseType( p, sipType_QPointF, state );
13899    }
13900    else if (
13901      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
13902      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
13903      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
13904      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
13905    {
13906      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
13907      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
13908      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
13909      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
13910      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
13911      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
13912    }
13913    else // Invalid ctor arguments
13914    {
13915      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
13916      sipIsErr = 1;
13917    }
13918    % End
13919#endif
13920
13921    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
13922    explicit QgsPoint( QPointF p ) SIP_SKIP;
13923    explicit QgsPoint(
13924      Qgis::WkbType wkbType,
13925      double x = std::numeric_limits<double>::quiet_NaN(),
13926      double y = std::numeric_limits<double>::quiet_NaN(),
13927      double z = std::numeric_limits<double>::quiet_NaN(),
13928      double m = std::numeric_limits<double>::quiet_NaN()
13929    ) SIP_SKIP;
13930    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
13931    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
13932    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
13933#ifndef SIP_RUN
13934  private:
13935    bool fuzzyHelper(
13936      double epsilon,
13937      const AbstractGeometry &other,
13938      bool is3DFlag,
13939      bool isMeasureFlag
13940    ) const
13941    {
13942      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
13943    }
13944#endif
13945};
13946class Ordinary : public Base { public: Ordinary(); };
13947class API_EXPORT Plain { public: Plain(); };
13948class API_EXPORT : public Base {};
13949class
13950PN_CPP_CLASS_EXTERN Sender : public Link {
13951    Sender();
13952};
13953class thread_ctx_t {};
13954class ctx_t ZMQ_FINAL : public thread_ctx_t {
13955    bool start();
13956};
13957"#;
13958        let mut parser = tree_sitter::Parser::new();
13959        parser
13960            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13961            .unwrap();
13962        let tree = parser.parse(source, None).unwrap();
13963        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
13964        let parsed = parse_cpp_file(&file, source, &tree);
13965        let declarations = parsed.declarations();
13966
13967        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
13968            assert!(
13969                declarations
13970                    .iter()
13971                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
13972                "missing recovered class {expected}: {declarations:#?}"
13973            );
13974        }
13975        let qgs_point = declarations
13976            .iter()
13977            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
13978            .expect("recovered QgsPoint class");
13979        assert_eq!(
13980            parsed.raw_supertypes.get(qgs_point),
13981            Some(&vec!["AbstractGeometry".to_string()]),
13982            "single-base export recovery must retain its displaced base"
13983        );
13984        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
13985        assert!(
13986            parsed
13987                .navigation_ranges
13988                .get(qgs_point)
13989                .is_some_and(|ranges| {
13990                    !ranges.is_empty()
13991                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
13992                }),
13993            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
13994            parsed.navigation_ranges.get(qgs_point)
13995        );
13996        let sender = declarations
13997            .iter()
13998            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
13999            .expect("recovered Sender class");
14000        assert_eq!(
14001            parsed.raw_supertypes.get(sender),
14002            Some(&vec!["Link".to_string()]),
14003            "post-declarator export recovery must retain its displaced base"
14004        );
14005        let ctx = declarations
14006            .iter()
14007            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
14008            .expect("recovered ctx_t class");
14009        assert_eq!(
14010            parsed.raw_supertypes.get(ctx),
14011            Some(&vec!["thread_ctx_t".to_string()]),
14012            "postfix export-macro recovery must retain its displaced base"
14013        );
14014        assert!(
14015            declarations.iter().any(|unit| {
14016                unit.is_function()
14017                    && unit.fq_name() == "QgsPoint.QgsPoint"
14018                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
14019            }),
14020            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
14021        );
14022        assert!(
14023            declarations.iter().all(|unit| {
14024                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
14025            }),
14026            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
14027        );
14028    }
14029
14030    #[test]
14031    fn function_like_export_macro_classes_keep_names_and_base_edges() {
14032        let source = r#"
14033namespace api {
14034class PROJECT_PUBLIC_API(2, 0) Prelude {
14035  public:
14036    Prelude();
14037};
14038class PROJECT_PUBLIC_API(2, 0) Base {
14039  public:
14040    Base(int value);
14041};
14042class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
14043  public:
14044    Derived(int value);
14045};
14046} // namespace api
14047"#;
14048        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
14049        let declarations = parsed.declarations();
14050        let base = declarations
14051            .iter()
14052            .find(|unit| unit.is_class() && unit.fq_name() == "api.Base")
14053            .expect("function-like export macro base class");
14054        let derived = declarations
14055            .iter()
14056            .find(|unit| unit.is_class() && unit.fq_name() == "api.Derived")
14057            .expect("function-like export macro derived class");
14058
14059        assert_eq!(
14060            parsed.raw_supertypes.get(derived),
14061            Some(&vec!["Base".to_string()])
14062        );
14063        assert!(
14064            declarations
14065                .iter()
14066                .all(|unit| unit.fq_name() != "PROJECT_PUBLIC_API"),
14067            "the export macro must not become a declaration: {declarations:#?}"
14068        );
14069        assert!(
14070            parsed
14071                .navigation_ranges
14072                .get(base)
14073                .is_some_and(|ranges| !ranges.is_empty()),
14074            "the recovered base must retain a navigable declaration range"
14075        );
14076    }
14077
14078    #[test]
14079    fn function_like_export_class_survives_a_preceding_malformed_body() {
14080        let source = r#"
14081namespace api {
14082class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
14083   public:
14084      /** Return a descriptive string. */
14085      const char* what() const noexcept override { return m_msg.c_str(); }
14086
14087      /** Return the type of error. */
14088      virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
14089
14090      /** Return an associated error code. */
14091      virtual int error_code() const noexcept { return 0; }
14092
14093      /** Avoid throwing the base directly. */
14094      explicit Exception(std::string_view msg);
14095
14096      /** Avoid throwing the base directly. */
14097      Exception(const char* prefix, std::string_view msg);
14098
14099      /** Avoid throwing the base directly. */
14100      Exception(std::string_view msg, const std::exception& e);
14101
14102   private:
14103      std::string m_msg;
14104};
14105
14106class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
14107   public:
14108      explicit Invalid_Argument(std::string_view msg);
14109
14110      explicit Invalid_Argument(std::string_view msg, std::string_view where);
14111
14112      Invalid_Argument(std::string_view msg, const std::exception& e);
14113
14114      ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
14115};
14116} // namespace api
14117"#;
14118        let mut parser = Parser::new();
14119        parser
14120            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14121            .expect("set C++ grammar");
14122        let tree = parser.parse(source, None).expect("parse fixture");
14123        let mut stack = vec![tree.root_node()];
14124        let mut saw_embedded_shape = false;
14125        while let Some(node) = stack.pop() {
14126            saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
14127                .iter()
14128                .any(|recovered| recovered.name == "Invalid_Argument");
14129            let mut cursor = node.walk();
14130            stack.extend(node.named_children(&mut cursor));
14131        }
14132        assert!(
14133            saw_embedded_shape,
14134            "fixture must retain the embedded error geometry: {}",
14135            tree.root_node().to_sexp()
14136        );
14137
14138        let parsed = parse_cpp_file(
14139            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
14140            source,
14141            &tree,
14142        );
14143        let declarations = parsed.declarations();
14144        let exception = declarations
14145            .iter()
14146            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
14147            .expect("qualified-base export class");
14148        let invalid = declarations
14149            .iter()
14150            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
14151            .expect("class embedded in the preceding malformed body");
14152
14153        assert_eq!(
14154            parsed.raw_supertypes.get(exception),
14155            Some(&vec!["std::exception".to_string()])
14156        );
14157        assert_eq!(
14158            parsed.raw_supertypes.get(invalid),
14159            Some(&vec!["Exception".to_string()])
14160        );
14161        assert!(
14162            parsed.materialization_records.iter().any(|record| matches!(
14163                record,
14164                MaterializationRecord::RecoveredDeclaration { unit, .. }
14165                    if unit == invalid
14166            )),
14167            "the embedded class must retain recovery provenance: {:#?}",
14168            parsed.materialization_records
14169        );
14170    }
14171
14172    #[test]
14173    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
14174        let source = r#"
14175public:
14176   explicit Lookup_Error(std::string_view err) : Exception(err) {}
14177
14178   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
14179"#;
14180        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
14181            .expect("reparse merged constructor body");
14182        let (range, body) =
14183            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
14184                .unwrap_or_else(|| {
14185                    panic!(
14186                        "the merged constructor must retain its structured declarator/body: {}",
14187                        tree.root_node().to_sexp()
14188                    )
14189                });
14190        assert_eq!(
14191            source.get(range).expect("constructor range"),
14192            "Lookup_Error(std::string_view err) : Exception(err) {}"
14193        );
14194        assert_eq!(node_text(body, source), "{}");
14195    }
14196
14197    #[test]
14198    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
14199        let positive_source =
14200            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
14201        let mut parser = tree_sitter::Parser::new();
14202        parser
14203            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14204            .unwrap();
14205        let positive_tree = parser.parse(positive_source, None).unwrap();
14206        assert!(cpp_reparsed_members_are_indexable(
14207            positive_tree.root_node(),
14208            positive_source
14209        ));
14210
14211        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
14212        let negative_tree = parser.parse(negative_source, None).unwrap();
14213        assert!(!cpp_reparsed_members_are_indexable(
14214            negative_tree.root_node(),
14215            negative_source
14216        ));
14217    }
14218
14219    #[test]
14220    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
14221        let copy_control_source = r#"
14222public:
14223    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
14224    explicit Token(const Token* tok);
14225    ~Token();
14226    Token* astOperand1() { return nullptr; }
14227"#;
14228        let constraint_source = r#"
14229private:
14230    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
14231    static T *tokAtImpl(T *tok, int index) {
14232        return tok;
14233    }
14234
14235    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
14236    static T *linkAtImpl(T *tok, int index) {
14237        return tok;
14238    }
14239
14240public:
14241    int late() const { return 1; }
14242"#;
14243        let mut parser = tree_sitter::Parser::new();
14244        parser
14245            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14246            .unwrap();
14247        let copy_control_tree = parser
14248            .parse(copy_control_source, None)
14249            .expect("parse copy-control fixture");
14250        assert!(
14251            copy_control_tree.root_node().has_error(),
14252            "fixture must exercise adjacent copy-control recovery"
14253        );
14254        assert!(
14255            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
14256            "a complete late getter must remain recoverable after adjacent copy-control declarations"
14257        );
14258        let mut cursor = copy_control_tree.root_node().walk();
14259        assert!(
14260            copy_control_tree
14261                .root_node()
14262                .named_children(&mut cursor)
14263                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
14264            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
14265            copy_control_tree.root_node().to_sexp()
14266        );
14267        let constraint_tree = parser
14268            .parse(constraint_source, None)
14269            .expect("parse constraint-macro fixture");
14270        assert!(constraint_tree.root_node().has_error());
14271        assert!(
14272            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
14273            "complete constraint-macro members must not hide a later ordinary member"
14274        );
14275        let mut cursor = constraint_tree.root_node().walk();
14276        assert!(
14277            constraint_tree
14278                .root_node()
14279                .named_children(&mut cursor)
14280                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
14281                    child,
14282                    constraint_source
14283                )),
14284            "fixture must retain the split constraint-macro prefix/function geometry"
14285        );
14286    }
14287
14288    #[test]
14289    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
14290        let source = r#"
14291struct Analyzer {
14292    struct Action {
14293        Action() = default;
14294        Action(const Action&) = default;
14295        Action& operator=(const Action& rhs) & = default;
14296
14297        template<class T,
14298                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
14299                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
14300        // NOLINTNEXTLINE(google-explicit-constructor)
14301        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
14302        {}
14303
14304        enum : std::uint16_t { None = 0, Read = (1 << 0) };
14305        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
14306
14307    private:
14308        unsigned int mFlag{};
14309    };
14310
14311    enum class Direction : unsigned char { Forward, Reverse };
14312    virtual Action analyze(Direction d) const = 0;
14313};
14314"#;
14315        let mut parser = tree_sitter::Parser::new();
14316        parser
14317            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14318            .unwrap();
14319        let tree = parser.parse(source, None).unwrap();
14320        assert!(tree.root_node().has_error());
14321        let root = tree.root_node();
14322        let outer = root
14323            .named_children(&mut root.walk())
14324            .find(|child| child.kind() == "ERROR")
14325            .expect("fragmented Analyzer prefix");
14326        let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
14327            .expect("structured Analyzer fragment boundary");
14328        assert_eq!(outer_name, "Analyzer");
14329        let outer_tree = cpp_reparse_fragmented_class_body(
14330            source,
14331            outer_fragment.reparse_start,
14332            outer_fragment.reparse_end,
14333        )
14334        .expect("reparse Analyzer body");
14335        let outer_root = outer_tree.root_node();
14336        let action_prefix = outer_root
14337            .named_children(&mut outer_root.walk())
14338            .find(|child| child.kind() == "ERROR")
14339            .expect("fragmented Action prefix");
14340        let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
14341            .expect("structured Action fragment boundary");
14342        assert_eq!(action_name, "Action");
14343        let action_tree = cpp_reparse_fragmented_class_body(
14344            source,
14345            action_fragment.reparse_start,
14346            action_fragment.reparse_end,
14347        )
14348        .expect("reparse Action body");
14349        let action_root = action_tree.root_node();
14350        let macro_prefix = action_root
14351            .named_children(&mut action_root.walk())
14352            .find(|child| child.kind() == "ERROR")
14353            .expect("constraint macro prefix");
14354        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
14355            .expect("structured template macro prefix");
14356        let macro_companion =
14357            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
14358        assert!(
14359            cpp_reparsed_template_macro_constructor_companion_is_indexable(
14360                macro_companion,
14361                macro_parameter,
14362                source,
14363            ),
14364            "split constrained constructor must be admitted: {}",
14365            macro_companion.to_sexp()
14366        );
14367        assert!(
14368            cpp_reparsed_members_are_indexable(action_root, source),
14369            "complete Action body must pass the recovery gate: {}",
14370            action_tree.root_node().to_sexp()
14371        );
14372        assert!(
14373            cpp_reparsed_members_are_indexable(outer_root, source),
14374            "complete Analyzer body must pass the recovery gate: {}",
14375            outer_tree.root_node().to_sexp()
14376        );
14377        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
14378        let parsed = parse_cpp_file(&file, source, &tree);
14379        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
14380            assert!(
14381                parsed
14382                    .declarations()
14383                    .iter()
14384                    .any(|unit| unit.fq_name() == expected),
14385                "missing recovered declaration {expected}: {:#?}",
14386                parsed.declarations()
14387            );
14388        }
14389        assert!(
14390            parsed
14391                .declarations()
14392                .iter()
14393                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
14394            "nested members must not remain flattened: {:#?}",
14395            parsed.declarations()
14396        );
14397    }
14398
14399    #[test]
14400    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
14401        let source = r#"
14402raw_hash_set& operator=(raw_hash_set&& that) {
14403  return move_assign(
14404      std::move(that),
14405      typename AllocTraits::propagate_on_container_move_assignment());
14406}
14407
14408iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
14409  return {};
14410}
14411
14412void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
14413
14414iterator insert(const_iterator hint, value_type&& value)
14415    ABSL_ATTRIBUTE_LIFETIME_BOUND {
14416  return {};
14417}
14418
14419friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
14420  return left.size() == right.size();
14421}
14422
14423static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
14424  return static_cast<slot_type*>(buffer);
14425}
14426
14427protected:
14428// Included-range recovery can attach this comment to the template prefix.
14429template <class K>
14430void AssertOnFind([[maybe_unused]] const K& key) {
14431  Check(key);
14432}
14433"#;
14434        let mut parser = tree_sitter::Parser::new();
14435        parser
14436            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14437            .unwrap();
14438        let tree = parser.parse(source, None).unwrap();
14439        assert!(
14440            tree.root_node().has_error(),
14441            "the fixture must exercise tree-sitter's errorful member shapes"
14442        );
14443        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
14444
14445        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
14446        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
14447        assert!(!cpp_reparsed_members_are_indexable(
14448            incomplete_tree.root_node(),
14449            incomplete_source
14450        ));
14451
14452        let outside_error_source = "int foo() stray_attribute {}\n";
14453        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
14454        assert!(outside_error_tree.root_node().has_error());
14455        assert!(!cpp_reparsed_members_are_indexable(
14456            outside_error_tree.root_node(),
14457            outside_error_source
14458        ));
14459
14460        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
14461        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
14462        assert!(!cpp_reparsed_members_are_indexable(
14463            variable_initializer_tree.root_node(),
14464            variable_initializer_source
14465        ));
14466    }
14467
14468    #[test]
14469    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
14470        let positive_source = r#"
14471std::pair<iterator, bool> insert(init_type&& value)
14472    ABSL_ATTRIBUTE_LIFETIME_BOUND
14473#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
14474  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
14475#endif
14476{
14477  return emplace(std::move(value));
14478}
14479"#;
14480        let mut parser = tree_sitter::Parser::new();
14481        parser
14482            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14483            .unwrap();
14484        let positive_tree = parser.parse(positive_source, None).unwrap();
14485        assert!(
14486            positive_tree.root_node().has_error(),
14487            "the fixture must exercise the split attribute/requires shape"
14488        );
14489        assert!(cpp_reparsed_members_are_indexable(
14490            positive_tree.root_node(),
14491            positive_source
14492        ));
14493
14494        let template_return_source = r#"
14495pair<int> insert(init_type&& value)
14496    ABSL_ATTRIBUTE_LIFETIME_BOUND
14497#if LANGUAGE_LEVEL >= 202002L
14498  requires(!Predicate<init_type>::value)
14499#endif
14500// Attributes and the function body may be separated by comments.
14501{
14502  return {};
14503}
14504"#;
14505        let template_return_tree = parser.parse(template_return_source, None).unwrap();
14506        assert!(
14507            cpp_reparsed_members_are_indexable(
14508                template_return_tree.root_node(),
14509                template_return_source
14510            ),
14511            "template-return attribute/requires tree: {}",
14512            template_return_tree.root_node().to_sexp()
14513        );
14514
14515        let no_body_source = r#"
14516std::pair<iterator, bool> insert(init_type&& value)
14517    ABSL_ATTRIBUTE_LIFETIME_BOUND
14518#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
14519  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
14520#endif
14521+ 0;
14522"#;
14523        let no_body_tree = parser.parse(no_body_source, None).unwrap();
14524        assert!(!cpp_reparsed_members_are_indexable(
14525            no_body_tree.root_node(),
14526            no_body_source
14527        ));
14528
14529        let extra_payload_source = r#"
14530pair<int> insert(init_type&& value)
14531    ABSL_ATTRIBUTE_LIFETIME_BOUND
14532#if LANGUAGE_LEVEL >= 202002L
14533  int unrelated;
14534  requires(Predicate<init_type>::value)
14535#endif
14536{
14537  return {};
14538}
14539"#;
14540        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
14541        assert!(!cpp_reparsed_members_are_indexable(
14542            extra_payload_tree.root_node(),
14543            extra_payload_source
14544        ));
14545
14546        let variable_initializer_source = r#"
14547int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
14548#if LANGUAGE_LEVEL >= 202002L
14549  requires(true)
14550#endif
14551{
14552  bad;
14553}
14554"#;
14555        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
14556        assert!(!cpp_reparsed_members_are_indexable(
14557            variable_initializer_tree.root_node(),
14558            variable_initializer_source
14559        ));
14560    }
14561
14562    #[test]
14563    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
14564        let source = r#"namespace absl {
14565ABSL_NAMESPACE_BEGIN namespace container_internal {
14566
14567class raw_hash_set : public Base {
14568 public:
14569  using value_type = int;
14570
14571  template <class U,
14572            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
14573  void insert(U value) { (void)value; }
14574
14575  struct InsertSlot {
14576    raw_hash_set& s;
14577  };
14578};
14579
14580}
14581ABSL_NAMESPACE_END
14582}"#;
14583        let mut parser = tree_sitter::Parser::new();
14584        parser
14585            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14586            .unwrap();
14587        let tree = parser.parse(source, None).unwrap();
14588        let root = tree.root_node();
14589        let outer_namespace = root
14590            .named_children(&mut root.walk())
14591            .find(|child| child.kind() == "namespace_definition")
14592            .expect("outer absl namespace");
14593        let declaration_list = outer_namespace
14594            .child_by_field_name("body")
14595            .expect("outer namespace body");
14596        let sentinel_function = declaration_list
14597            .named_children(&mut declaration_list.walk())
14598            .find(|child| child.kind() == "function_definition")
14599            .expect("malformed namespace sentinel function");
14600        let ancestry = ParentIndex::new(root);
14601        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
14602            .expect("structured nested namespace sentinel");
14603        let fragmented =
14604            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
14605                .expect("fragmented raw_hash_set class");
14606        assert_eq!(fragmented.class_node.kind(), "ERROR");
14607        assert_eq!(fragmented.name, "raw_hash_set");
14608        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
14609
14610        let outer_scope =
14611            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
14612        let mut outer_siblings = Vec::new();
14613        push_cpp_sentinel_sibling_classes(
14614            &mut outer_siblings,
14615            declaration_list,
14616            sentinel.function,
14617            &outer_scope,
14618            source,
14619            &ancestry,
14620        );
14621        let [outer_shadow] = outer_siblings.as_slice() else {
14622            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
14623        };
14624        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
14625        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
14626
14627        let field = "    raw_hash_set& s;";
14628        let start = source.find(field).expect("InsertSlot field") + 4;
14629        let node = root
14630            .descendant_for_byte_range(start, start + "raw_hash_set".len())
14631            .expect("raw_hash_set type node");
14632        let recovered = cpp_sentinel_recovered_classes(root, source);
14633        let [deep_class] = recovered.as_slice() else {
14634            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
14635        };
14636        assert_eq!(
14637            deep_class.namespace_scope_components,
14638            vec!["absl", "container_internal"]
14639        );
14640        assert_eq!(
14641            deep_class.scope_components,
14642            vec!["absl", "container_internal", "raw_hash_set"]
14643        );
14644        assert!(
14645            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
14646                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
14647        );
14648
14649        assert_eq!(
14650            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
14651            Some(vec![
14652                "absl".to_string(),
14653                "container_internal".to_string(),
14654                "raw_hash_set".to_string(),
14655                "InsertSlot".to_string(),
14656            ])
14657        );
14658
14659        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
14660        let parsed = parse_cpp_file(&file, source, &tree);
14661        let raw_hash_set = parsed
14662            .declarations()
14663            .iter()
14664            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
14665            .expect("recovered raw_hash_set class");
14666        assert_eq!(
14667            raw_hash_set.fq_name(),
14668            "absl::container_internal.raw_hash_set",
14669            "the recovered declaration must publish under the deeper sentinel namespace"
14670        );
14671        assert_eq!(
14672            parsed.raw_supertypes.get(raw_hash_set),
14673            Some(&vec!["Base".to_string()]),
14674            "the structured base clause on the fragmented ERROR prefix must survive publication"
14675        );
14676        assert!(
14677            parsed.materialization_records.iter().any(|record| matches!(
14678                record,
14679                MaterializationRecord::RecoveredDeclaration { recovery, unit }
14680                    if unit == raw_hash_set && *recovery == deep_class.class_range
14681            )),
14682            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
14683            parsed.materialization_records
14684        );
14685    }
14686
14687    /// Issue #2358: recording an aggregate definition must not walk the whole
14688    /// file.
14689    ///
14690    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
14691    /// class-like shape that has a body, so the removal step runs once per
14692    /// aggregate. It used to `retain` over `top_level_declarations` and over
14693    /// *every* child list in the file on each of those calls, comparing whole
14694    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
14695    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
14696    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
14697    /// tens of thousands of times over and the C forward differential never
14698    /// finished.
14699    ///
14700    /// A definition the file has not already declared removes nothing, so the
14701    /// honest cost is zero regardless of how many other aggregates surround it.
14702    /// Two sizes an order of magnitude apart pin that the count is not merely
14703    /// small but independent of the file.
14704    ///
14705    /// The declaration walk answers every ancestor question from a
14706    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
14707    /// the root for each one (#2361). Substituting the index is only safe
14708    /// because it answers the identical question, so pin that on the shapes
14709    /// this file's recovery paths care about: anonymous and named aggregates,
14710    /// nested namespaces, templates, macro-displaced declarations and the
14711    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
14712    /// too -- `Node::parent` walks the visible tree, not the named one.
14713    #[test]
14714    fn the_parent_index_answers_what_tree_sitter_answers() {
14715        const SHAPES: [&str; 5] = [
14716            "namespace outer { namespace inner { struct Tag { int field; }; } }",
14717            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
14718            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
14719            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
14720            "class API Broken : public First, public Second {\n  void member();\n",
14721        ];
14722        for source in SHAPES {
14723            let mut parser = tree_sitter::Parser::new();
14724            parser
14725                .set_language(&tree_sitter_cpp::LANGUAGE.into())
14726                .unwrap();
14727            let tree = parser.parse(source, None).unwrap();
14728            let root = tree.root_node();
14729            let ancestry = ParentIndex::new(root);
14730            let mut nodes = 0usize;
14731            let mut stack = vec![root];
14732            while let Some(node) = stack.pop() {
14733                nodes += 1;
14734                assert_eq!(
14735                    node.parent().map(|parent| parent.id()),
14736                    ancestry.parent(node).map(|parent| parent.id()),
14737                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
14738                );
14739                let mut cursor = node.walk();
14740                stack.extend(node.children(&mut cursor));
14741            }
14742            assert!(nodes > 1, "{source:?} produced no tree to compare");
14743        }
14744    }
14745
14746    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
14747    /// index for every ancestor edge. Asking tree-sitter directly makes each
14748    /// edge re-descend from the root, turning declaration extraction on a
14749    /// deeply nested generated header from quadratic output work into a cubic
14750    /// tree walk. Exact query counts pin the route without a machine-dependent
14751    /// wall-clock ceiling.
14752    #[test]
14753    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
14754        const DEPTH: usize = 64;
14755        let mut source = String::new();
14756        for level in 0..DEPTH {
14757            writeln!(source, "namespace n{level} {{").unwrap();
14758        }
14759        source.push_str("int deepest(int value);\n");
14760        for _ in 0..DEPTH {
14761            source.push_str("}\n");
14762        }
14763
14764        let mut parser = tree_sitter::Parser::new();
14765        parser
14766            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14767            .unwrap();
14768        let tree = parser.parse(&source, None).unwrap();
14769        let root = tree.root_node();
14770        let ancestry = ParentIndex::new(root);
14771        let mut function_declarator = None;
14772        walk_named_tree_preorder(root, true, |node| {
14773            if node.kind() == "function_declarator" {
14774                function_declarator = Some(node);
14775                WalkControl::Break
14776            } else {
14777                WalkControl::Continue
14778            }
14779        });
14780        let function_declarator = function_declarator.expect("deepest function declarator");
14781        let ancestor_count =
14782            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
14783
14784        ancestry.reset_parent_query_count_for_test();
14785        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
14786        assert_eq!(DEPTH, lexical_scope.len());
14787        assert_eq!(
14788            ancestor_count + 1,
14789            ancestry.parent_query_count_for_test(),
14790            "lexical-scope ancestry bypassed the parent index"
14791        );
14792
14793        ancestry.reset_parent_query_count_for_test();
14794        assert_eq!(
14795            DispatchExtensibility::Closed,
14796            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
14797        );
14798        assert_eq!(
14799            ancestor_count,
14800            ancestry.parent_query_count_for_test(),
14801            "dispatch ancestry bypassed the parent index"
14802        );
14803
14804        ancestry.reset_parent_query_count_for_test();
14805        assert_eq!(
14806            CallableLinkage::External,
14807            cpp_callable_linkage(function_declarator, &source, &ancestry)
14808        );
14809        assert_eq!(
14810            ancestor_count + 1,
14811            ancestry.parent_query_count_for_test(),
14812            "linkage ancestry bypassed the parent index"
14813        );
14814
14815        ancestry.reset_parent_query_count_for_test();
14816        assert!(!cpp_callable_is_structural_constructor(
14817            function_declarator,
14818            &source,
14819            &ancestry
14820        ));
14821        assert_eq!(
14822            ancestor_count + 1,
14823            ancestry.parent_query_count_for_test(),
14824            "constructor ancestry bypassed the parent index"
14825        );
14826    }
14827
14828    /// Forward declarations followed by definitions are compacted as one
14829    /// batch, without rescanning the shared namespace/top-level lists for each
14830    /// tag. Definitions are intentionally visited in reverse order so the
14831    /// assertion also pins eager remove-and-reappend ordering.
14832    #[test]
14833    fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
14834        for aggregates in [64usize, 512] {
14835            let mut source =
14836                String::from("typedef unsigned long long u64;\nnamespace generated {\n");
14837            for index in 0..aggregates {
14838                writeln!(source, "struct tag{index};").unwrap();
14839            }
14840            for index in (0..aggregates).rev() {
14841                writeln!(
14842                    source,
14843                    "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
14844                )
14845                .unwrap();
14846            }
14847            source.push_str("}\n");
14848
14849            start_code_unit_removal_scan_probe();
14850            let parsed = parse_cpp_declarations(&source, "vmlinux.h");
14851            let scanned = finish_code_unit_removal_scan_probe();
14852
14853            let expected_names: Vec<String> = (0..aggregates)
14854                .rev()
14855                .map(|index| format!("tag{index}"))
14856                .collect();
14857            let top_level_names: Vec<String> = parsed
14858                .top_level_declarations
14859                .iter()
14860                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
14861                .map(|unit| unit.short_name().to_string())
14862                .collect();
14863            let namespace = parsed
14864                .declarations()
14865                .iter()
14866                .find(|unit| {
14867                    unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
14868                })
14869                .expect("generated namespace should be declared");
14870            let child_names: Vec<String> = parsed.children[namespace]
14871                .iter()
14872                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
14873                .map(|unit| unit.short_name().to_string())
14874                .collect();
14875            assert_eq!(
14876                aggregates,
14877                parsed
14878                    .declarations()
14879                    .iter()
14880                    .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
14881                    .count(),
14882                "every aggregate must still be declared at {aggregates} aggregates"
14883            );
14884            assert_eq!(expected_names, top_level_names);
14885            assert_eq!(expected_names, child_names);
14886            assert_eq!(
14887                0, scanned,
14888                "replacing {aggregates} forward declarations must compact their shared lists once"
14889            );
14890        }
14891    }
14892
14893    #[test]
14894    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
14895        const DISTINCT_PER_KIND: usize = 64;
14896        let mut source = String::new();
14897        for index in 0..DISTINCT_PER_KIND {
14898            writeln!(source, "typedef int Alias{index};").unwrap();
14899        }
14900        writeln!(source, "typedef long Alias0;").unwrap();
14901        for index in 0..DISTINCT_PER_KIND {
14902            writeln!(source, "#define MACRO_{index} {index}").unwrap();
14903        }
14904        writeln!(source, "#define MACRO_0 duplicate").unwrap();
14905        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
14906
14907        let mut parser = tree_sitter::Parser::new();
14908        parser
14909            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14910            .unwrap();
14911        let tree = parser.parse(&source, None).unwrap();
14912        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
14913
14914        start_declaration_identity_comparison_probe();
14915        let parsed = parse_cpp_file(&file, &source, &tree);
14916        let comparisons = finish_declaration_identity_comparison_probe();
14917
14918        assert_eq!(
14919            DISTINCT_PER_KIND + 1,
14920            parsed
14921                .declarations()
14922                .iter()
14923                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
14924                .count(),
14925            "every physical typedef alias declaration must be retained so \
14926             conditional branch guards stay available to the resolver"
14927        );
14928        assert_eq!(
14929            DISTINCT_PER_KIND + 1,
14930            parsed
14931                .declarations()
14932                .iter()
14933                .filter(|unit| {
14934                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
14935                })
14936                .count(),
14937            "distinct macro redefinitions must remain available to temporal lookup"
14938        );
14939        assert_eq!(
14940            2,
14941            parsed
14942                .declarations()
14943                .iter()
14944                .filter(|unit| {
14945                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
14946                })
14947                .count(),
14948            "function overloads must remain distinct"
14949        );
14950
14951        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
14952        assert!(
14953            comparisons <= dedup_inputs * 4,
14954            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
14955        );
14956    }
14957
14958    #[test]
14959    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
14960        let source = r#"namespace absl {
14961ABSL_NAMESPACE_BEGIN namespace container_internal {
14962template <typename T>
14963class broken {
14964 public:
14965  using value_type = T;
14966  T operator->() const { return &operator*(); }
14967  using alias = value_type;
14968};
14969}
14970}
14971"#;
14972        let mut parser = tree_sitter::Parser::new();
14973        parser
14974            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14975            .unwrap();
14976        let tree = parser.parse(source, None).unwrap();
14977        let broken = find_class_named(tree.root_node(), source, "broken")
14978            .expect("the positive fixture must expose the broken class node");
14979        assert!(
14980            broken.has_error(),
14981            "the positive fixture must retain an internal parser error"
14982        );
14983        assert!(
14984            cpp_complete_class_body_close(broken).is_some(),
14985            "the positive fixture must expose a real class body close"
14986        );
14987        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
14988        assert!(
14989            recovered.iter().any(|class| {
14990                class.scope_components == ["absl", "container_internal", "broken"]
14991            }),
14992            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
14993        );
14994    }
14995
14996    #[test]
14997    fn sentinel_recovery_keeps_members_after_nested_body_close() {
14998        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
14999NLOHMANN_BASIC_JSON_TPL_DECLARATION
15000class basic_json {
15001 private:
15002  union storage {
15003    int value;
15004  } data;
15005 public:
15006  using late_alias = int;
15007  late_alias value() const;
15008};
15009NLOHMANN_JSON_NAMESPACE_END
15010"#;
15011        let mut parser = tree_sitter::Parser::new();
15012        parser
15013            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15014            .unwrap();
15015        let tree = parser.parse(source, None).unwrap();
15016        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15017        let basic_json = recovered
15018            .iter()
15019            .find(|class| {
15020                class
15021                    .scope_components
15022                    .last()
15023                    .is_some_and(|name| name == "basic_json")
15024            })
15025            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
15026        let late_alias = source
15027            .find("late_alias value")
15028            .expect("late alias reference");
15029        assert!(
15030            basic_json.class_range.start_byte < late_alias
15031                && late_alias < basic_json.class_range.end_byte,
15032            "the recovered class range must include members after a nested close: {basic_json:#?}"
15033        );
15034    }
15035
15036    #[test]
15037    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
15038        let source = r#"namespace absl {
15039ABSL_NAMESPACE_BEGIN namespace container_internal {
15040template <typename T>
15041class broken {
15042 public:
15043  using value_type = T;
15044  T operator->() const { return &operator*(); }
15045}
15046}
15047"#;
15048        let mut parser = tree_sitter::Parser::new();
15049        parser
15050            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15051            .unwrap();
15052        let tree = parser.parse(source, None).unwrap();
15053        let broken = find_class_named(tree.root_node(), source, "broken")
15054            .expect("the negative fixture must expose the malformed class node");
15055        assert!(
15056            broken.has_error(),
15057            "the negative fixture must retain a parser error"
15058        );
15059        assert!(
15060            cpp_complete_class_body_close(broken).is_none(),
15061            "the malformed class must not expose a real body close"
15062        );
15063        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15064        assert!(
15065            recovered
15066                .iter()
15067                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
15068            "an incomplete class must not borrow the namespace close: {recovered:#?}"
15069        );
15070    }
15071
15072    #[test]
15073    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
15074        let source = r#"namespace absl {
15075ABSL_NAMESPACE_BEGIN namespace container_internal {
15076template <typename T>
15077struct broken {
15078  using value_type = T;
15079};
15080}
15081
15082#ifdef OWNER_DEF
15083template <typename T>
15084typename broken<T>::value_type broken<T>::method() {
15085  value_type value{};
15086  return value;
15087}
15088#endif
15089
15090namespace sibling {
15091template <typename T>
15092typename broken<T>::value_type broken<T>::other() {
15093  value_type value{};
15094  return value;
15095}
15096}
15097
15098ABSL_NAMESPACE_END
15099}
15100"#;
15101        let mut parser = tree_sitter::Parser::new();
15102        parser
15103            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15104            .unwrap();
15105        let tree = parser.parse(source, None).unwrap();
15106        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15107        let broken = recovered
15108            .iter()
15109            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
15110            .expect("the sentinel class must be recovered");
15111        let method_start = source
15112            .find("typename broken<T>::value_type broken<T>::method()")
15113            .expect("guarded sibling owner");
15114        let method_end = source[method_start..]
15115            .find("\n}")
15116            .map(|offset| method_start + offset + 2)
15117            .expect("guarded sibling owner close");
15118        assert!(
15119            broken
15120                .owner_ranges
15121                .iter()
15122                .any(|owner| owner.range.start_byte <= method_start
15123                    && method_end <= owner.range.end_byte),
15124            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
15125        );
15126        let sibling_start = source
15127            .find("typename broken<T>::value_type broken<T>::other()")
15128            .expect("nested namespace sibling owner");
15129        assert!(
15130            broken
15131                .owner_ranges
15132                .iter()
15133                .all(|owner| owner.range.start_byte > sibling_start
15134                    || owner.range.end_byte <= sibling_start),
15135            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
15136        );
15137    }
15138
15139    #[test]
15140    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
15141        let source = r#"#ifdef OUTER
15142namespace absl {
15143ABSL_NAMESPACE_BEGIN namespace container_internal {
15144template <typename T>
15145struct broken {
15146  using value_type = T;
15147};
15148}
15149}
15150
15151#ifdef OWNER_DEF
15152template <typename T>
15153typename broken<T>::value_type broken<T>::method() {
15154  value_type value{};
15155  return value;
15156}
15157#endif
15158#endif
15159"#;
15160        let mut parser = tree_sitter::Parser::new();
15161        parser
15162            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15163            .unwrap();
15164        let tree = parser.parse(source, None).unwrap();
15165        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15166        let broken = recovered
15167            .iter()
15168            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
15169            .expect("the sentinel class must be recovered");
15170        let method_start = source
15171            .find("typename broken<T>::value_type broken<T>::method()")
15172            .expect("outer sibling owner");
15173        assert!(
15174            broken
15175                .owner_ranges
15176                .iter()
15177                .all(|owner| owner.range.start_byte > method_start
15178                    || owner.range.end_byte <= method_start),
15179            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
15180        );
15181    }
15182
15183    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
15184    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
15185        let mut signatures = parsed
15186            .declarations()
15187            .iter()
15188            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
15189            .filter_map(|unit| unit.signature().map(str::to_string))
15190            .collect::<Vec<_>>();
15191        signatures.sort();
15192        signatures.dedup();
15193        signatures
15194    }
15195
15196    #[test]
15197    fn callable_parameter_types_come_from_the_ast_parameter_list() {
15198        let source = r#"
15199template <typename T, ENABLE_BYTES(T)>
15200Vec256<T> DupOdd(Vec256<T> value) { return value; }
15201
15202struct Visitor {
15203  void fail(this auto const& self) {}
15204};
15205"#;
15206        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
15207        let dup_odd = parsed
15208            .declarations()
15209            .iter()
15210            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
15211            .expect("DupOdd declaration");
15212        assert_eq!(
15213            dup_odd.signature(),
15214            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
15215        );
15216        assert_eq!(
15217            parsed
15218                .signature_metadata
15219                .get(dup_odd)
15220                .and_then(|metadata| metadata.first())
15221                .and_then(SignatureMetadata::callable_parameter_types),
15222            Some(["Vec256<T>".to_string()].as_slice())
15223        );
15224
15225        let fail = parsed
15226            .declarations()
15227            .iter()
15228            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
15229            .expect("explicit-object member");
15230        assert_eq!(fail.signature(), Some("(const this auto &)"));
15231        let metadata = parsed
15232            .signature_metadata
15233            .get(fail)
15234            .and_then(|metadata| metadata.first())
15235            .expect("explicit-object signature metadata");
15236        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
15237        assert!(
15238            metadata
15239                .callable_arity()
15240                .is_some_and(|arity| arity.accepts(0))
15241        );
15242    }
15243
15244    #[test]
15245    fn trailing_qualifiers_survive_parameter_list_whitespace() {
15246        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
15247        // declarator's structure, so an out-of-line definition that spells its
15248        // parameter list with different whitespace than the declaration must
15249        // still carry it.
15250        let source = r#"
15251struct Widget {
15252  bool multiline(int settings, int supprs) const;
15253  bool doublespace(int settings, int supprs) const;
15254  bool noexcept_multiline(int settings, int supprs) noexcept;
15255  bool ref_multiline(int settings, int supprs) &&;
15256};
15257bool
15258Widget::multiline (int settings,
15259                   int supprs) const
15260{ return settings + supprs > 0; }
15261bool Widget::doublespace(int settings,  int supprs) const { return true; }
15262bool Widget::noexcept_multiline(int settings,
15263                                int supprs) noexcept { return true; }
15264bool Widget::ref_multiline(int settings,
15265                           int supprs) && { return true; }
15266"#;
15267        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
15268        assert_eq!(
15269            vec!["(int, int) const".to_string()],
15270            identity_signatures(&parsed, "Widget.multiline")
15271        );
15272        assert_eq!(
15273            vec!["(int, int) const".to_string()],
15274            identity_signatures(&parsed, "Widget.doublespace")
15275        );
15276        assert_eq!(
15277            vec!["(int, int) noexcept".to_string()],
15278            identity_signatures(&parsed, "Widget.noexcept_multiline")
15279        );
15280        assert_eq!(
15281            vec!["(int, int) &&".to_string()],
15282            identity_signatures(&parsed, "Widget.ref_multiline")
15283        );
15284    }
15285
15286    #[test]
15287    fn macro_fragmented_plain_class_keeps_following_member_signature() {
15288        let source = r#"
15289struct CString {};
15290class CMessage {
15291public:
15292  CString GetParams(unsigned int index, unsigned int length = -1) const
15293      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
15294    return GetParamsColon(index, length);
15295  }
15296  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
15297};
15298CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
15299  return {};
15300}
15301"#;
15302        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
15303        assert_eq!(
15304            vec!["(unsigned int, unsigned int) const".to_string()],
15305            identity_signatures(&parsed, "CMessage.GetParamsColon")
15306        );
15307    }
15308
15309    #[test]
15310    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
15311        let source = r#"
15312#pragma once
15313#define DEMO_DEPRECATED(message)
15314namespace demo {
15315struct Base {
15316    static int aligned(int value) { return value; }
15317    int legacy(int value) const
15318        DEMO_DEPRECATED("use replacement()") { return value; }
15319    int replacement() const;
15320    void run(int value);
15321};
15322struct OtherBase {
15323    void run(int value);
15324    static int aligned(int value) { return value; }
15325};
15326struct Derived : Base {};
15327struct Override : Base {
15328    void run(int value);
15329    static int aligned(int value) { return value; }
15330};
15331struct RecoveredOverride : Base {
15332    int legacy(int value) const
15333        DEMO_DEPRECATED("use replacement()") { return value; }
15334    void run(int value);
15335};
15336struct Hidden : Base {
15337    void run(int first, int second);
15338    static int aligned(int first, int second) { return first + second; }
15339};
15340struct Ambiguous : Base, OtherBase {};
15341}
15342struct Global {};
15343"#;
15344        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
15345        let declarations = parsed.declarations();
15346        let fq_names = declarations
15347            .iter()
15348            .map(|unit| unit.fq_name())
15349            .collect::<std::collections::BTreeSet<_>>();
15350
15351        for expected in [
15352            "demo.Base",
15353            "demo.Base.aligned",
15354            "demo.Base.legacy",
15355            "demo.Base.replacement",
15356            "demo.Base.run",
15357            "demo.Derived",
15358            "demo.OtherBase",
15359            "demo.Override",
15360            "demo.RecoveredOverride",
15361            "demo.Hidden",
15362            "demo.Ambiguous",
15363            "Global",
15364        ] {
15365            assert!(
15366                fq_names.contains(expected),
15367                "missing {expected} from namespaced macro fragment: {declarations:#?}"
15368            );
15369        }
15370        assert!(
15371            !fq_names.contains("Derived"),
15372            "following class escaped its namespace: {declarations:#?}"
15373        );
15374        assert!(
15375            !fq_names.contains("demo.Global"),
15376            "global class crossed the recovered namespace boundary: {declarations:#?}"
15377        );
15378    }
15379
15380    #[test]
15381    fn trailing_qualifiers_still_separate_genuine_overloads() {
15382        // The qualifier must keep distinguishing the real C++ overload sets it
15383        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
15384        let source = r#"
15385struct Widget {
15386  int* slot(int index);
15387  const int* slot(int index) const;
15388  int log(int severity) &;
15389  int log(int severity) &&;
15390};
15391"#;
15392        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
15393        assert_eq!(
15394            vec!["(int)".to_string(), "(int) const".to_string()],
15395            identity_signatures(&parsed, "Widget.slot")
15396        );
15397        assert_eq!(
15398            vec!["(int) &".to_string(), "(int) &&".to_string()],
15399            identity_signatures(&parsed, "Widget.log")
15400        );
15401    }
15402
15403    #[test]
15404    fn virtual_specifier_is_not_part_of_the_identity_signature() {
15405        // `override` never appears on the out-of-line definition, and C++ does
15406        // not make it part of the signature, so it must not split the identity.
15407        let source = r#"
15408struct Base {
15409  virtual void run(int value) const;
15410};
15411struct Widget : Base {
15412  void run(int value) const override;
15413};
15414void Widget::run(int value) const {}
15415"#;
15416        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
15417        assert_eq!(
15418            vec!["(int) const".to_string()],
15419            identity_signatures(&parsed, "Widget.run")
15420        );
15421    }
15422
15423    #[test]
15424    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
15425        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
15426        // the function type, so a declaration that spells `const int` and a
15427        // definition that spells `int` are one entity.
15428        let source = r#"
15429struct Widget {
15430  bool value_params(const int settings, const int supprs);
15431  void pointee_const(const int* p);
15432  void pointer_const(int* const p);
15433  void both_const(const int* const p);
15434  void reference_const(const int& p);
15435  void array_const(const int values[4]);
15436};
15437bool Widget::value_params(int settings, int supprs) { return true; }
15438void Widget::pointer_const(int* p) {}
15439void Widget::both_const(const int* p) {}
15440"#;
15441        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
15442        assert_eq!(
15443            vec!["(int, int)".to_string()],
15444            identity_signatures(&parsed, "Widget.value_params")
15445        );
15446        assert_eq!(
15447            vec!["(int *)".to_string()],
15448            identity_signatures(&parsed, "Widget.pointer_const")
15449        );
15450        assert_eq!(
15451            vec!["(const int *)".to_string()],
15452            identity_signatures(&parsed, "Widget.both_const")
15453        );
15454        // The const that is not top-level still distinguishes the type.
15455        assert_eq!(
15456            vec!["(const int *)".to_string()],
15457            identity_signatures(&parsed, "Widget.pointee_const")
15458        );
15459        assert_eq!(
15460            vec!["(const int &)".to_string()],
15461            identity_signatures(&parsed, "Widget.reference_const")
15462        );
15463        assert_eq!(
15464            vec!["(const int [4])".to_string()],
15465            identity_signatures(&parsed, "Widget.array_const")
15466        );
15467    }
15468
15469    #[test]
15470    fn top_level_parameter_const_still_separates_pointee_overloads() {
15471        let source = r#"
15472struct Widget {
15473  void take(const int* p);
15474  void take(int* p);
15475};
15476"#;
15477        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
15478        assert_eq!(
15479            vec!["(const int *)".to_string(), "(int *)".to_string()],
15480            identity_signatures(&parsed, "Widget.take")
15481        );
15482    }
15483
15484    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
15485        let mut parser = tree_sitter::Parser::new();
15486        parser
15487            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15488            .unwrap();
15489        let tree = parser.parse(source, None).unwrap();
15490        let start = source.find(callable_name).expect("callable declaration");
15491        let declarator =
15492            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
15493        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
15494    }
15495
15496    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
15497        let mut shapes = comparable_shapes(source, callable_name);
15498        assert_eq!(1, shapes.len(), "{shapes:?}");
15499        match shapes.remove(0) {
15500            CppComparableSlot::Shape(shape) => shape,
15501            other => panic!("expected a comparable shape, got {other:?}"),
15502        }
15503    }
15504
15505    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
15506        let mut current = shape.root();
15507        loop {
15508            match shape.node(current) {
15509                CppComparableNode::Named { .. } => return shape.node(current),
15510                CppComparableNode::Pointer { inner, .. }
15511                | CppComparableNode::Reference { inner }
15512                | CppComparableNode::Array { inner } => current = *inner,
15513                CppComparableNode::Generic { base, .. } => current = *base,
15514            }
15515        }
15516    }
15517
15518    #[test]
15519    fn comparable_shape_keeps_pointee_const() {
15520        assert_ne!(
15521            sole_comparable_shape("void f(const char* p);", "f("),
15522            sole_comparable_shape("void f(char* p);", "f(")
15523        );
15524    }
15525
15526    #[test]
15527    fn comparable_shape_keeps_inner_pointer_const() {
15528        assert_ne!(
15529            sole_comparable_shape("void f(int** p);", "f("),
15530            sole_comparable_shape("void f(int* const* p);", "f(")
15531        );
15532    }
15533
15534    #[test]
15535    fn comparable_shape_drops_top_level_pointer_const() {
15536        assert_eq!(
15537            sole_comparable_shape("void f(int* const p);", "f("),
15538            sole_comparable_shape("void f(int* p);", "f(")
15539        );
15540    }
15541
15542    #[test]
15543    fn comparable_shape_drops_top_level_base_const() {
15544        assert_eq!(
15545            sole_comparable_shape("void f(const int p);", "f("),
15546            sole_comparable_shape("void f(int p);", "f(")
15547        );
15548    }
15549
15550    #[test]
15551    fn comparable_shape_decays_top_level_array_to_pointer() {
15552        assert_eq!(
15553            sole_comparable_shape("void f(int a[3]);", "f("),
15554            sole_comparable_shape("void f(int* a);", "f(")
15555        );
15556        assert_eq!(
15557            sole_comparable_shape("void f(int* a[3]);", "f("),
15558            sole_comparable_shape("void f(int** a);", "f(")
15559        );
15560    }
15561
15562    #[test]
15563    fn comparable_shape_keeps_array_behind_pointer() {
15564        assert_ne!(
15565            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
15566            sole_comparable_shape("struct S { void f(int** a); };", "f(")
15567        );
15568    }
15569
15570    #[test]
15571    fn comparable_shape_records_written_name_and_lexical_scope() {
15572        let declared =
15573            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
15574        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
15575        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
15576            panic!("named leaf");
15577        };
15578        assert_eq!(["Msg".to_string()].as_slice(), name.path());
15579        assert_eq!(
15580            ["ns".to_string(), "S".to_string()].as_slice(),
15581            name.lexical_scope()
15582        );
15583        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
15584            panic!("named leaf");
15585        };
15586        assert_eq!(
15587            ["ns".to_string(), "Msg".to_string()].as_slice(),
15588            name.path()
15589        );
15590        assert!(name.lexical_scope().is_empty());
15591        assert_ne!(declared, defined);
15592    }
15593
15594    #[test]
15595    fn comparable_shape_marks_sized_primitive_leaf() {
15596        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
15597        let CppComparableNode::Named {
15598            name, primitive, ..
15599        } = comparable_named_leaf(&shape)
15600        else {
15601            panic!("named leaf");
15602        };
15603        assert!(primitive);
15604        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
15605        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
15606    }
15607
15608    #[test]
15609    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
15610        assert_eq!(
15611            vec![CppComparableSlot::Unstructured],
15612            comparable_shapes("void f(void (*cb)(int));", "f(")
15613        );
15614    }
15615
15616    #[test]
15617    fn comparable_shape_reports_ellipsis_slot() {
15618        let shapes = comparable_shapes("void f(int a, ...);", "f(");
15619        assert_eq!(2, shapes.len(), "{shapes:?}");
15620        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
15621    }
15622
15623    #[test]
15624    fn comparable_shape_keeps_template_argument_const() {
15625        assert_ne!(
15626            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
15627            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
15628        );
15629    }
15630
15631    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
15632    /// file-scope tag that a later `struct inner *` at file scope may name.
15633    #[test]
15634    fn c_file_mints_aggregate_member_tag_at_file_scope() {
15635        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
15636        let parsed = parse_cpp_declarations(source, "x.c");
15637        let declarations = parsed.declarations();
15638
15639        assert!(
15640            declarations
15641                .iter()
15642                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
15643            "expected a file-scope inner tag, got {declarations:?}"
15644        );
15645        assert!(
15646            declarations
15647                .iter()
15648                .all(|unit| unit.fq_name() != "outer$inner"),
15649            "expected no nested identity, got {declarations:?}"
15650        );
15651        assert!(
15652            declarations
15653                .iter()
15654                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
15655        );
15656        // Members still belong to their own aggregate.
15657        assert!(
15658            declarations
15659                .iter()
15660                .any(|unit| unit.fq_name() == "inner.value")
15661        );
15662        assert!(
15663            declarations
15664                .iter()
15665                .any(|unit| unit.fq_name() == "outer.item")
15666        );
15667
15668        let outer = declarations
15669            .iter()
15670            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
15671            .expect("outer");
15672        assert!(
15673            parsed
15674                .children
15675                .get(outer)
15676                .into_iter()
15677                .flatten()
15678                .all(|child| child.fq_name() != "inner"),
15679            "the tag must not hang off the aggregate it is written inside: {:?}",
15680            parsed.children
15681        );
15682    }
15683
15684    /// A header carries no compilation language of its own, and a `.cpp`
15685    /// translation unit really does declare a nested class. Both keep exactly
15686    /// the C++ extraction they had before the C dialect existed.
15687    #[test]
15688    fn header_and_cpp_files_keep_nested_tag_identity() {
15689        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
15690        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
15691            let parsed = parse_cpp_declarations(source, name);
15692            let declarations = parsed.declarations();
15693            assert!(
15694                declarations
15695                    .iter()
15696                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
15697                "{name} must keep the nested identity, got {declarations:?}"
15698            );
15699            assert!(
15700                declarations.iter().all(|unit| unit.fq_name() != "inner"),
15701                "{name} must not mint a file-scope tag, got {declarations:?}"
15702            );
15703            assert!(
15704                declarations
15705                    .iter()
15706                    .any(|unit| unit.fq_name() == "outer$inner.value")
15707            );
15708        }
15709    }
15710
15711    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
15712    #[test]
15713    fn uppercase_c_extension_keeps_cpp_tag_scope() {
15714        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
15715        let parsed = parse_cpp_declarations(source, "x.C");
15716        assert!(
15717            parsed
15718                .declarations()
15719                .iter()
15720                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
15721        );
15722    }
15723
15724    /// There is no such thing as a partially nested tag in C: every level of a
15725    /// nested aggregate chain lands at the same enclosing scope.
15726    #[test]
15727    fn c_file_mints_every_nesting_level_at_file_scope() {
15728        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
15729        let parsed = parse_cpp_declarations(source, "z.c");
15730        let declarations = parsed.declarations();
15731
15732        for tag in ["a", "b", "c"] {
15733            assert!(
15734                declarations
15735                    .iter()
15736                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
15737                "expected a file-scope {tag}, got {declarations:?}"
15738            );
15739        }
15740        assert!(
15741            declarations
15742                .iter()
15743                .all(|unit| !unit.fq_name().contains('$')),
15744            "no level may keep a nested identity, got {declarations:?}"
15745        );
15746        // Each member still belongs to the aggregate that declares it.
15747        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
15748        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
15749        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
15750    }
15751
15752    /// An enum tag is a tag; its enumerators stay members of the enum, which is
15753    /// what makes them ordinary identifiers at the enum's own (file) scope.
15754    #[test]
15755    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
15756        let source = "struct outer { enum color { RED, GREEN } c; };\n";
15757        let parsed = parse_cpp_declarations(source, "e.c");
15758        let declarations = parsed.declarations();
15759
15760        let color = declarations
15761            .iter()
15762            .find(|unit| unit.is_class() && unit.fq_name() == "color")
15763            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
15764        assert!(
15765            declarations
15766                .iter()
15767                .all(|unit| unit.fq_name() != "outer$color")
15768        );
15769        for enumerator in ["color.RED", "color.GREEN"] {
15770            assert!(
15771                declarations.iter().any(|unit| unit.fq_name() == enumerator),
15772                "expected {enumerator}, got {declarations:?}"
15773            );
15774        }
15775        let children = parsed
15776            .children
15777            .get(color)
15778            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
15779        assert!(
15780            ["color.RED", "color.GREEN"]
15781                .iter()
15782                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
15783            "enumerators must hang off their enum: {children:?}"
15784        );
15785    }
15786
15787    #[test]
15788    fn c_file_mints_member_list_union_at_file_scope() {
15789        let source = "struct outer { union inner { int a; float b; } item; };\n";
15790        let parsed = parse_cpp_declarations(source, "u.c");
15791        let declarations = parsed.declarations();
15792        assert!(
15793            declarations
15794                .iter()
15795                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
15796            "expected a file-scope inner union, got {declarations:?}"
15797        );
15798        assert!(
15799            declarations
15800                .iter()
15801                .all(|unit| unit.fq_name() != "outer$inner")
15802        );
15803        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
15804        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
15805    }
15806
15807    /// A tag declared in a namespace member list is not a file-scope tag: the
15808    /// nearest enclosing non-aggregate scope is the namespace.
15809    #[test]
15810    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
15811        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
15812        let parsed = parse_cpp_declarations(source, "n.c");
15813        let declarations = parsed.declarations();
15814        let inner = declarations
15815            .iter()
15816            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
15817            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
15818        assert_eq!(inner.package_name(), "ns");
15819        assert!(
15820            declarations
15821                .iter()
15822                .all(|unit| unit.fq_name() != "ns.outer$inner")
15823        );
15824    }
15825
15826    /// Pins today's treatment of a tag declared inside a function body: the
15827    /// declaration walk does not descend into statement bodies, so no unit is
15828    /// minted for it in either dialect. C block scope is out of scope for the
15829    /// dialect change, and this test proves the change did not disturb it.
15830    #[test]
15831    fn function_local_tags_are_unchanged_in_both_dialects() {
15832        let source =
15833            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
15834        for name in ["y.c", "y.cpp"] {
15835            let parsed = parse_cpp_declarations(source, name);
15836            let declarations = parsed.declarations();
15837            assert!(
15838                declarations
15839                    .iter()
15840                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
15841                "{name}: {declarations:?}"
15842            );
15843            for tag in ["localtag", "deeper", "localtag$deeper"] {
15844                assert!(
15845                    declarations.iter().all(|unit| unit.fq_name() != tag),
15846                    "{name} must not mint {tag}, got {declarations:?}"
15847                );
15848            }
15849        }
15850    }
15851
15852    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
15853    /// re-scope: the typedef name is identical in both dialects.
15854    #[test]
15855    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
15856        let source = "typedef struct { int v; } T;\n";
15857        for name in ["t.c", "t.cpp"] {
15858            let parsed = parse_cpp_declarations(source, name);
15859            let declarations = parsed.declarations();
15860            assert!(
15861                declarations
15862                    .iter()
15863                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
15864                "{name}: {declarations:?}"
15865            );
15866        }
15867    }
15868
15869    #[test]
15870    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
15871        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
15872        let parsed = parse_cpp_declarations(source, "socket.c");
15873        let declarations = parsed.declarations();
15874        assert_eq!(
15875            declarations
15876                .iter()
15877                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
15878                .count(),
15879            1,
15880            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
15881        );
15882        for expected in [
15883            "PAL_HANDLE",
15884            "PAL_HANDLE.sock",
15885            "PAL_HANDLE$sock",
15886            "PAL_HANDLE$sock.ops",
15887        ] {
15888            assert!(
15889                declarations.iter().any(|unit| unit.fq_name() == expected),
15890                "expected {expected}, got {declarations:?}"
15891            );
15892        }
15893    }
15894
15895    /// `class` is not C. Source that spells one in a `.c` file is not C code,
15896    /// so it keeps the C++ reading rather than acquiring a half-C identity.
15897    #[test]
15898    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
15899        let source = "class outer { class inner { int v; }; };\n";
15900        let c_parsed = parse_cpp_declarations(source, "k.c");
15901        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
15902        let c_declarations = c_parsed.declarations();
15903        let cpp_declarations = cpp_parsed.declarations();
15904        assert!(
15905            c_declarations
15906                .iter()
15907                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
15908            "{c_declarations:?}"
15909        );
15910        assert_eq!(
15911            c_declarations
15912                .iter()
15913                .map(|unit| unit.fq_name())
15914                .collect::<std::collections::BTreeSet<_>>(),
15915            cpp_declarations
15916                .iter()
15917                .map(|unit| unit.fq_name())
15918                .collect::<std::collections::BTreeSet<_>>()
15919        );
15920    }
15921}