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::{FqName, SegmentId, SegmentKind, segment_interner};
9use brokk_bifrost_core::analyzer::model::{
10    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
11    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
12    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
13    ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
14    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
15};
16use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
17use brokk_bifrost_core::analyzer::structural::materialization::{
18    GenerationKind, MaterializationRecord,
19};
20use brokk_bifrost_core::analyzer::tree_walk::{WalkControl, walk_named_tree_preorder};
21use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
22use brokk_bifrost_core::hash::{HashMap, HashSet};
23use regex::Regex;
24use tree_sitter::{Node, Parser, Tree};
25
26/// Intern one qualified-name segment in the process-global interner.
27fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
28    segment_interner().intern(text, kind)
29}
30
31/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
32/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
33/// `::` head is exactly the mixed-separator store issue #1163 is about; the
34/// structured form records each namespace component, and the equivalence check
35/// renders it natively (with `::` between adjacent Package segments) so it
36/// round-trips to the legacy string. Splitting the already-joined string here is
37/// the M1 bridge — the legacy strings stay authoritative until M3.
38fn cpp_push_package(fq: &mut FqName, package_name: &str) {
39    for component in package_name.split("::").filter(|c| !c.is_empty()) {
40        fq.push(cpp_segment(component, SegmentKind::Package));
41    }
42}
43
44/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
45/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
46/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
47/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
48/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
49/// cpp-specific native rendering rule is needed for this chain.
50fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
51    let mut first = true;
52    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
53    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
54    // entry point, not re-inference of an already-structured name.
55    for component in chain.split('$').filter(|c| !c.is_empty()) {
56        let kind = if first {
57            SegmentKind::Type
58        } else {
59            SegmentKind::Nested
60        };
61        fq.push(cpp_segment(component, kind));
62        first = false;
63    }
64}
65
66/// Structured name for a C++ namespace module: every `::`-separated component is
67/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
68/// `short_name` with an empty `package_name`).
69fn cpp_namespace_fq(full_name: &str) -> FqName {
70    let mut fq = FqName::new();
71    cpp_push_package(&mut fq, full_name);
72    fq
73}
74
75/// The per-level namespace components a `namespace_definition`'s `name` field
76/// declares.
77///
78/// A C++17 nested definition (`namespace a::b::c`) parses as a
79/// `nested_namespace_specifier` whose named children are the per-level
80/// `namespace_identifier`s plus, for three or more levels, a further
81/// `nested_namespace_specifier`; the `::` separators, the optional per-level
82/// `inline`, and the leading global `::` are all anonymous tokens the walk
83/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
84/// segment path as the expanded `namespace a { namespace b { } }` form.
85///
86/// A shape outside that grammar is the deliberately ill-formed source the
87/// diagnostic corpora carry. Those keep their historical single-component
88/// reading of the raw name text, which the caller still joins to the lexical
89/// namespace exactly as before.
90fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
91    let mut components = Vec::new();
92    let mut stack = vec![node];
93    while let Some(current) = stack.pop() {
94        match current.kind() {
95            "namespace_identifier" | "identifier" => {
96                components.push(normalize_cpp_whitespace(node_text(current, source)));
97            }
98            "nested_namespace_specifier" => {
99                for index in (0..current.named_child_count()).rev() {
100                    stack.push(
101                        current
102                            .named_child(index)
103                            .expect("index below the node's own named child count"),
104                    );
105                }
106            }
107            _ => return cpp_raw_namespace_name_components(node, source),
108        }
109    }
110    if components.iter().any(String::is_empty) {
111        return cpp_raw_namespace_name_components(node, source);
112    }
113    components
114}
115
116/// The historical reading of a namespace name node: its whole source text as
117/// one component, with a leading global `::` marker dropped so the caller's
118/// global-scope handling stays the AST boundary rather than a text prefix.
119fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
120    let start = node
121        .child(0)
122        .filter(|child| !child.is_named() && child.kind() == "::")
123        .map_or(node.start_byte(), |marker| marker.end_byte());
124    let text = normalize_cpp_whitespace(
125        source
126            .get(start..node.end_byte())
127            .expect("namespace name node covers one source range"),
128    );
129    if text.is_empty() {
130        return Vec::new();
131    }
132    vec![text]
133}
134
135/// Return the named namespace path that structurally encloses `node`.
136///
137/// This intentionally follows namespace AST ancestors rather than inspecting
138/// source text. Anonymous namespaces are not representable in the legacy C++
139/// package field, so a path containing one fails closed.
140fn cpp_lexical_namespace_name(node: Node<'_>, source: &str) -> Option<String> {
141    let mut components = Vec::new();
142    let mut ancestor = node.parent();
143    while let Some(current) = ancestor {
144        if current.kind() == "namespace_definition" {
145            let name_node = current.child_by_field_name("name")?;
146            let name = normalize_cpp_whitespace(node_text(name_node, source));
147            if name.is_empty() {
148                return None;
149            }
150            components.push(name);
151        }
152        ancestor = current.parent();
153    }
154    if components.is_empty() {
155        return None;
156    }
157    components.reverse();
158    Some(components.join("::"))
159}
160
161/// Nested-class `$` join for short names. An anonymous parent class (empty
162/// short_name) contributes no segment: the FqName bridge drops empty
163/// components, so a bare `parent$child` join would desync `short_name` from
164/// the fq and trip the package/short boundary assert in
165/// `CodeUnit::with_signature_and_fq` (#2140).
166fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
167    if parent_short.is_empty() {
168        name.to_string()
169    } else {
170        format!("{parent_short}${name}")
171    }
172}
173
174/// Member `.` join for short names; same anonymous-parent guard as
175/// [`cpp_join_nested_short`] (#2140).
176fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
177    if parent_short.is_empty() {
178        name.to_string()
179    } else {
180        format!("{parent_short}.{name}")
181    }
182}
183
184/// Structural fq for a leaf declaration: the parent unit's fq plus this
185/// declaration's own name as one segment (or the package segments plus the
186/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
187/// chain, so a literal `$` inside a source identifier (Cython template
188/// substitution points, gcc `$`-identifiers) survives instead of corrupting
189/// the chain and tripping the package/short boundary assert (#2140).
190fn cpp_leaf_fq(
191    package_name: &str,
192    parent: Option<&CodeUnit>,
193    name: &str,
194    kind_if_nested: SegmentKind,
195    kind_if_top: SegmentKind,
196) -> FqName {
197    if let Some(parent) = parent {
198        parent
199            .fq()
200            .clone()
201            .with_pushed(cpp_segment(name, kind_if_nested))
202    } else {
203        let mut fq = FqName::new();
204        cpp_push_package(&mut fq, package_name);
205        fq.push(cpp_segment(name, kind_if_top));
206        fq
207    }
208}
209
210/// Structured name for a member unit (function, field, enumerator). The
211/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
212/// the member has an owner, by `.member`; free functions and globals have no
213/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
214/// C++ member names never contain a literal `.`, so the single `.` (if any)
215/// separates the owner chain from the member.
216pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
217    let mut fq = FqName::new();
218    cpp_push_package(&mut fq, package_name);
219    match short_name.rsplit_once('.') {
220        Some((owner_chain, member)) => {
221            cpp_push_type_chain(&mut fq, owner_chain);
222            fq.push(cpp_segment(member, SegmentKind::Member));
223        }
224        None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
225    }
226    fq
227}
228
229#[derive(Clone)]
230pub struct ScopeInfo {
231    package_name: String,
232    module: Option<CodeUnit>,
233    class_unit: Option<CodeUnit>,
234    template_signature: Option<String>,
235    template_metadata: Option<CppTemplateMetadata>,
236    declarations_are_fields: bool,
237    recovered_specialization_member_scope: bool,
238    /// Namespace targets of every `using namespace X;` directive lexically
239    /// visible at this point in the file (declaration order), threaded
240    /// forward sibling-by-sibling by the sequential container walk (see
241    /// `CppWork::Siblings`). An out-of-line member definition written as a
242    /// bare `Class::method` at file/namespace scope with no enclosing
243    /// `namespace {}` block (issue #1093, e.g. log4cxx's
244    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
245    /// const { ... }`) has no other structural signal for which namespace
246    /// actually owns `Class`; this is the best-effort candidate list used to
247    /// recover it so the definition's indexed identity matches its header
248    /// declaration's.
249    visible_using_namespaces: Vec<String>,
250}
251
252struct CppContainer<'tree> {
253    node: Node<'tree>,
254    scope: ScopeInfo,
255}
256
257struct CppNodeWork<'tree> {
258    node: Node<'tree>,
259    scope: ScopeInfo,
260}
261
262/// Cursor over one container's remaining named children, processed one at a
263/// time (rather than all at once) so a `using namespace X;` sibling can
264/// update `scope.visible_using_namespaces` for the siblings that follow it,
265/// matching real C++ using-directive semantics. Nested container work is
266/// still pushed and fully drained before the cursor resumes (stack LIFO
267/// order), preserving the original left-to-right visitation order.
268struct CppSiblingsWork<'tree> {
269    children: std::vec::IntoIter<Node<'tree>>,
270    scope: ScopeInfo,
271}
272
273enum CppWork<'tree> {
274    Container(CppContainer<'tree>),
275    Node(CppNodeWork<'tree>),
276    Siblings(CppSiblingsWork<'tree>),
277}
278
279fn class_like_name(node: Node<'_>, source: &str) -> Option<String> {
280    let best = class_like_name_from_children(node, source);
281    if let Some(parent) = node.parent()
282        && matches!(
283            parent.kind(),
284            "declaration" | "field_declaration" | "function_definition"
285        )
286        // A class_specifier carrying its own body proves the grammar name is
287        // the real class name: a sibling declarator then declares an object
288        // (`class X {} x;`), never a displaced class name. The gate matters
289        // when the class name is itself an all-caps token (`X`, `API`) --
290        // without it the export-macro re-read below steals the object
291        // declarator's name for the class (#2283). The genuine export-macro
292        // shapes leave the class_specifier bodyless, the same invariant
293        // recover_malformed_exported_multiple_base_class already gates on.
294        && cpp_body_node(node).is_none()
295        && node
296            .child_by_field_name("name")
297            .map(|name_node| {
298                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
299            })
300            .unwrap_or(false)
301        && let Some(recovered) = exported_class_name_from_node(parent, source)
302        && best.as_deref() != Some(recovered.as_str())
303    {
304        return Some(recovered);
305    }
306    best.or_else(|| {
307        node.child_by_field_name("name")
308            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
309            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
310    })
311}
312
313fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
314    let mut grammar_name = None;
315    if let Some(name_node) = node.child_by_field_name("name") {
316        let name = normalize_cpp_whitespace(node_text(name_node, source));
317        if name.is_empty() {
318            return None;
319        }
320        if !cpp_export_macro_token(&name) {
321            return Some(name);
322        }
323        grammar_name = Some(name);
324    }
325
326    let mut best = None;
327    let mut cursor = node.walk();
328    let mut stack = Vec::new();
329    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
330        if matches!(
331            child.kind(),
332            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
333        ) {
334            break;
335        }
336        stack.push(child);
337    }
338
339    while let Some(current) = stack.pop() {
340        if matches!(current.kind(), "type_identifier" | "identifier") {
341            let name = normalize_cpp_whitespace(node_text(current, source));
342            if !name.is_empty() && !cpp_export_macro_token(&name) {
343                best = Some(name);
344            }
345            continue;
346        }
347
348        for index in (0..current.named_child_count()).rev() {
349            if let Some(child) = current.named_child(index) {
350                stack.push(child);
351            }
352        }
353    }
354    best.or(grammar_name)
355}
356
357pub fn cpp_export_macro_token(token: &str) -> bool {
358    token
359        .chars()
360        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
361}
362
363struct RecoveredExportedClass<'tree> {
364    declaration_node: Node<'tree>,
365    name: String,
366    body: Option<Node<'tree>>,
367    raw_supertypes: Option<Vec<String>>,
368    uses_initializer_body: bool,
369    /// Present only for the fragmented multiple-base export shape (issue #938).
370    /// Carries the true class-body byte region -- the members tree-sitter scattered
371    /// out of the recovered node -- so they can be reparsed and re-owned as members
372    /// rather than lost inside the truncated `initializer_list` stand-in.
373    fragmented_body: Option<FragmentedExportBody>,
374}
375
376/// The recovered class-body geometry for a fragmented multiple-base export class.
377/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
378/// verbatim for a region reparse (issue #941 machinery) so every recovered member
379/// keeps its exact original byte/line position. `class_range` is the full class
380/// navigation range spanning to the displaced closing brace.
381struct FragmentedExportBody {
382    reparse_start: usize,
383    reparse_end: usize,
384    class_range: Range,
385}
386
387struct DisplacedFragmentNamespaceBoundary<'tree> {
388    class_close: Node<'tree>,
389    class_semicolon: Node<'tree>,
390    namespace_items: Vec<Node<'tree>>,
391}
392
393/// Result of validating a reparsed fragmented class body.  A complete tree can
394/// safely consume the whole region.  A partial tree may contain only the exact
395/// class-named constructor that tree-sitter merged into an access label; its
396/// remaining siblings must stay on the ordinary outer walk.
397enum FragmentedExportMembers {
398    Complete(Tree),
399    ConditionalConstructor(Tree),
400}
401
402#[derive(Clone, Copy)]
403struct DisplacedMacroClassTail {
404    split_index: usize,
405    class_range: Range,
406}
407
408fn recover_exported_class_declaration<'tree>(
409    node: Node<'tree>,
410    source: &str,
411) -> Option<RecoveredExportedClass<'tree>> {
412    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
413        return Some(recovered);
414    }
415
416    let class_node = first_class_like_child(node)?;
417    if let Some(name_node) = class_node.child_by_field_name("name") {
418        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
419        if cpp_export_macro_token(&class_name) {
420            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
421            // Name declarator. Only a bare declarator can be the displaced class name;
422            // wrappers describe an object whose type merely happens to look macro-like.
423            let mut cursor = node.walk();
424            if node
425                .children_by_field_name("declarator", &mut cursor)
426                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
427            {
428                return None;
429            }
430        } else if has_direct_cpp_declarator(node) {
431            return None;
432        }
433    }
434    let name = exported_class_name_from_node(class_node, source)?;
435    Some(RecoveredExportedClass {
436        declaration_node: class_node,
437        name,
438        body: cpp_body_node(class_node),
439        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
440            .then(|| extract_cpp_supertypes(class_node, source)),
441        uses_initializer_body: false,
442        fragmented_body: None,
443    })
444}
445
446fn recover_malformed_exported_base_class<'tree>(
447    node: Node<'tree>,
448    source: &str,
449) -> Option<RecoveredExportedClass<'tree>> {
450    if node.kind() != "declaration" {
451        return None;
452    }
453    let class_node = node.child_by_field_name("type")?;
454    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
455        return None;
456    }
457    let macro_name = class_node
458        .child_by_field_name("name")
459        .and_then(|name| direct_identifier_name(name, source))?;
460    if !cpp_export_macro_token(&macro_name) {
461        return None;
462    }
463
464    let mut named_cursor = node.walk();
465    let mut named = node.named_children(&mut named_cursor);
466    if named
467        .next()
468        .is_none_or(|child| !same_node(child, class_node))
469    {
470        return None;
471    }
472    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
473    if displaced.kind() != "ERROR" {
474        return None;
475    }
476    let name = displaced_exported_class_name(displaced, source)?;
477
478    let remaining = named.collect::<Vec<_>>();
479    let init = *remaining.last()?;
480    if init.kind() != "init_declarator" {
481        return None;
482    }
483    let final_base = init
484        .child_by_field_name("declarator")
485        .and_then(|base| recovered_malformed_base_name(base, source))?;
486    let body = init.child_by_field_name("value")?;
487    // A complete reduction has a real closing brace here. In Chromium's Widget
488    // declaration, tree-sitter instead emits the same direct `}` slot as a
489    // zero-width missing node where the first body macro truncates the prefix.
490    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
491        return None;
492    }
493
494    if remaining[..remaining.len() - 1]
495        .iter()
496        .any(|child| match child.kind() {
497            "qualified_identifier"
498            | "scoped_type_identifier"
499            | "type_identifier"
500            | "identifier" => false,
501            "ERROR" => !is_malformed_inheritance_access(*child, source),
502            _ => true,
503        })
504    {
505        return None;
506    }
507
508    let mut raw_supertypes = Vec::new();
509    for base in &remaining[..remaining.len() - 1] {
510        if base.kind() == "ERROR" {
511            continue;
512        }
513        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
514    }
515    raw_supertypes.push(final_base);
516
517    Some(RecoveredExportedClass {
518        declaration_node: node,
519        name,
520        body: Some(body),
521        raw_supertypes: Some(raw_supertypes),
522        uses_initializer_body: true,
523        fragmented_body: fragmented_export_body_region(node, body, source),
524    })
525}
526
527/// Locate the true class-body region for a fragmented multiple-base export class.
528///
529/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
530/// emits in place of the real class body. Tree-sitter reduces that body in one of
531/// two shapes, both of which lose the members from the recovered node:
532///
533/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
534///   a real closing brace and holds the whole body text inline. The interior between
535///   the braces reparses to the members directly.
536/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
537///   first member with a zero-width MISSING `}`; every later member -- and the real
538///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
539///   siblings. The interior runs from the opening brace to that displaced `}`.
540///
541/// Returns the interior byte range to reparse plus the full class navigation range.
542fn fragmented_export_body_region(
543    node: Node<'_>,
544    body: Node<'_>,
545    source: &str,
546) -> Option<FragmentedExportBody> {
547    let reparse_start = body.start_byte() + 1;
548    let close = direct_close_brace(body)?;
549    if close.end_byte() > close.start_byte() {
550        return Some(FragmentedExportBody {
551            reparse_start,
552            reparse_end: close.start_byte(),
553            class_range: cpp_declaration_range(node),
554        });
555    }
556    // The closing brace was displaced past the recovered node. A balanced nested
557    // class keeps its own braces, so the first lone-`}` sibling is this class's.
558    let mut sibling = node.next_named_sibling();
559    let displaced_close = loop {
560        let Some(current) = sibling else {
561            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
562        };
563        if cpp_is_stray_close_brace(current, source) {
564            break current;
565        }
566        sibling = current.next_named_sibling();
567    };
568    Some(FragmentedExportBody {
569        reparse_start,
570        reparse_end: displaced_close.start_byte(),
571        class_range: Range {
572            start_byte: node.start_byte(),
573            end_byte: displaced_close.end_byte(),
574            start_line: node.start_position().row + 1,
575            end_line: displaced_close.end_position().row + 1,
576        },
577    })
578}
579
580/// Locate the true class-body region for the export-macro class shape that
581/// tree-sitter promotes to a `function_definition`.
582///
583/// In this shape the synthetic function body closes at the first inline
584/// method, while the class's real members continue as root-level siblings until
585/// a stray `}` followed by the displaced class `;`. Reparse the complete
586/// interior so those siblings are visited with the recovered class scope.
587fn fragmented_export_function_body_region(
588    node: Node<'_>,
589    body: Node<'_>,
590    source: &str,
591    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
592) -> Option<FragmentedExportBody> {
593    let reparse_start = body.start_byte().checked_add(1)?;
594    if let Some(boundary) = displaced_namespace {
595        return Some(FragmentedExportBody {
596            reparse_start,
597            reparse_end: boundary.class_close.start_byte(),
598            class_range: Range {
599                start_byte: node.start_byte(),
600                end_byte: boundary.class_semicolon.end_byte(),
601                start_line: node.start_position().row + 1,
602                end_line: boundary.class_semicolon.end_position().row + 1,
603            },
604        });
605    }
606    let siblings = cpp_following_named_siblings(node, source);
607    let boundary = fragmented_export_sibling_class_boundary(node, source);
608    let boundary_index = boundary.and_then(|boundary| {
609        siblings
610            .iter()
611            .position(|candidate| same_node(*candidate, boundary))
612    });
613    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
614    let mut sibling_index = 0;
615    // A complete recovered class's synthetic wrapper is immediately followed
616    // by its displaced semicolon (comments and a trailing attribute macro --
617    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
618    // may sit between the body and that semicolon). Only scan for a later
619    // stray close when real member siblings intervene; otherwise every earlier
620    // complete class would borrow the next malformed class's close and claim
621    // its members. The trailing-attribute case is the gtest shape: the scan
622    // borrowed a close ~1900 lines later and re-owned a following
623    // `namespace testing { namespace internal {` block as class members,
624    // doubling the package path ("testing::internal::testing::internal") and
625    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
626    // boundary assert (#2297).
627    while let Some(current) = siblings.get(sibling_index).copied() {
628        if current.kind() == "comment" {
629            sibling_index += 1;
630            continue;
631        }
632        if is_trailing_attribute_macro_sibling(current) {
633            sibling_index += 1;
634            continue;
635        }
636        if cpp_is_stray_semicolon(current, source) {
637            return None;
638        }
639        break;
640    }
641    while let Some(current) = siblings.get(sibling_index).copied() {
642        let next = siblings.get(sibling_index + 1).copied();
643        if cpp_is_stray_close_brace(current, source)
644            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
645        {
646            let semicolon = next.expect("checked above");
647            return Some(FragmentedExportBody {
648                reparse_start,
649                reparse_end: current.start_byte(),
650                class_range: Range {
651                    start_byte: node.start_byte(),
652                    end_byte: semicolon.end_byte(),
653                    start_line: node.start_position().row + 1,
654                    end_line: semicolon.end_position().row + 1,
655                },
656            });
657        }
658        // When the final access label keeps the class close in its malformed
659        // declaration body, tree-sitter nests the lone `}` ERROR below the
660        // label instead of exposing it as a direct sibling. Search only the
661        // scattered siblings after the synthetic wrapper. The first such
662        // close is the class terminator because nested class bodies retain
663        // their own balanced class_specifier nodes.
664        if current.start_byte() >= body.end_byte()
665            && let Some(close) = cpp_nested_stray_close_brace(current, source)
666        {
667            return Some(FragmentedExportBody {
668                reparse_start,
669                reparse_end: close.start_byte(),
670                class_range: Range {
671                    start_byte: node.start_byte(),
672                    end_byte: current.end_byte(),
673                    start_line: node.start_position().row + 1,
674                    end_line: current.end_position().row + 1,
675                },
676            });
677        }
678        sibling_index += 1;
679    }
680    boundary.map(|boundary| FragmentedExportBody {
681        reparse_start,
682        reparse_end: boundary.start_byte(),
683        class_range: Range {
684            start_byte: node.start_byte(),
685            end_byte: boundary.start_byte(),
686            start_line: node.start_position().row + 1,
687            end_line: boundary.start_position().row + 1,
688        },
689    })
690}
691
692/// Find a later macro-export class that tree-sitter lifted through an enclosing
693/// preprocessor container. A class that is still a direct sibling can be a
694/// nested member of the current fragmented class, so only a changed parent is
695/// a proven boundary between the two recovered class envelopes.
696fn fragmented_export_sibling_class_boundary<'tree>(
697    node: Node<'tree>,
698    source: &str,
699) -> Option<Node<'tree>> {
700    let node_parent = node.parent()?;
701    cpp_following_named_siblings(node, source)
702        .into_iter()
703        .find(|candidate| {
704            recover_exported_class_function_definition(*candidate, source).is_some()
705                && candidate
706                    .parent()
707                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
708        })
709}
710
711/// A trailing attribute macro after a recovered class's closing brace, spelled
712/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
713/// bare identifier is never a class member (members need a type), so this
714/// sibling can only be the class's own tail (#2297).
715fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
716    if node.kind() != "expression_statement" {
717        return false;
718    }
719    let mut cursor = node.walk();
720    let mut children = node.named_children(&mut cursor);
721    children
722        .next()
723        .is_some_and(|child| child.kind() == "identifier")
724        && children.next().is_none()
725}
726
727/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
728/// export-class wrapper can place the class close inside an access-label node,
729/// so direct-sibling checks alone miss the boundary.  Walk named CST children
730/// only; the helper does not inspect source text beyond the existing structured
731/// stray-brace predicate.
732fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
733    let mut stack = vec![node];
734    while let Some(current) = stack.pop() {
735        if cpp_is_stray_close_brace(current, source) {
736            return Some(current);
737        }
738        let mut cursor = current.walk();
739        stack.extend(current.named_children(&mut cursor));
740    }
741    None
742}
743
744/// Return named siblings that follow `node`, including siblings that tree-sitter
745/// attached to an enclosing container after malformed recovery split the local
746/// declaration list. Stop at the first structurally visible class close so a
747/// later namespace or exported class cannot supply the recovery boundary.
748fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
749    let mut siblings = Vec::new();
750    let mut anchor = node;
751    while let Some(parent) = anchor.parent() {
752        let at_translation_unit = parent.kind() == "translation_unit";
753        let mut sibling = anchor.next_named_sibling();
754        while let Some(current) = sibling {
755            if at_translation_unit
756                && (current.kind() == "namespace_definition"
757                    || (current.kind() == "function_definition"
758                        && first_class_like_child(current).is_some()))
759            {
760                return siblings;
761            }
762            siblings.push(current);
763            if cpp_is_stray_close_brace(current, source) {
764                if let Some(semicolon) = current
765                    .next_named_sibling()
766                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
767                {
768                    siblings.push(semicolon);
769                }
770                return siblings;
771            }
772            if current.start_byte() >= node.end_byte()
773                && matches!(current.kind(), "ERROR" | "labeled_statement")
774                && cpp_nested_stray_close_brace(current, source).is_some()
775            {
776                return siblings;
777            }
778            sibling = current.next_named_sibling();
779        }
780        anchor = parent;
781    }
782    siblings
783}
784
785fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
786    if node.start_byte() >= class_end {
787        return false;
788    }
789    node.end_byte() <= class_end
790        || cpp_nested_stray_close_brace(node, source)
791            .is_some_and(|close| close.start_byte() == class_end)
792}
793
794/// Recover a plain class whose opening prefix is retained in one ERROR node
795/// while one or more nested class closes and the outer close are displaced to
796/// sibling `}`/`;` nodes. This is the non-export counterpart to the fragmented
797/// export-class recovery above. All boundaries come from tree-sitter nodes: the
798/// direct class tokens establish nesting depth and the displaced close nodes
799/// terminate it.
800fn fragmented_plain_class_body<'tree>(
801    node: Node<'tree>,
802    source: &str,
803) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
804    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
805        return Some(recovered);
806    }
807    if node.kind() != "ERROR" {
808        return None;
809    }
810    let mut cursor = node.walk();
811    let children = node.children(&mut cursor).collect::<Vec<_>>();
812    let keyword = children.first()?;
813    if !matches!(keyword.kind(), "class" | "struct" | "union") {
814        return None;
815    }
816    let name_node = children
817        .iter()
818        .copied()
819        .skip(1)
820        .find(|child| child.is_named())?;
821    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
822        return None;
823    }
824    let name = normalize_cpp_whitespace(node_text(name_node, source));
825    if name.is_empty() || cpp_export_macro_token(&name) {
826        return None;
827    }
828    let open_index = children.iter().position(|child| child.kind() == "{")?;
829    let open = children[open_index];
830    let nested_class_opens = children[open_index + 1..]
831        .iter()
832        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
833        .count();
834    let mut closes_remaining = 1 + nested_class_opens;
835    let mut sibling = node.next_named_sibling();
836    while let Some(candidate) = sibling {
837        let next = candidate.next_named_sibling();
838        if cpp_is_stray_close_brace(candidate, source) {
839            closes_remaining -= 1;
840            if closes_remaining == 0 {
841                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
842                if open.end_byte() >= candidate.start_byte() {
843                    return None;
844                }
845                return Some((
846                    node,
847                    name,
848                    FragmentedExportBody {
849                        reparse_start: open.end_byte(),
850                        reparse_end: candidate.start_byte(),
851                        class_range: Range {
852                            start_byte: node.start_byte(),
853                            end_byte: semicolon.end_byte(),
854                            start_line: node.start_position().row + 1,
855                            end_line: semicolon.end_position().row + 1,
856                        },
857                    },
858                ));
859            }
860        }
861        sibling = next;
862    }
863    None
864}
865
866pub(crate) fn recovered_fragmented_plain_class_has_body(
867    node: Node<'_>,
868    source: &str,
869    expected_name: &str,
870    expected_range: &Range,
871) -> bool {
872    fragmented_plain_class_body(node, source).is_some_and(|(_, name, fragmented)| {
873        name == expected_name
874            && fragmented.class_range.start_byte == expected_range.start_byte
875            && fragmented.class_range.end_byte == expected_range.end_byte
876    })
877}
878
879/// Recover a plain class whose parser-visible body ends inside a malformed
880/// inline member. Tree-sitter then attaches either the next real member
881/// declarator or the unfinished `else` branch directly to the outer function
882/// definition and leaves the class's actual `};` among later siblings. Those
883/// structured continuations and the close/semicolon siblings establish the
884/// complete body envelope without interpreting source text.
885fn fragmented_plain_class_declaration_body<'tree>(
886    node: Node<'tree>,
887    source: &str,
888) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
889    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
890        return None;
891    }
892    let class_node = node.child_by_field_name("type")?;
893    if !matches!(
894        class_node.kind(),
895        "class_specifier" | "struct_specifier" | "union_specifier"
896    ) {
897        return None;
898    }
899    let name_node = class_node.child_by_field_name("name")?;
900    let name = normalize_cpp_whitespace(node_text(name_node, source));
901    if name.is_empty() || cpp_export_macro_token(&name) {
902        return None;
903    }
904    let body = cpp_body_node(class_node)?;
905    if body.kind() != "field_declaration_list" {
906        return None;
907    }
908    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
909        if declarator.start_byte() < class_node.end_byte() {
910            return None;
911        }
912        let mut cursor = node.walk();
913        node.named_children(&mut cursor).any(|child| {
914            if child.kind() != "ERROR"
915                || child.start_byte() < class_node.end_byte()
916                || child.end_byte() > declarator.start_byte()
917            {
918                return false;
919            }
920            let mut cursor = child.walk();
921            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
922            let Some((return_type, attributes)) = components.split_last() else {
923                return false;
924            };
925            matches!(
926                return_type.kind(),
927                "identifier"
928                    | "type_identifier"
929                    | "primitive_type"
930                    | "decltype"
931                    | "placeholder_type_specifier"
932            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
933                && attributes.iter().all(|attribute| {
934                    matches!(attribute.kind(), "identifier" | "type_identifier")
935                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
936                            *attribute, source,
937                        )))
938                })
939        })
940    } else {
941        let mut cursor = node.walk();
942        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
943        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
944            if same_node(*candidate_class, class_node)
945                && continuation.kind() == "identifier"
946                && node_text(*continuation, source) == "else"
947                && continuation_body.kind() == "compound_statement"
948                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
949                && continuation_body
950                    .child(continuation_body.child_count().saturating_sub(1))
951                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
952    };
953    if !displaced_member {
954        return None;
955    }
956    let open = body
957        .children(&mut body.walk())
958        .find(|child| child.kind() == "{")?;
959    let siblings = cpp_following_named_siblings(node, source);
960    let ordinary_boundary =
961        siblings
962            .iter()
963            .copied()
964            .enumerate()
965            .find_map(|(close_index, close)| {
966                cpp_is_stray_close_brace(close, source)
967                    .then(|| {
968                        siblings
969                            .get(close_index + 1)
970                            .copied()
971                            .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
972                            .map(|semicolon| (close, semicolon))
973                    })
974                    .flatten()
975            });
976    let (close, semicolon) =
977        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
978            (boundary.class_close, boundary.class_semicolon)
979        } else {
980            ordinary_boundary?
981        };
982    if open.end_byte() >= close.start_byte() {
983        return None;
984    }
985    Some((
986        class_node,
987        name,
988        FragmentedExportBody {
989            reparse_start: open.end_byte(),
990            reparse_end: close.start_byte(),
991            class_range: Range {
992                start_byte: class_node.start_byte(),
993                end_byte: semicolon.end_byte(),
994                start_line: class_node.start_position().row + 1,
995                end_line: semicolon.end_position().row + 1,
996            },
997        },
998    ))
999}
1000
1001fn displaced_export_function_namespace_shape<'tree>(
1002    declaration: Node<'tree>,
1003    source: &str,
1004) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1005    let mut nested = Vec::new();
1006    for index in (0..declaration.named_child_count()).rev() {
1007        nested.push(declaration.named_child(index)?);
1008    }
1009    while let Some(current) = nested.pop() {
1010        // A recovered export class nested in this class can consume the first
1011        // parser-visible namespace close itself. In that shape the existing
1012        // later-class boundary logic already distinguishes the nested and
1013        // namespace-sibling owners; do not mistake the nested close for this
1014        // class's terminator.
1015        if recover_exported_class_function_definition(current, source).is_some() {
1016            return None;
1017        }
1018        for index in (0..current.named_child_count()).rev() {
1019            nested.push(current.named_child(index)?);
1020        }
1021    }
1022    let mut same_envelope_sibling = declaration.next_named_sibling();
1023    while let Some(current) = same_envelope_sibling {
1024        if recover_exported_class_function_definition(current, source).is_some() {
1025            return None;
1026        }
1027        same_envelope_sibling = current.next_named_sibling();
1028    }
1029    let declaration_list = declaration.parent()?;
1030    if declaration_list.kind() != "declaration_list" {
1031        return None;
1032    }
1033    let namespace = declaration_list.parent()?;
1034    if namespace.kind() != "namespace_definition"
1035        || namespace.child_by_field_name("body") != Some(declaration_list)
1036    {
1037        return None;
1038    }
1039    let class_close = direct_close_brace(declaration_list)?;
1040    let trailing_semicolon = namespace.next_named_sibling()?;
1041    if trailing_semicolon.kind() != "expression_statement"
1042        || trailing_semicolon.named_child_count() != 0
1043    {
1044        return None;
1045    }
1046    // A chain of malformed export classes can consume one parser-visible
1047    // namespace close per class. Walk through the enclosing sibling levels so
1048    // the later real namespace close remains the structural boundary; a
1049    // direct next-sibling walk stops at the first collapsed namespace and
1050    // incorrectly makes its intervening items members of this class.
1051    let siblings = cpp_following_named_siblings(namespace, source);
1052    let trailing_index = siblings
1053        .iter()
1054        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1055    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1056        recover_exported_class_function_definition(*candidate, source).is_some()
1057    }) {
1058        // Consecutive recovered classes already have an exact sibling-class
1059        // boundary. Preserve that established path, including nested export
1060        // classes, instead of interpreting the first class close as a
1061        // collapsed namespace boundary.
1062        return None;
1063    }
1064    let mut namespace_items = Vec::new();
1065    let mut nested_fragment_end = 0;
1066    for current in siblings.into_iter().skip(trailing_index + 1) {
1067        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1068        {
1069            return Some(DisplacedFragmentNamespaceBoundary {
1070                class_close,
1071                class_semicolon: trailing_semicolon,
1072                namespace_items,
1073            });
1074        }
1075        if current.start_byte() >= nested_fragment_end
1076            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1077        {
1078            nested_fragment_end = fragmented.class_range.end_byte;
1079        } else if current.start_byte() >= nested_fragment_end
1080            && recover_exported_class_function_definition(current, source).is_some()
1081            && let Some(body) = cpp_body_node(current)
1082            && let Some(fragmented) =
1083                fragmented_export_function_body_region(current, body, source, None)
1084        {
1085            nested_fragment_end = fragmented.class_range.end_byte;
1086        }
1087        namespace_items.push(current);
1088    }
1089    None
1090}
1091
1092fn displaced_fragment_namespace_boundary<'tree>(
1093    declaration: Node<'tree>,
1094    body: Node<'tree>,
1095    source: &str,
1096) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1097    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1098    let reparse_start = body.start_byte() + 1;
1099    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1100    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1101}
1102
1103/// Recover the class/namespace brace geometry for a declaration whose class
1104/// close tree-sitter consumed as the enclosing namespace close. This proof is
1105/// independent of whether every member in the class body can be reparsed: the
1106/// ordinary-tree fallback can still re-own bounded sibling declarations when
1107/// an unknown macro makes the complete body reparse unsafe.
1108fn displaced_fragment_namespace_geometry<'tree>(
1109    declaration: Node<'tree>,
1110    source: &str,
1111) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1112    // A templated class's malformed function wrapper remains beneath the
1113    // template node even though its later members have escaped to the
1114    // enclosing declaration list. Lift only that exact declaration child.
1115    let envelope = declaration
1116        .parent()
1117        .filter(|parent| {
1118            parent.kind() == "template_declaration"
1119                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1120        })
1121        .unwrap_or(declaration);
1122    let declaration_list = envelope.parent()?;
1123    if declaration_list.kind() != "declaration_list" {
1124        return None;
1125    }
1126    let namespace = declaration_list.parent()?;
1127    if namespace.kind() != "namespace_definition"
1128        || namespace.child_by_field_name("body") != Some(declaration_list)
1129    {
1130        return None;
1131    }
1132    let class_close = direct_close_brace(declaration_list)?;
1133    let trailing_semicolon = namespace.next_named_sibling()?;
1134    if trailing_semicolon.kind() != "expression_statement"
1135        || trailing_semicolon.named_child_count() != 0
1136    {
1137        return None;
1138    }
1139    let mut namespace_items = Vec::new();
1140    let mut sibling = trailing_semicolon.next_named_sibling();
1141    let mut nested_fragment_end = 0;
1142    loop {
1143        let current = sibling?;
1144        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1145        {
1146            break;
1147        }
1148        if current.start_byte() >= nested_fragment_end
1149            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1150        {
1151            nested_fragment_end = fragmented.class_range.end_byte;
1152        }
1153        namespace_items.push(current);
1154        sibling = current.next_named_sibling();
1155    }
1156    Some(DisplacedFragmentNamespaceBoundary {
1157        class_close,
1158        class_semicolon: trailing_semicolon,
1159        namespace_items,
1160    })
1161}
1162
1163/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1164fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1165    (0..node.child_count())
1166        .filter_map(|index| node.child(index))
1167        .find(|child| !child.is_named() && child.kind() == "}")
1168}
1169
1170/// A displaced lone closing brace: the class close that the fragmented multiple-base
1171/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1172fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1173    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1174}
1175
1176/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1177/// text while skipping line/block comments and string/char literals. The
1178/// exported-class recovery needs this when tree-sitter's bogus
1179/// `function_definition` body runs past the class's true closing brace and
1180/// swallows following siblings (issue #1524): the grammar tree carries no
1181/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1182/// close is located textually. Returns `None` when the text is unbalanced or
1183/// contains a construct the scanner deliberately does not interpret (raw
1184/// strings) -- callers treat that as "cannot partition" and keep the
1185/// un-split recovery.
1186fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1187    let bytes = source.as_bytes();
1188    if bytes.get(open_byte) != Some(&b'{') {
1189        return None;
1190    }
1191    let mut depth = 0usize;
1192    let mut i = open_byte;
1193    while i < bytes.len() {
1194        match bytes[i] {
1195            b'{' => depth += 1,
1196            b'}' => {
1197                depth = depth.checked_sub(1)?;
1198                if depth == 0 {
1199                    return Some(i);
1200                }
1201            }
1202            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1203                while i < bytes.len() && bytes[i] != b'\n' {
1204                    i += 1;
1205                }
1206                continue;
1207            }
1208            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1209                i += 2;
1210                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1211                    i += 1;
1212                }
1213                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1214                continue;
1215            }
1216            quote @ (b'"' | b'\'') => {
1217                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1218                // bail out rather than mis-count.
1219                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1220                    return None;
1221                }
1222                i += 1;
1223                while i < bytes.len() && bytes[i] != quote {
1224                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1225                }
1226                if i >= bytes.len() {
1227                    return None;
1228                }
1229            }
1230            _ => {}
1231        }
1232        i += 1;
1233    }
1234    None
1235}
1236
1237fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1238    let mut name = None;
1239    let mut colon_count = 0;
1240    let mut access_count = 0;
1241    for index in 0..node.child_count() {
1242        let child = node.child(index)?;
1243        match child.kind() {
1244            "identifier" | "type_identifier" if child.is_named() => {
1245                if name.is_some() {
1246                    return None;
1247                }
1248                let candidate = normalize_cpp_whitespace(node_text(child, source));
1249                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1250                    return None;
1251                }
1252                name = Some(candidate);
1253            }
1254            "template_function" | "template_type" if child.is_named() => {
1255                if name.is_some() {
1256                    return None;
1257                }
1258                let candidate = child
1259                    .child_by_field_name("name")
1260                    .and_then(|name| direct_identifier_name(name, source))?;
1261                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1262                    return None;
1263                }
1264                name = Some(candidate);
1265            }
1266            ":" if !child.is_named() => colon_count += 1,
1267            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1268            _ => return None,
1269        }
1270    }
1271    (colon_count == 1 && access_count == 1)
1272        .then_some(name)
1273        .flatten()
1274}
1275
1276fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1277    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1278        return false;
1279    }
1280    node.named_child(0)
1281        .and_then(|child| direct_identifier_name(child, source))
1282        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1283}
1284
1285fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1286    (0..node.child_count()).any(|index| {
1287        node.child(index)
1288            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1289    })
1290}
1291
1292fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1293    match node.kind() {
1294        "type_identifier" | "identifier" | "namespace_identifier" => {
1295            recovered_base_atom(node, source)
1296        }
1297        "template_type" | "template_function" => node
1298            .child_by_field_name("name")
1299            .and_then(|name| recovered_malformed_base_name(name, source)),
1300        "ERROR" => None,
1301        "qualified_identifier" | "scoped_type_identifier" => {
1302            let suffix = node
1303                .child_by_field_name("name")
1304                .and_then(|name| recovered_malformed_base_name(name, source))?;
1305            let scope = node
1306                .child_by_field_name("scope")
1307                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1308            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1309                malformed_qualified_prefix(node, source)?
1310            } else {
1311                if malformed_qualified_prefix(node, source).is_some() {
1312                    return None;
1313                }
1314                scope
1315            };
1316            Some(format!("{prefix}::{suffix}"))
1317        }
1318        _ => None,
1319    }
1320}
1321
1322fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1323    if !matches!(
1324        node.kind(),
1325        "identifier" | "type_identifier" | "namespace_identifier"
1326    ) {
1327        return None;
1328    }
1329    let name = normalize_cpp_whitespace(node_text(node, source));
1330    (!name.is_empty()).then_some(name)
1331}
1332
1333fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1334    let mut prefix = None;
1335    let mut cursor = node.walk();
1336    for error in node
1337        .named_children(&mut cursor)
1338        .filter(|child| child.kind() == "ERROR")
1339    {
1340        if error.named_child_count() != 1 || prefix.is_some() {
1341            return None;
1342        }
1343        prefix = error
1344            .named_child(0)
1345            .and_then(|child| recovered_base_atom(child, source));
1346        prefix.as_ref()?;
1347    }
1348    prefix
1349}
1350
1351fn recover_exported_class_function_definition<'tree>(
1352    node: Node<'tree>,
1353    source: &str,
1354) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1355    if node.kind() != "function_definition" {
1356        return None;
1357    }
1358    let type_node = node.child_by_field_name("type")?;
1359    let declarator = node.child_by_field_name("declarator")?;
1360
1361    if matches!(
1362        type_node.kind(),
1363        "class_specifier" | "struct_specifier" | "union_specifier"
1364    ) {
1365        let type_name = type_node
1366            .child_by_field_name("name")
1367            .and_then(|name| direct_identifier_name(name, source));
1368        let exported_macro_type = type_name
1369            .as_ref()
1370            .is_some_and(|name| cpp_export_macro_token(name));
1371        if exported_macro_type {
1372            let mut cursor = node.walk();
1373            let errors_before_declarator = node
1374                .named_children(&mut cursor)
1375                .filter(|child| {
1376                    child.kind() == "ERROR"
1377                        && child.start_byte() >= type_node.end_byte()
1378                        && child.end_byte() <= declarator.start_byte()
1379                })
1380                .collect::<Vec<_>>();
1381            if let Some(name) = errors_before_declarator
1382                .iter()
1383                .find_map(|error| displaced_exported_class_name(*error, source))
1384            {
1385                let raw_supertypes = errors_before_declarator
1386                    .iter()
1387                    .any(|error| malformed_inheritance_syntax(*error))
1388                    .then(|| recovered_malformed_base_name(declarator, source))
1389                    .flatten()
1390                    .map(|base| vec![base]);
1391                return Some((node, name, raw_supertypes));
1392            }
1393            if errors_before_declarator
1394                .iter()
1395                .any(|error| malformed_inheritance_syntax(*error))
1396            {
1397                return None;
1398            }
1399        }
1400        if !exported_macro_type
1401            && let Some(name) = type_name
1402            && !cpp_export_macro_token(&name)
1403            && let Some(base) =
1404                recovered_postfix_export_macro_base(node, type_node, declarator, source)
1405        {
1406            return Some((node, name, Some(vec![base])));
1407        }
1408        if let Some(name) = direct_identifier_name(declarator, source)
1409            && exported_macro_type
1410            && !cpp_export_macro_token(&name)
1411        {
1412            let raw_supertypes = exported_macro_type
1413                .then(|| recovered_single_base_after_declarator(node, declarator, source))
1414                .flatten()
1415                .map(|base| vec![base]);
1416            return Some((node, name, raw_supertypes));
1417        }
1418        if declarator.kind() == "parenthesized_declarator"
1419            && type_node
1420                .child_by_field_name("name")
1421                .and_then(|name| direct_identifier_name(name, source))
1422                .is_some_and(|name| cpp_export_macro_token(&name))
1423        {
1424            let body_start = node
1425                .child_by_field_name("body")
1426                .map(|body| body.start_byte())
1427                .unwrap_or(node.end_byte());
1428            let mut cursor = node.walk();
1429            if let Some(name) = node
1430                .named_children(&mut cursor)
1431                .filter(|child| {
1432                    child.kind() == "ERROR"
1433                        && child.start_byte() >= declarator.end_byte()
1434                        && child.end_byte() <= body_start
1435                })
1436                .find_map(|error| declarator_name_from_node(error, source))
1437            {
1438                return Some((node, name, None));
1439            }
1440        }
1441    }
1442
1443    let declarator_text = direct_identifier_name(declarator, source)?;
1444    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1445        return None;
1446    }
1447    class_identifier_before_body(node, source).map(|name| (node, name, None))
1448}
1449
1450/// Whether `node` is the base type displaced into the declarator field of an
1451/// export-macro class that tree-sitter represented as a declaration or
1452/// function definition.
1453///
1454/// Declaration extraction already recovers this exact malformed envelope as a
1455/// class and records the declarator as its base. Reference extraction must use
1456/// the same structural fact instead of treating the node as a function name.
1457pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
1458    if !matches!(
1459        node.kind(),
1460        "qualified_identifier" | "scoped_type_identifier" | "template_type"
1461    ) {
1462        return false;
1463    }
1464    if let Some(function) = node.parent().filter(|parent| {
1465        parent.kind() == "function_definition"
1466            && parent
1467                .child_by_field_name("declarator")
1468                .is_some_and(|declarator| same_node(declarator, node))
1469    }) {
1470        return recover_exported_class_function_definition(function, source)
1471            .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
1472    }
1473    let Some(initializer) = node.parent().filter(|parent| {
1474        parent.kind() == "init_declarator"
1475            && parent
1476                .child_by_field_name("declarator")
1477                .is_some_and(|declarator| same_node(declarator, node))
1478    }) else {
1479        return false;
1480    };
1481    initializer
1482        .parent()
1483        .filter(|parent| parent.kind() == "declaration")
1484        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
1485        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
1486}
1487
1488/// Recover the class item from a region reparse that still carries the
1489/// sentinel's synthetic function envelope.  An unknown class attribute can
1490/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
1491/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
1492/// then nested below that function, so direct class-child lookup is not enough.
1493struct CppSentinelReparsedClass<'tree> {
1494    declaration_node: Node<'tree>,
1495    name: String,
1496    body: Node<'tree>,
1497    raw_supertypes: Option<Vec<String>>,
1498}
1499
1500fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1501    let mut cursor = root.walk();
1502    root.named_children(&mut cursor)
1503        .find(|child| child.kind() != "comment")
1504        .filter(|child| child.kind() == "template_declaration")
1505}
1506
1507fn cpp_sentinel_reparsed_class<'tree>(
1508    root: Node<'tree>,
1509    template_node: Option<Node<'tree>>,
1510    source: &str,
1511) -> Option<CppSentinelReparsedClass<'tree>> {
1512    let container = template_node.unwrap_or(root);
1513    let mut cursor = container.walk();
1514    for child in container.named_children(&mut cursor) {
1515        if matches!(
1516            child.kind(),
1517            "class_specifier" | "struct_specifier" | "union_specifier"
1518        ) {
1519            let name = class_like_name(child, source)?;
1520            let body = cpp_body_node(child)?;
1521            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1522                .then(|| extract_cpp_supertypes(child, source));
1523            return Some(CppSentinelReparsedClass {
1524                declaration_node: child,
1525                name,
1526                body,
1527                raw_supertypes,
1528            });
1529        }
1530        if child.kind() == "declaration"
1531            && let Some(class_node) = first_class_like_child(child)
1532        {
1533            let name = class_like_name(class_node, source)?;
1534            let body = cpp_body_node(class_node)?;
1535            let raw_supertypes =
1536                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1537                    .then(|| extract_cpp_supertypes(class_node, source));
1538            return Some(CppSentinelReparsedClass {
1539                declaration_node: class_node,
1540                name,
1541                body,
1542                raw_supertypes,
1543            });
1544        }
1545        // Only when the nested class item carries its own body. A bodyless
1546        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
1547        // a function definition -- is the export-macro shape recovered by the
1548        // next arm, and must fall through to it rather than abort the search.
1549        if child.kind() == "function_definition"
1550            && let Some(class_node) = first_class_like_child(child)
1551            && let Some(body) = cpp_body_node(class_node)
1552            && let Some(name) = class_like_name(class_node, source)
1553        {
1554            let raw_supertypes =
1555                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1556                    .then(|| extract_cpp_supertypes(class_node, source));
1557            return Some(CppSentinelReparsedClass {
1558                declaration_node: class_node,
1559                name,
1560                body,
1561                raw_supertypes,
1562            });
1563        }
1564        if child.kind() == "function_definition"
1565            && let Some((_, name, raw_supertypes)) =
1566                recover_exported_class_function_definition(child, source)
1567        {
1568            let body = cpp_body_node(child)?;
1569            return Some(CppSentinelReparsedClass {
1570                declaration_node: child,
1571                name,
1572                body,
1573                raw_supertypes,
1574            });
1575        }
1576    }
1577    None
1578}
1579
1580fn recovered_postfix_export_macro_base(
1581    node: Node<'_>,
1582    type_node: Node<'_>,
1583    declarator: Node<'_>,
1584    source: &str,
1585) -> Option<String> {
1586    let mut cursor = node.walk();
1587    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
1588        child.kind() == "ERROR"
1589            && child.start_byte() >= type_node.end_byte()
1590            && child.end_byte() <= declarator.start_byte()
1591            && postfix_export_macro_inheritance(*child, source)
1592    });
1593    malformed_clauses.next()?;
1594    if malformed_clauses.next().is_some() {
1595        return None;
1596    }
1597    recovered_malformed_base_name(declarator, source)
1598}
1599
1600fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
1601    let mut macro_count = 0;
1602    let mut colon_count = 0;
1603    let mut access_count = 0;
1604    for index in 0..node.child_count() {
1605        let Some(child) = node.child(index) else {
1606            return false;
1607        };
1608        match child.kind() {
1609            "identifier" | "type_identifier" if child.is_named() => {
1610                let candidate = normalize_cpp_whitespace(node_text(child, source));
1611                if !cpp_export_macro_token(&candidate) {
1612                    return false;
1613                }
1614                macro_count += 1;
1615            }
1616            ":" if !child.is_named() => colon_count += 1,
1617            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1618            _ => return false,
1619        }
1620    }
1621    macro_count == 1 && colon_count == 1 && access_count == 1
1622}
1623
1624fn recovered_single_base_after_declarator(
1625    node: Node<'_>,
1626    declarator: Node<'_>,
1627    source: &str,
1628) -> Option<String> {
1629    let body_start = node
1630        .child_by_field_name("body")
1631        .map(|body| body.start_byte())
1632        .unwrap_or(node.end_byte());
1633    let mut cursor = node.walk();
1634    let mut bases = node
1635        .named_children(&mut cursor)
1636        .filter(|child| {
1637            child.kind() == "ERROR"
1638                && child.start_byte() >= declarator.end_byte()
1639                && child.end_byte() <= body_start
1640        })
1641        .filter_map(|error| displaced_exported_class_name(error, source));
1642    let base = bases.next()?;
1643    bases.next().is_none().then_some(base)
1644}
1645
1646fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
1647    (0..node.child_count()).any(|index| {
1648        node.child(index)
1649            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
1650    })
1651}
1652
1653pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
1654    recover_exported_class_function_definition(node, source).is_some()
1655}
1656
1657fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
1658    matches!(
1659        kind,
1660        "ERROR"
1661            | "preproc_if"
1662            | "preproc_ifdef"
1663            | "preproc_ifndef"
1664            | "preproc_else"
1665            | "preproc_elif"
1666    ) || (kind == "labeled_statement" && in_class_scope)
1667}
1668
1669pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
1670    if node.kind() != "declaration" {
1671        return false;
1672    }
1673    let mut ancestor = node.parent();
1674    while let Some(container) = ancestor {
1675        match container.kind() {
1676            "compound_statement" => {
1677                return container.parent().is_some_and(|class_container| {
1678                    is_recovered_exported_class_container(class_container, source)
1679                });
1680            }
1681            // These containers preserve ScopeInfo in visit_node. declaration_list is
1682            // the body container selected for a linkage specification.
1683            "template_declaration" | "linkage_specification" | "declaration_list" => {}
1684            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
1685            _ => return false,
1686        }
1687        ancestor = container.parent();
1688    }
1689    false
1690}
1691
1692pub fn recovered_exported_class_has_body(
1693    node: Node<'_>,
1694    source: &str,
1695    expected_name: &str,
1696) -> Option<bool> {
1697    match node.kind() {
1698        "function_definition" => {
1699            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
1700            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
1701        }
1702        "declaration" | "field_declaration" => {
1703            let recovered = recover_exported_class_declaration(node, source)?;
1704            (recovered.name == expected_name).then(|| recovered.body.is_some())
1705        }
1706        _ => None,
1707    }
1708}
1709
1710fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
1711    let body_start = node
1712        .child_by_field_name("body")
1713        .map(|body| body.start_byte())
1714        .unwrap_or(node.end_byte());
1715    let mut stack = Vec::new();
1716    for index in (0..node.named_child_count()).rev() {
1717        let Some(child) = node.named_child(index) else {
1718            continue;
1719        };
1720        if child.start_byte() >= body_start {
1721            continue;
1722        }
1723        stack.push(child);
1724    }
1725
1726    let mut best = None;
1727    while let Some(current) = stack.pop() {
1728        if matches!(current.kind(), "identifier" | "type_identifier") {
1729            let name = normalize_cpp_whitespace(node_text(current, source));
1730            if !name.is_empty()
1731                && !cpp_export_macro_token(&name)
1732                && !matches!(name.as_str(), "class" | "struct" | "union")
1733            {
1734                best = Some(name);
1735            }
1736            continue;
1737        }
1738
1739        for index in (0..current.named_child_count()).rev() {
1740            if let Some(child) = current.named_child(index)
1741                && child.start_byte() < body_start
1742            {
1743                stack.push(child);
1744            }
1745        }
1746    }
1747    best
1748}
1749
1750fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1751    if node.kind() == "declaration"
1752        && node
1753            .child_by_field_name("type")
1754            .or_else(|| first_class_like_child(node))
1755            .is_some_and(|type_node| {
1756                matches!(
1757                    type_node.kind(),
1758                    "class_specifier" | "struct_specifier" | "union_specifier"
1759                )
1760            })
1761        && let Some(name) = node
1762            .child_by_field_name("declarator")
1763            .and_then(|declarator| declarator_name_from_node(declarator, source))
1764        && !cpp_export_macro_token(&name)
1765    {
1766        return Some(name);
1767    }
1768
1769    if node.kind() == "function_definition"
1770        && node.child_by_field_name("type").is_some_and(|type_node| {
1771            matches!(
1772                type_node.kind(),
1773                "class_specifier" | "struct_specifier" | "union_specifier"
1774            )
1775        })
1776        && let Some(name) = node
1777            .child_by_field_name("declarator")
1778            .and_then(|declarator| direct_identifier_name(declarator, source))
1779        && !cpp_export_macro_token(&name)
1780    {
1781        return Some(name);
1782    }
1783
1784    let class_node = if matches!(
1785        node.kind(),
1786        "class_specifier" | "struct_specifier" | "union_specifier"
1787    ) {
1788        node
1789    } else {
1790        first_class_like_child(node)?
1791    };
1792    class_like_name_from_children(class_node, source)
1793}
1794
1795fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
1796    if !matches!(
1797        node.kind(),
1798        "identifier" | "field_identifier" | "type_identifier"
1799    ) {
1800        return None;
1801    }
1802    let name = normalize_cpp_whitespace(node_text(node, source));
1803    (!name.is_empty()).then_some(name)
1804}
1805
1806fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1807    match node.kind() {
1808        "identifier" | "field_identifier" | "type_identifier" => {
1809            let name = normalize_cpp_whitespace(node_text(node, source));
1810            (!name.is_empty()).then_some(name)
1811        }
1812        _ => {
1813            let mut cursor = node.walk();
1814            node.named_children(&mut cursor)
1815                .find_map(|child| declarator_name_from_node(child, source))
1816        }
1817    }
1818}
1819
1820fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
1821    let mut cursor = node.walk();
1822    node.named_children(&mut cursor).find(|child| {
1823        matches!(
1824            child.kind(),
1825            "class_specifier" | "struct_specifier" | "union_specifier"
1826        )
1827    })
1828}
1829
1830/// Push a container's children as a `Siblings` cursor rather than snapshotting
1831/// them all with one shared scope: children are visited one at a time so a
1832/// `using namespace X;` sibling can affect the scope threaded to the siblings
1833/// that textually follow it (issue #1093).
1834fn push_cpp_container_work<'tree>(
1835    node: Node<'tree>,
1836    scope: ScopeInfo,
1837    stack: &mut Vec<CppWork<'tree>>,
1838) {
1839    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
1840}
1841
1842/// Materialize one selected named-child range with a tree-sitter cursor. The
1843/// cursor advances linearly across the parent's concrete children; repeatedly
1844/// asking for `named_child(index)` is quadratic on very wide generated nodes.
1845fn push_cpp_sibling_range<'tree>(
1846    parent: Node<'tree>,
1847    start_index: usize,
1848    end_index: usize,
1849    scope: ScopeInfo,
1850    stack: &mut Vec<CppWork<'tree>>,
1851) {
1852    let mut cursor = parent.walk();
1853    let children = parent
1854        .named_children(&mut cursor)
1855        .skip(start_index)
1856        .take(end_index.saturating_sub(start_index))
1857        .collect::<Vec<_>>()
1858        .into_iter();
1859    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
1860}
1861
1862/// Advance a `Siblings` cursor by one child: dispatch the current child under
1863/// the scope accumulated from its *earlier* siblings, then push a
1864/// continuation for the remaining siblings carrying the scope updated for
1865/// *this* child (only `using namespace X;` directives change it). Pushing the
1866/// continuation before the current child's own node work means the current
1867/// child's subtree fully drains (LIFO) before the next sibling is visited,
1868/// preserving left-to-right order.
1869fn advance_cpp_siblings<'tree>(
1870    mut siblings: CppSiblingsWork<'tree>,
1871    source: &str,
1872    stack: &mut Vec<CppWork<'tree>>,
1873) {
1874    let Some(child) = siblings.children.next() else {
1875        return;
1876    };
1877    let current_scope = siblings.scope.clone();
1878    if let Some(namespace) = cpp_using_namespace_target(child, source) {
1879        siblings.scope.visible_using_namespaces.push(namespace);
1880    }
1881    if !siblings.children.as_slice().is_empty() {
1882        stack.push(CppWork::Siblings(siblings));
1883    }
1884    stack.push(CppWork::Node(CppNodeWork {
1885        node: child,
1886        scope: current_scope,
1887    }));
1888}
1889
1890/// The namespace target of a `using namespace X;` directive, or `None` for
1891/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
1892/// kind. Distinguished structurally by the presence of the grammar's literal
1893/// `namespace` keyword token among the node's children -- not by inspecting
1894/// source text -- so it never misreads a member-importing using-declaration
1895/// as a namespace directive.
1896fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
1897    if node.kind() != "using_declaration" {
1898        return None;
1899    }
1900    let mut cursor = node.walk();
1901    let is_namespace_directive = node
1902        .children(&mut cursor)
1903        .any(|child| child.kind() == "namespace");
1904    if !is_namespace_directive {
1905        return None;
1906    }
1907    let target = node.named_child(0)?;
1908    // A leading `::` is the explicit-global marker, not part of the namespace
1909    // path (`using namespace ::std::chrono;`). Drop that AST token before
1910    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
1911    // keeps: storing the marker verbatim desynced the legacy package string from
1912    // the FqName bridge, which splits on `::` and drops the empty leading
1913    // component, tripping the package/short boundary assert when a bare-owner
1914    // out-of-line definition borrowed the directive's namespace (#1093 path).
1915    let start = target
1916        .child(0)
1917        .filter(|child| !child.is_named() && child.kind() == "::")
1918        .map_or(target.start_byte(), |marker| marker.end_byte());
1919    let text = normalize_cpp_whitespace(
1920        source
1921            .get(start..target.end_byte())
1922            .expect("using-directive target covers one source range"),
1923    );
1924    (!text.is_empty()).then_some(text)
1925}
1926
1927/// Every `using namespace X;` directive target in a file, in source order, for
1928/// resolution-time consumers that need the file's using-directives without the
1929/// per-position scope threading extraction does. Parses `source` fresh and
1930/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
1931/// keys on the grammar's `namespace` keyword token, not source text), so it
1932/// never misreads a member-importing `using X::Y;` as a namespace directive.
1933///
1934/// This is a whole-file over-approximation of what is in scope at any one point
1935/// (a directive nested inside a `namespace {}` block or a function body is still
1936/// reported), which is exactly what the #1134 identity reconciler wants: extra
1937/// candidate namespaces that no visible class confirms are harmless, and two
1938/// that both confirm are treated as a genuine ambiguity by the reconciler.
1939pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
1940    let mut parser = Parser::new();
1941    if parser
1942        .set_language(&tree_sitter_cpp::LANGUAGE.into())
1943        .is_err()
1944    {
1945        return Vec::new();
1946    }
1947    let Some(tree) = parser.parse(source, None) else {
1948        return Vec::new();
1949    };
1950    let mut namespaces = Vec::new();
1951    let mut seen = std::collections::HashSet::new();
1952    let mut stack = vec![tree.root_node()];
1953    while let Some(node) = stack.pop() {
1954        if let Some(namespace) = cpp_using_namespace_target(node, source)
1955            && seen.insert(namespace.clone())
1956        {
1957            namespaces.push(namespace);
1958        }
1959        let mut cursor = node.walk();
1960        stack.extend(node.named_children(&mut cursor));
1961    }
1962    namespaces
1963}
1964
1965pub struct CppVisitor<'a> {
1966    pub file: &'a ProjectFile,
1967    pub source: &'a str,
1968    pub parsed: &'a mut ParsedFile,
1969    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
1970    /// Byte regions whose contents were re-owned by a fragmented export-class
1971    /// recovery (#938): the scattered members between the fragmented
1972    /// declaration and its displaced closing brace are indexed as members of
1973    /// the recovered class by the region reparse, so the ordinary sibling walk
1974    /// must not ALSO index them as top-level declarations (that double-indexing
1975    /// made a scattered nested class ambiguous between `Inner` and
1976    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
1977    /// linear scan at visit time is fine.
1978    pub consumed_fragment_regions: Vec<(usize, usize)>,
1979}
1980
1981impl<'a> CppVisitor<'a> {
1982    #[allow(clippy::too_many_arguments)]
1983    pub fn visit_container(
1984        &mut self,
1985        node: Node<'_>,
1986        package_name: &str,
1987        module: Option<CodeUnit>,
1988        class_unit: Option<CodeUnit>,
1989        template_signature: Option<String>,
1990        visible_using_namespaces: Vec<String>,
1991    ) {
1992        let scope = ScopeInfo {
1993            package_name: package_name.to_string(),
1994            module,
1995            class_unit,
1996            template_signature,
1997            template_metadata: None,
1998            declarations_are_fields: false,
1999            recovered_specialization_member_scope: false,
2000            visible_using_namespaces,
2001        };
2002        self.run_container_work(node, scope);
2003    }
2004
2005    /// Whether a work node lies entirely inside a byte region consumed by a
2006    /// fragmented export-class recovery (#938); such nodes were already indexed
2007    /// as members of the recovered class by the region reparse.
2008    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
2009        self.consumed_fragment_regions
2010            .iter()
2011            .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
2012    }
2013
2014    /// Drive the container work loop from an explicit seed scope to completion. The
2015    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
2016    /// stays alive for the whole traversal.
2017    fn run_container_work<'tree>(&mut self, node: Node<'tree>, scope: ScopeInfo) {
2018        let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
2019        while let Some(work) = stack.pop() {
2020            match work {
2021                CppWork::Container(container) => {
2022                    push_cpp_container_work(container.node, container.scope, &mut stack);
2023                }
2024                CppWork::Siblings(siblings) => {
2025                    advance_cpp_siblings(siblings, self.source, &mut stack);
2026                }
2027                CppWork::Node(work) => {
2028                    if self.node_is_inside_consumed_fragment(work.node) {
2029                        continue;
2030                    }
2031                    self.visit_node(work.node, &work.scope, &mut stack);
2032                }
2033            }
2034        }
2035    }
2036
2037    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
2038    /// it only when the entire region is member-shaped. This validation must happen
2039    /// before registering the recovered class because a rejected speculative range
2040    /// must not leak into the ordinary recovery path.
2041    fn reparse_fragmented_export_class_members(
2042        &self,
2043        fragmented: &FragmentedExportBody,
2044        class_name: &str,
2045    ) -> Option<FragmentedExportMembers> {
2046        if fragmented.reparse_start >= fragmented.reparse_end {
2047            return None;
2048        }
2049        let tree = cpp_reparse_fragmented_class_body(
2050            self.source,
2051            fragmented.reparse_start,
2052            fragmented.reparse_end,
2053        )?;
2054        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
2055            return Some(FragmentedExportMembers::Complete(tree));
2056        }
2057        let has_conditional_constructor = {
2058            let root = tree.root_node();
2059            let mut cursor = root.walk();
2060            root.named_children(&mut cursor).any(|child| {
2061                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
2062            })
2063        };
2064        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
2065    }
2066
2067    /// Index an already validated fragmented body as members of `class_unit`. The
2068    /// region reparse keeps each member's exact original byte and line positions.
2069    fn visit_fragmented_export_class_members(
2070        &mut self,
2071        outcome: FragmentedExportMembers,
2072        class_unit: CodeUnit,
2073        scope: &ScopeInfo,
2074    ) -> bool {
2075        let (tree, complete) = match outcome {
2076            FragmentedExportMembers::Complete(tree) => (tree, true),
2077            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
2078        };
2079        let root = tree.root_node();
2080        let class_name = class_unit.identifier().to_string();
2081        let member_scope = ScopeInfo {
2082            // A recovered export-macro class may borrow its namespace from an
2083            // earlier forward declaration even when the malformed node itself
2084            // sits at file scope. Use the recovered class identity as the
2085            // authoritative package for reparsed members as well.
2086            package_name: class_unit.package_name().to_string(),
2087            module: scope.module.clone(),
2088            class_unit: Some(class_unit),
2089            template_signature: scope.template_signature.clone(),
2090            template_metadata: None,
2091            declarations_are_fields: true,
2092            recovered_specialization_member_scope: false,
2093            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2094        };
2095        if !complete {
2096            // A conditional beginning immediately after an access label can
2097            // fragment one constructor declaration while leaving the rest of
2098            // the class body as unsafe statement soup. Recover only that
2099            // structurally proven constructor and leave the outer-tree
2100            // siblings unconsumed for their ordinary walk.
2101            let mut cursor = root.walk();
2102            let constructors = root
2103                .named_children(&mut cursor)
2104                .filter_map(|child| {
2105                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
2106                })
2107                .collect::<Vec<_>>();
2108            for constructor in constructors {
2109                let mut stack = Vec::new();
2110                self.visit_node(constructor, &member_scope, &mut stack);
2111                while let Some(work) = stack.pop() {
2112                    match work {
2113                        CppWork::Container(container) => {
2114                            push_cpp_container_work(container.node, container.scope, &mut stack);
2115                        }
2116                        CppWork::Siblings(siblings) => {
2117                            advance_cpp_siblings(siblings, self.source, &mut stack);
2118                        }
2119                        CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
2120                    }
2121                }
2122            }
2123            return false;
2124        }
2125        self.run_container_work(root, member_scope);
2126        true
2127    }
2128
2129    fn visit_recovered_fragment_constructor(
2130        &mut self,
2131        range: std::ops::Range<usize>,
2132        constructor_body: Node<'_>,
2133        class_declaration: Node<'_>,
2134        class_unit: &CodeUnit,
2135        scope: &ScopeInfo,
2136    ) {
2137        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
2138            return;
2139        };
2140        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
2141            tree.root_node(),
2142            range.start,
2143            class_unit.identifier(),
2144            self.source,
2145        ) else {
2146            return;
2147        };
2148        let member_scope = ScopeInfo {
2149            package_name: class_unit.package_name().to_string(),
2150            module: scope.module.clone(),
2151            class_unit: Some(class_unit.clone()),
2152            template_signature: scope.template_signature.clone(),
2153            template_metadata: None,
2154            declarations_are_fields: true,
2155            recovered_specialization_member_scope: false,
2156            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2157        };
2158        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
2159        else {
2160            return;
2161        };
2162        debug_assert_eq!(function.name, class_unit.identifier());
2163        let code_unit = function.code_unit(self.file.clone());
2164        self.parsed.add_code_unit_with_range(
2165            code_unit.clone(),
2166            Range {
2167                start_byte: function_declarator.start_byte(),
2168                end_byte: constructor_body.end_byte(),
2169                start_line: function_declarator.start_position().row + 1,
2170                end_line: constructor_body.end_position().row + 1,
2171            },
2172            None,
2173            None,
2174        );
2175        self.parsed.add_signature_with_metadata(
2176            code_unit.clone(),
2177            cpp_signature_metadata(
2178                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
2179                function_declarator,
2180                self.source,
2181            )
2182            .with_declaration_only(false)
2183            .with_callable_linkage(cpp_callable_linkage(class_declaration, self.source)),
2184        );
2185        self.parsed.add_child(class_unit.clone(), code_unit);
2186    }
2187
2188    fn visit_recovered_fragment_prefix_members(
2189        &mut self,
2190        root: Node<'_>,
2191        constructor_start: usize,
2192        class_unit: &CodeUnit,
2193        scope: &ScopeInfo,
2194    ) {
2195        let member_scope = ScopeInfo {
2196            package_name: class_unit.package_name().to_string(),
2197            module: scope.module.clone(),
2198            class_unit: Some(class_unit.clone()),
2199            template_signature: scope.template_signature.clone(),
2200            template_metadata: None,
2201            declarations_are_fields: true,
2202            recovered_specialization_member_scope: false,
2203            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2204        };
2205        let mut stack = vec![root];
2206        while let Some(current) = stack.pop() {
2207            if current.kind() == "comment" || current.start_byte() >= constructor_start {
2208                continue;
2209            }
2210            if current.end_byte() <= constructor_start
2211                && current.kind() != "translation_unit"
2212                && current.kind() != "labeled_statement"
2213                && current.kind() != "ERROR"
2214            {
2215                let mut work_stack = Vec::new();
2216                self.visit_node(current, &member_scope, &mut work_stack);
2217                while let Some(work) = work_stack.pop() {
2218                    match work {
2219                        CppWork::Container(container) => {
2220                            push_cpp_container_work(
2221                                container.node,
2222                                container.scope,
2223                                &mut work_stack,
2224                            );
2225                        }
2226                        CppWork::Siblings(siblings) => {
2227                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
2228                        }
2229                        CppWork::Node(work) => {
2230                            self.visit_node(work.node, &work.scope, &mut work_stack)
2231                        }
2232                    }
2233                }
2234                continue;
2235            }
2236            if matches!(
2237                current.kind(),
2238                "translation_unit" | "labeled_statement" | "ERROR"
2239            ) {
2240                let mut cursor = current.walk();
2241                stack.extend(current.named_children(&mut cursor));
2242            }
2243        }
2244    }
2245
2246    fn visit_node<'tree>(
2247        &mut self,
2248        node: Node<'tree>,
2249        scope: &ScopeInfo,
2250        stack: &mut Vec<CppWork<'tree>>,
2251    ) {
2252        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
2253            self.visit_node(node, &recovered_scope, stack);
2254            return;
2255        }
2256        if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
2257        {
2258            let displaced_namespace_items =
2259                displaced_fragment_namespace_geometry(node, self.source)
2260                    .map(|boundary| boundary.namespace_items)
2261                    .unwrap_or_default();
2262            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
2263            let mut class_stack = Vec::new();
2264            // When the full body cannot be safely reparsed, the original class
2265            // node still proves ownership for its parser-visible prefix.
2266            let parser_visible_body =
2267                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
2268                    .then(|| cpp_body_node(class_node))
2269                    .flatten();
2270            let class_unit = self.visit_named_class_like_shape(
2271                class_node,
2272                name,
2273                parser_visible_body,
2274                true,
2275                Some(fragmented.class_range),
2276                Some(extract_cpp_supertypes(class_node, self.source)),
2277                scope,
2278                &mut class_stack,
2279            );
2280            let member_scope = ScopeInfo {
2281                package_name: class_unit.package_name().to_string(),
2282                module: scope.module.clone(),
2283                class_unit: Some(class_unit.clone()),
2284                template_signature: scope.template_signature.clone(),
2285                template_metadata: None,
2286                declarations_are_fields: true,
2287                recovered_specialization_member_scope: false,
2288                visible_using_namespaces: scope.visible_using_namespaces.clone(),
2289            };
2290            let complete = outcome.is_some_and(|outcome| {
2291                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
2292            });
2293            if complete {
2294                self.consumed_fragment_regions
2295                    .push((node.start_byte(), fragmented.class_range.end_byte));
2296            } else {
2297                // A macro-constrained member can make the full body reparse
2298                // unsafe while tree-sitter still exposes later class members
2299                // as bounded siblings up to the displaced `}`/`;`. Keep the
2300                // structurally proven class/base declaration and re-own those
2301                // sibling nodes under it. They retain their original parser
2302                // nodes and exact ranges; the close boundary comes solely from
2303                // `fragmented_plain_class_body`.
2304                // Template wrappers put the escaped members beside the
2305                // template rather than beside its malformed declaration.
2306                for candidate in cpp_following_named_siblings(node, self.source) {
2307                    if candidate.start_byte() >= fragmented.reparse_end {
2308                        break;
2309                    }
2310                    if cpp_fragment_sibling_is_class_member(
2311                        candidate,
2312                        fragmented.reparse_end,
2313                        self.source,
2314                    ) {
2315                        self.recovered_class_sibling_scopes
2316                            .insert(candidate.id(), member_scope.clone());
2317                    }
2318                }
2319            }
2320            for item in displaced_namespace_items {
2321                self.recovered_class_sibling_scopes
2322                    .insert(item.id(), scope.clone());
2323            }
2324            stack.extend(class_stack);
2325            return;
2326        }
2327        match node.kind() {
2328            "template_declaration" => {
2329                if let Some(recovered) = recover_fragmented_preprocessor_class(node, self.source) {
2330                    let mut template_scope = scope.clone();
2331                    template_scope.template_signature =
2332                        cpp_template_signature(node, recovered.declaration_node, self.source);
2333                    template_scope.template_metadata =
2334                        cpp_template_metadata(node, recovered.class_node, self.source);
2335                    let raw_supertypes =
2336                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
2337                    let mut class_stack = Vec::new();
2338                    let class_unit = self.visit_named_class_like_shape(
2339                        recovered.class_node,
2340                        recovered.name,
2341                        Some(recovered.body),
2342                        true,
2343                        Some(recovered.range),
2344                        raw_supertypes,
2345                        &template_scope,
2346                        &mut class_stack,
2347                    );
2348                    self.parsed.record_materialization(
2349                        MaterializationRecord::RecoveredDeclaration {
2350                            recovery: recovered.range,
2351                            unit: class_unit.clone(),
2352                        },
2353                    );
2354                    let member_scope = ScopeInfo {
2355                        package_name: template_scope.package_name.clone(),
2356                        module: template_scope.module.clone(),
2357                        class_unit: Some(class_unit.clone()),
2358                        template_signature: template_scope.template_signature.clone(),
2359                        template_metadata: None,
2360                        declarations_are_fields: true,
2361                        recovered_specialization_member_scope: recovered
2362                            .class_node
2363                            .child_by_field_name("name")
2364                            .is_some_and(|name| name.kind() == "template_type"),
2365                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
2366                    };
2367                    for tail_member in recovered.tail_members.into_iter().rev() {
2368                        stack.push(CppWork::Node(CppNodeWork {
2369                            node: tail_member,
2370                            scope: member_scope.clone(),
2371                        }));
2372                    }
2373                    stack.extend(class_stack);
2374                    for sibling in recovered.member_siblings {
2375                        self.recovered_class_sibling_scopes
2376                            .insert(sibling.id(), member_scope.clone());
2377                    }
2378                    return;
2379                }
2380                for index in (0..node.named_child_count()).rev() {
2381                    let Some(child) = node.named_child(index) else {
2382                        continue;
2383                    };
2384                    if matches!(
2385                        child.kind(),
2386                        "class_specifier"
2387                            | "struct_specifier"
2388                            | "union_specifier"
2389                            | "enum_specifier"
2390                            | "function_definition"
2391                            | "declaration"
2392                            | "field_declaration"
2393                            | "alias_declaration"
2394                            | "namespace_definition"
2395                    ) {
2396                        let mut template_scope = scope.clone();
2397                        template_scope.template_signature =
2398                            cpp_template_signature(node, child, self.source);
2399                        template_scope.template_metadata =
2400                            cpp_template_metadata(node, child, self.source);
2401                        if let Some(recovered) =
2402                            recover_fragmented_partial_specialization(node, child, self.source)
2403                        {
2404                            let code_unit = self.visit_named_class_like_shape(
2405                                recovered.declaration_node,
2406                                recovered.name,
2407                                None,
2408                                true,
2409                                Some(recovered.range),
2410                                None,
2411                                &template_scope,
2412                                stack,
2413                            );
2414                            self.parsed.record_materialization(
2415                                MaterializationRecord::RecoveredDeclaration {
2416                                    recovery: recovered.range,
2417                                    unit: code_unit.clone(),
2418                                },
2419                            );
2420                            let mut member_scope = template_scope.clone();
2421                            member_scope.class_unit = Some(code_unit);
2422                            member_scope.declarations_are_fields = true;
2423                            member_scope.recovered_specialization_member_scope = true;
2424                            for prefix_member in recovered.prefix_members.into_iter().rev() {
2425                                stack.push(CppWork::Node(CppNodeWork {
2426                                    node: prefix_member,
2427                                    scope: member_scope.clone(),
2428                                }));
2429                            }
2430                            for sibling in recovered.member_siblings {
2431                                self.recovered_class_sibling_scopes
2432                                    .insert(sibling.id(), member_scope.clone());
2433                            }
2434                            for following in recovered.following_declarations.into_iter().rev() {
2435                                stack.push(CppWork::Node(CppNodeWork {
2436                                    node: following,
2437                                    scope: scope.clone(),
2438                                }));
2439                            }
2440                            return;
2441                        }
2442                        stack.push(CppWork::Node(CppNodeWork {
2443                            node: child,
2444                            scope: template_scope,
2445                        }));
2446                    }
2447                }
2448            }
2449            "namespace_definition" => self.visit_namespace(node, scope, stack),
2450            "linkage_specification" => {
2451                if let Some(body) = cpp_body_node(node) {
2452                    stack.push(CppWork::Container(CppContainer {
2453                        node: body,
2454                        scope: scope.clone(),
2455                    }));
2456                } else {
2457                    stack.push(CppWork::Container(CppContainer {
2458                        node,
2459                        scope: scope.clone(),
2460                    }));
2461                }
2462            }
2463            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
2464                self.visit_class_like(node, scope, stack)
2465            }
2466            "function_definition" => self.visit_function_definition(node, scope, stack),
2467            // A bare namespace-begin sentinel can make tree-sitter promote the
2468            // wrapped declaration to an ERROR node instead of the usual bogus
2469            // function_definition envelope. Keep the recovery entry point on
2470            // the same structured path for both shapes; ordinary ERROR nodes
2471            // retain their declaration-preserving wrapper traversal when the
2472            // sentinel predicate does not match.
2473            "ERROR" => {
2474                if !self.visit_sentinel_macro_region(node, scope, stack) {
2475                    self.visit_macro_swallowed_function_declarations(node, scope);
2476                    stack.push(CppWork::Container(CppContainer {
2477                        node,
2478                        scope: scope.clone(),
2479                    }));
2480                }
2481            }
2482            "declaration" => {
2483                if scope.class_unit.is_some()
2484                    && scope.declarations_are_fields
2485                    && scope.recovered_specialization_member_scope
2486                    && let Some(alias_name) =
2487                        recovered_using_declaration_alias_name(node, self.source)
2488                {
2489                    self.add_type_aliases(node, scope, vec![alias_name]);
2490                } else {
2491                    self.visit_declaration(node, scope, scope.declarations_are_fields, stack)
2492                }
2493            }
2494            "field_declaration" => self.visit_declaration(node, scope, true, stack),
2495            "type_definition" | "alias_declaration" => {
2496                self.visit_type_declaration(node, scope, stack)
2497            }
2498            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
2499            "preproc_include" => self.visit_include(node),
2500            kind if preserves_declaration_scope_through_wrapper(
2501                kind,
2502                scope.class_unit.is_some(),
2503            ) =>
2504            {
2505                // A preprocessor conditional gates every declaration inside it
2506                // on a configuration this analyzer never evaluates; record the
2507                // interval so declaration state can say so (issue #1476). The
2508                // else/elif branches are children of the `preproc_if` node, so
2509                // recording the openers covers every branch.
2510                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
2511                    let mut range = cpp_declaration_range(node);
2512                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
2513                        range.end_byte = boundary.end_byte;
2514                        range.end_line = boundary.end_line;
2515                    }
2516                    self.parsed.record_materialization(
2517                        MaterializationRecord::ConfigurationConditional { range },
2518                    );
2519                }
2520                stack.push(CppWork::Container(CppContainer {
2521                    node,
2522                    scope: scope.clone(),
2523                }))
2524            }
2525            _ => {}
2526        }
2527    }
2528
2529    fn visit_macro_swallowed_function_declarations(
2530        &mut self,
2531        envelope: Node<'_>,
2532        scope: &ScopeInfo,
2533    ) {
2534        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
2535            || envelope.kind() == "ERROR"
2536                && envelope
2537                    .parent()
2538                    .is_some_and(|parent| parent.kind() == "ERROR")
2539        {
2540            return;
2541        }
2542        let mut stack = (0..envelope.named_child_count())
2543            .filter_map(|index| envelope.named_child(index))
2544            .collect::<Vec<_>>();
2545        while let Some(node) = stack.pop() {
2546            if node.kind() == "function_declarator" {
2547                self.visit_error_swallowed_function_declaration(node, scope);
2548            }
2549            for index in 0..node.named_child_count() {
2550                if let Some(child) = node.named_child(index) {
2551                    stack.push(child);
2552                }
2553            }
2554        }
2555    }
2556
2557    fn visit_error_swallowed_function_declaration(
2558        &mut self,
2559        node: Node<'_>,
2560        scope: &ScopeInfo,
2561    ) -> bool {
2562        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
2563            return false;
2564        };
2565        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
2566            return false;
2567        };
2568        let root = tree.root_node();
2569        let mut cursor = root.walk();
2570        let declarations = root
2571            .named_children(&mut cursor)
2572            .filter(|child| child.kind() != "comment")
2573            .collect::<Vec<_>>();
2574        let [declaration] = declarations.as_slice() else {
2575            return false;
2576        };
2577        if declaration.kind() != "declaration"
2578            || declaration.has_error()
2579            || declaration.start_byte() != start
2580            || declaration.end_byte() != end
2581        {
2582            return false;
2583        }
2584        let recovery = cpp_recovery_window(self.source, start, end);
2585        self.record_recovered_declarations(recovery, |visitor| {
2586            visitor.run_container_work(root, scope.clone());
2587        });
2588        true
2589    }
2590
2591    fn visit_namespace<'tree>(
2592        &mut self,
2593        node: Node<'tree>,
2594        scope: &ScopeInfo,
2595        stack: &mut Vec<CppWork<'tree>>,
2596    ) {
2597        let name_node = node.child_by_field_name("name");
2598        let Some(name_node) = name_node else {
2599            if let Some(body) = cpp_body_node(node) {
2600                stack.push(CppWork::Container(CppContainer {
2601                    node: body,
2602                    scope: scope.clone(),
2603                }));
2604            }
2605            return;
2606        };
2607        // Diagnostic corpora contain deliberately ill-formed global namespace
2608        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
2609        // the leading global `::` as the first anonymous child. Honor that AST
2610        // boundary instead of appending the name to the lexical namespace;
2611        // appending produced legacy names such as `outer::::outer::inner`, which
2612        // could not round-trip through the structured FqName boundary.
2613        let explicitly_global = name_node
2614            .child(0)
2615            .is_some_and(|child| !child.is_named() && child.kind() == "::");
2616        let components = cpp_namespace_name_components(name_node, self.source);
2617        if components.is_empty() {
2618            return;
2619        }
2620        // One Module per namespace level. C++17's `namespace a::b { ... }` is
2621        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
2622        // shorthand must declare `a` as well as `a::b` -- extracting only the
2623        // innermost level left the enclosing namespace undeclared and made the
2624        // two spellings of one construct disagree (issue #1878).
2625        let mut package_name = if explicitly_global {
2626            String::new()
2627        } else {
2628            scope.package_name.clone()
2629        };
2630        let mut module = None;
2631        for component in components {
2632            let full_name = if package_name.is_empty() {
2633                component
2634            } else {
2635                format!("{package_name}::{component}")
2636            };
2637            let level = CodeUnit::new_fq(
2638                self.file.clone(),
2639                CodeUnitType::Module,
2640                "",
2641                full_name.clone(),
2642                cpp_namespace_fq(&full_name),
2643            );
2644            if !self.parsed.contains_declaration(&level) {
2645                self.parsed
2646                    .add_code_unit(level.clone(), node, self.source, None, None);
2647            }
2648            package_name = full_name;
2649            module = Some(level);
2650        }
2651
2652        let namespace_scope = ScopeInfo {
2653            package_name,
2654            module,
2655            // C++ never nests a namespace inside a class, so a surviving
2656            // class_unit here is always recovery bleed: a malformed-region
2657            // boundary upstream mis-scoped this namespace block. Keeping the
2658            // owner would mint the namespace's declarations as class members
2659            // under a re-appended package, desyncing the fq boundary assert
2660            // (#2306). Dropping it is identity-neutral for valid code, where
2661            // class_unit is always empty at a namespace definition.
2662            class_unit: None,
2663            template_signature: scope.template_signature.clone(),
2664            template_metadata: scope.template_metadata.clone(),
2665            declarations_are_fields: false,
2666            recovered_specialization_member_scope: false,
2667            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2668        };
2669        let container = cpp_body_node(node).unwrap_or(node);
2670        stack.push(CppWork::Container(CppContainer {
2671            node: container,
2672            scope: namespace_scope,
2673        }));
2674    }
2675
2676    fn visit_class_like<'tree>(
2677        &mut self,
2678        node: Node<'tree>,
2679        scope: &ScopeInfo,
2680        stack: &mut Vec<CppWork<'tree>>,
2681    ) {
2682        let Some(name) = class_like_name(node, self.source) else {
2683            return;
2684        };
2685        let name = qualified_class_name_chain(node, self.source, scope)
2686            .map(|chain| chain.join("$"))
2687            .unwrap_or(name);
2688        self.visit_named_class_like(node, name, scope, stack);
2689    }
2690
2691    fn visit_named_class_like<'tree>(
2692        &mut self,
2693        node: Node<'tree>,
2694        name: String,
2695        scope: &ScopeInfo,
2696        stack: &mut Vec<CppWork<'tree>>,
2697    ) {
2698        let body = cpp_body_node(node);
2699        let definition_body_present = body.is_some();
2700        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
2701            .then(|| extract_cpp_supertypes(node, self.source));
2702        self.visit_named_class_like_shape(
2703            node,
2704            name,
2705            body,
2706            definition_body_present,
2707            None,
2708            raw_supertypes,
2709            scope,
2710            stack,
2711        );
2712    }
2713
2714    #[allow(clippy::too_many_arguments)]
2715    fn visit_named_class_like_shape<'tree>(
2716        &mut self,
2717        declaration_node: Node<'tree>,
2718        name: String,
2719        body: Option<Node<'tree>>,
2720        definition_body_present: bool,
2721        explicit_range: Option<Range>,
2722        raw_supertypes: Option<Vec<String>>,
2723        scope: &ScopeInfo,
2724        stack: &mut Vec<CppWork<'tree>>,
2725    ) -> CodeUnit {
2726        let displaced_macro_tail = if explicit_range.is_none() {
2727            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
2728        } else {
2729            None
2730        };
2731        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
2732        let recovered_scope = self.scope_for_recovered_exported_class(
2733            declaration_node,
2734            &name,
2735            definition_body_present,
2736            scope,
2737        );
2738        let scope = &recovered_scope;
2739        let short_name = if let Some(parent) = &scope.class_unit {
2740            cpp_join_nested_short(parent.short_name(), &name)
2741        } else {
2742            name.clone()
2743        };
2744        // A top-level out-of-line qualified class definition (`struct
2745        // Outer::Inner { ... }` inside its namespace, #2246) carries its
2746        // nesting chain as the `$`-joined display name; push one Type/Nested
2747        // segment per class so segment-pop owner navigation keeps working.
2748        // Every other leaf name stays opaque so a literal `$` in a source
2749        // identifier never crosses the split/join boundary (#2140).
2750        let qualified_chain = if scope.class_unit.is_none() {
2751            qualified_class_name_chain(declaration_node, self.source, scope)
2752                .filter(|chain| chain.join("$") == name)
2753        } else {
2754            None
2755        };
2756        let fq = if let Some(chain) = qualified_chain {
2757            let mut fq = FqName::new();
2758            cpp_push_package(&mut fq, &scope.package_name);
2759            let mut first = true;
2760            for component in chain {
2761                let kind = if first {
2762                    SegmentKind::Type
2763                } else {
2764                    SegmentKind::Nested
2765                };
2766                fq.push(cpp_segment(&component, kind));
2767                first = false;
2768            }
2769            fq
2770        } else {
2771            cpp_leaf_fq(
2772                &scope.package_name,
2773                scope.class_unit.as_ref(),
2774                &name,
2775                SegmentKind::Nested,
2776                SegmentKind::Type,
2777            )
2778        };
2779        let code_unit = CodeUnit::with_signature_and_fq(
2780            self.file.clone(),
2781            CodeUnitType::Class,
2782            scope.package_name.clone(),
2783            short_name,
2784            scope.template_signature.clone(),
2785            false,
2786            fq,
2787        );
2788        let has_body = definition_body_present;
2789        if !has_body && self.parsed.contains_declaration(&code_unit) {
2790            self.parsed.record_navigation_range(
2791                code_unit.clone(),
2792                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
2793            );
2794            return code_unit;
2795        }
2796        if has_body {
2797            if let Some(range) = explicit_range {
2798                self.parsed
2799                    .replace_code_unit_with_range(code_unit.clone(), range, None, None);
2800            } else {
2801                self.parsed.replace_code_unit(
2802                    code_unit.clone(),
2803                    declaration_node,
2804                    self.source,
2805                    None,
2806                    None,
2807                );
2808            }
2809        } else {
2810            self.parsed
2811                .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
2812        }
2813        if let Some(raw_supertypes) = raw_supertypes {
2814            self.parsed
2815                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
2816        }
2817        self.parsed.add_signature(
2818            code_unit.clone(),
2819            render_cpp_type_signature(
2820                declaration_node,
2821                self.source,
2822                scope.template_signature.as_deref(),
2823            ),
2824        );
2825        if let Some(metadata) = &scope.template_metadata {
2826            let primary_short_name = if let Some(parent) = &scope.class_unit {
2827                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
2828            } else {
2829                metadata.primary_name.clone()
2830            };
2831            let primary_fq_name = CodeUnit::new(
2832                self.file.clone(),
2833                CodeUnitType::Class,
2834                scope.package_name.clone(),
2835                primary_short_name,
2836            )
2837            .fq_name();
2838            let mut metadata = metadata.clone();
2839            metadata.primary_fq_name = primary_fq_name;
2840            self.parsed
2841                .set_cpp_template_metadata(code_unit.clone(), metadata);
2842        }
2843        if let Some(parent) = &scope.class_unit {
2844            self.parsed.add_child(parent.clone(), code_unit.clone());
2845        } else if let Some(module) = &scope.module {
2846            self.parsed.add_child(module.clone(), code_unit.clone());
2847        }
2848
2849        if let Some(body) = body {
2850            let mut nested_scope = scope.clone();
2851            nested_scope.class_unit = Some(code_unit.clone());
2852            nested_scope.template_signature = scope.template_signature.clone();
2853            // Template metadata describes the class just created. It must not
2854            // leak into ordinary nested declarations in that class's body.
2855            // Recovered export-macro specializations carry a separate scope bit
2856            // for their declaration-shaped body members.
2857            nested_scope.template_metadata = None;
2858            // Export-macro class bodies recovered from a function_definition use
2859            // compound_statement children, whose direct fields are declarations.
2860            nested_scope.recovered_specialization_member_scope =
2861                scope.template_metadata.as_ref().is_some_and(|metadata| {
2862                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
2863                });
2864            nested_scope.declarations_are_fields =
2865                is_recovered_exported_class_container(declaration_node, self.source)
2866                    || nested_scope.recovered_specialization_member_scope;
2867            if let Some(displaced) = displaced_macro_tail {
2868                // A macro-shaped field without a source semicolon can make
2869                // tree-sitter consume the real class terminator as an ERROR
2870                // inside that field, then retain following namespace items as
2871                // later field-list children. Drain the proven class prefix
2872                // first and re-own only the structured tail with the outer
2873                // scope. The tail is pushed first because the work stack is
2874                // LIFO.
2875                push_cpp_sibling_range(
2876                    body,
2877                    displaced.split_index,
2878                    usize::MAX,
2879                    scope.clone(),
2880                    stack,
2881                );
2882                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
2883            } else {
2884                stack.push(CppWork::Container(CppContainer {
2885                    node: body,
2886                    scope: nested_scope,
2887                }));
2888            }
2889        }
2890        if declaration_node.kind() == "enum_specifier" {
2891            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
2892            if !self.has_enum_enumerator_units(&code_unit) {
2893                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
2894            }
2895        }
2896        code_unit
2897    }
2898
2899    fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
2900        let prefix = format!("{}.", parent.short_name());
2901        let parent_short = parent.short_name();
2902        self.parsed.declarations().iter().any(|unit| {
2903            unit.kind() == CodeUnitType::Field
2904                && unit.source() == parent.source()
2905                && unit.package_name() == parent.package_name()
2906                && if parent_short.is_empty() {
2907                    // Anonymous enum/union parent: its enumerators carry bare
2908                    // short names (#2140), so presence means any ownerless
2909                    // field in this file.
2910                    !unit.short_name().contains(['.', '$'])
2911                } else {
2912                    unit.short_name().starts_with(&prefix)
2913                }
2914        })
2915    }
2916
2917    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
2918        walk_named_tree_preorder(node, false, |child| {
2919            if child.kind() != "enumerator" {
2920                return WalkControl::Continue;
2921            }
2922            let Some(name_node) = child.child_by_field_name("name") else {
2923                return WalkControl::Continue;
2924            };
2925            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
2926            if name.is_empty() {
2927                return WalkControl::Continue;
2928            }
2929            let code_unit = CodeUnit::new_fq(
2930                self.file.clone(),
2931                CodeUnitType::Field,
2932                scope.package_name.clone(),
2933                cpp_join_member_short(parent.short_name(), &name),
2934                parent
2935                    .fq()
2936                    .clone()
2937                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
2938            );
2939            if self.parsed.contains_declaration(&code_unit) {
2940                return WalkControl::Continue;
2941            }
2942            self.parsed.add_code_unit(
2943                code_unit.clone(),
2944                child,
2945                self.source,
2946                Some(parent.clone()),
2947                None,
2948            );
2949            self.parsed.add_signature(
2950                code_unit,
2951                normalize_cpp_whitespace(node_text(child, self.source)),
2952            );
2953            WalkControl::Continue
2954        });
2955    }
2956
2957    fn visit_enum_enumerators_from_text(
2958        &mut self,
2959        node: Node<'_>,
2960        scope: &ScopeInfo,
2961        parent: &CodeUnit,
2962    ) {
2963        let text = node_text(node, self.source);
2964        let Some((_, body)) = text.split_once('{') else {
2965            return;
2966        };
2967        let Some((body, _)) = body.rsplit_once('}') else {
2968            return;
2969        };
2970        for entry in body.split(',') {
2971            let trimmed = entry.trim();
2972            let name = trimmed
2973                .split('=')
2974                .next()
2975                .unwrap_or("")
2976                .split_whitespace()
2977                .next()
2978                .unwrap_or("");
2979            if name.is_empty() {
2980                continue;
2981            }
2982            let code_unit = CodeUnit::new_fq(
2983                self.file.clone(),
2984                CodeUnitType::Field,
2985                scope.package_name.clone(),
2986                cpp_join_member_short(parent.short_name(), name),
2987                parent
2988                    .fq()
2989                    .clone()
2990                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
2991            );
2992            if self.parsed.contains_declaration(&code_unit) {
2993                continue;
2994            }
2995            self.parsed.add_code_unit(
2996                code_unit.clone(),
2997                node,
2998                self.source,
2999                Some(parent.clone()),
3000                None,
3001            );
3002            self.parsed.add_signature(code_unit, trimmed.to_string());
3003        }
3004    }
3005
3006    fn visit_function_definition<'tree>(
3007        &mut self,
3008        node: Node<'tree>,
3009        scope: &ScopeInfo,
3010        stack: &mut Vec<CppWork<'tree>>,
3011    ) {
3012        // A file-scope object-like macro sentinel the parser cannot see (issue
3013        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
3014        // prefixes as a bogus `function_definition` that swallows real namespaces,
3015        // classes, and members. Reparse the swallowed interior as C++ items so the
3016        // ordinary declaration visitors index it with byte/line-exact ownership.
3017        if self.visit_sentinel_macro_region(node, scope, stack) {
3018            return;
3019        }
3020        if node.has_error() {
3021            self.visit_macro_swallowed_function_declarations(node, scope);
3022        }
3023        if let Some((class_node, name, raw_supertypes)) =
3024            recover_exported_class_function_definition(node, self.source)
3025        {
3026            let body = cpp_body_node(class_node);
3027            let displaced_namespace = cpp_body_node(node)
3028                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
3029            let fragmented = cpp_body_node(node).and_then(|body| {
3030                fragmented_export_function_body_region(
3031                    node,
3032                    body,
3033                    self.source,
3034                    displaced_namespace.as_ref(),
3035                )
3036            });
3037            // The recovery tuple's first node is the class-like type when the
3038            // parser exposes one, but the synthetic wrapper owns the compound
3039            // statement that contains the truncated class body. Use the
3040            // wrapper body for fragmented-member detection; retain the
3041            // class-node body for the ordinary (non-fragmented) path below.
3042            if let Some(fragmented) = fragmented {
3043                // The lifted sibling no longer sits below the parser-visible
3044                // namespace node. Restore the current parent scope when the
3045                // ordinary work walk reaches that class.
3046                if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
3047                    .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
3048                {
3049                    let mut boundary_scope = scope.clone();
3050                    for sibling in cpp_following_named_siblings(node, self.source) {
3051                        if sibling.start_byte() >= boundary.start_byte() {
3052                            break;
3053                        }
3054                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
3055                            boundary_scope.visible_using_namespaces.push(namespace);
3056                        }
3057                    }
3058                    self.recovered_class_sibling_scopes
3059                        .insert(boundary.id(), boundary_scope);
3060                }
3061                let mut recovered_constructor = None;
3062                let mut recovered_prefix_tree = None;
3063                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
3064                {
3065                    Some(FragmentedExportMembers::Complete(tree)) => {
3066                        if let Some(body) = body
3067                            && let Some(range) =
3068                                cpp_reparsed_synthetic_initializer_constructor_range(
3069                                    tree.root_node(),
3070                                    &name,
3071                                    self.source,
3072                                    body.end_byte(),
3073                                )
3074                        {
3075                            recovered_constructor = Some(range);
3076                            recovered_prefix_tree = Some(tree);
3077                            None
3078                        } else {
3079                            Some(FragmentedExportMembers::Complete(tree))
3080                        }
3081                    }
3082                    outcome => outcome,
3083                };
3084                let mut class_stack = Vec::new();
3085                let class_unit = self.visit_named_class_like_shape(
3086                    class_node,
3087                    name,
3088                    None,
3089                    true,
3090                    Some(fragmented.class_range),
3091                    raw_supertypes,
3092                    scope,
3093                    &mut class_stack,
3094                );
3095                self.parsed
3096                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
3097                        recovery: fragmented.class_range,
3098                        unit: class_unit.clone(),
3099                    });
3100                let complete = outcome.is_some_and(|outcome| {
3101                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
3102                });
3103                if complete {
3104                    self.consumed_fragment_regions
3105                        .push((node.start_byte(), fragmented.class_range.end_byte));
3106                } else {
3107                    // The reparse can fail when the first constructor or a
3108                    // method body is split into statement-shaped siblings.
3109                    // Keep the recovered class envelope, but do not visit the
3110                    // synthetic wrapper body: its initializer expressions can
3111                    // look like same-named member functions (for example
3112                    // `Token.location(loc)`). Re-own only the original sibling
3113                    // nodes that fall inside the proven class range. Their CST
3114                    // shapes retain the real field/function kinds and ranges.
3115                    let member_scope = ScopeInfo {
3116                        package_name: class_unit.package_name().to_string(),
3117                        module: scope.module.clone(),
3118                        class_unit: Some(class_unit.clone()),
3119                        template_signature: scope.template_signature.clone(),
3120                        template_metadata: None,
3121                        declarations_are_fields: true,
3122                        recovered_specialization_member_scope: false,
3123                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
3124                    };
3125                    for candidate in cpp_following_named_siblings(node, self.source) {
3126                        if candidate.start_byte() >= fragmented.reparse_end {
3127                            break;
3128                        }
3129                        if cpp_fragment_sibling_is_class_member(
3130                            candidate,
3131                            fragmented.reparse_end,
3132                            self.source,
3133                        ) {
3134                            self.recovered_class_sibling_scopes
3135                                .insert(candidate.id(), member_scope.clone());
3136                        }
3137                    }
3138                    if let Some(range) = recovered_constructor
3139                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
3140                    {
3141                        self.visit_recovered_fragment_prefix_members(
3142                            prefix_tree.root_node(),
3143                            range.start,
3144                            &class_unit,
3145                            scope,
3146                        );
3147                        self.visit_recovered_fragment_constructor(
3148                            range,
3149                            body,
3150                            class_node,
3151                            &class_unit,
3152                            scope,
3153                        );
3154                    }
3155                }
3156                if let Some(boundary) = displaced_namespace {
3157                    for item in boundary.namespace_items {
3158                        self.recovered_class_sibling_scopes
3159                            .insert(item.id(), scope.clone());
3160                    }
3161                }
3162                stack.extend(class_stack);
3163                return;
3164            }
3165            let mut stack = Vec::new();
3166            let class_unit = self.visit_named_class_like_shape(
3167                class_node,
3168                name,
3169                body,
3170                body.is_some(),
3171                None,
3172                raw_supertypes,
3173                scope,
3174                &mut stack,
3175            );
3176            self.parsed
3177                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3178                    recovery: cpp_declaration_range(node),
3179                    unit: class_unit,
3180                });
3181            // Issue #1524: the bogus `function_definition` body can run past
3182            // the class's true closing brace (the parse ends it with a
3183            // zero-width `MISSING "}"`), swallowing following namespace-scope
3184            // siblings -- they would index as members of the recovered class.
3185            // When the body's text-balanced close lands before the body's own
3186            // end, re-own the swallowed tail with the outer scope instead.
3187            if let Some(body) = body
3188                && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
3189                && class_close < body.end_byte()
3190            {
3191                let split = {
3192                    let mut cursor = body.walk();
3193                    body.named_children(&mut cursor)
3194                        .position(|child| child.start_byte() > class_close)
3195                };
3196                if let Some(split) = split {
3197                    // The seeded work is a single Container over the whole
3198                    // body with the class scope; replace it with the bounded
3199                    // head (class scope) plus the swallowed tail (outer
3200                    // scope). Push tail first so the head drains first.
3201                    let seeded = stack.pop();
3202                    match seeded {
3203                        Some(CppWork::Container(container)) => {
3204                            push_cpp_sibling_range(
3205                                body,
3206                                split,
3207                                usize::MAX,
3208                                scope.clone(),
3209                                &mut stack,
3210                            );
3211                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
3212                        }
3213                        // visit_named_class_like_shape always seeds exactly
3214                        // one Container when a body is present.
3215                        _ => unreachable!("exported-class seed is always one Container"),
3216                    }
3217                }
3218            }
3219            while let Some(work) = stack.pop() {
3220                match work {
3221                    CppWork::Container(container) => {
3222                        push_cpp_container_work(container.node, container.scope, &mut stack);
3223                    }
3224                    CppWork::Siblings(siblings) => {
3225                        advance_cpp_siblings(siblings, self.source, &mut stack);
3226                    }
3227                    CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
3228                }
3229            }
3230            return;
3231        }
3232        let recovered_constraint_constructor =
3233            cpp_recovered_template_macro_constructor(node, self.source);
3234        let declarator = recovered_constraint_constructor
3235            .map(|(declarator, _)| declarator)
3236            .or_else(|| node.child_by_field_name("declarator"));
3237        let Some(declarator) = declarator else {
3238            self.visit_malformed_function_definition_container(node, scope, stack);
3239            return;
3240        };
3241        let Some(function_declarator) = extract_function_declarator(declarator) else {
3242            self.visit_malformed_function_definition_container(node, scope, stack);
3243            return;
3244        };
3245        let function = if let Some((_, callable_name)) =
3246            cpp_macro_displaced_callable_parts(function_declarator, self.source)
3247        {
3248            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
3249        } else {
3250            extract_function_info(function_declarator, self.source, scope)
3251        };
3252        let Some(mut function) = function else {
3253            self.visit_malformed_function_definition_container(node, scope, stack);
3254            return;
3255        };
3256        if let Some((_, template_parameter)) = recovered_constraint_constructor {
3257            function.signature = format!(
3258                "template <{}>{}",
3259                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
3260                function.signature
3261            );
3262        }
3263        let code_unit = function.code_unit(self.file.clone());
3264        // Keep an earlier same-file prototype as another physical occurrence
3265        // of this callable. `CodeUnit` already identifies the role-neutral
3266        // overload, while ranges and signature metadata describe its
3267        // declaration/definition occurrences.
3268        self.parsed
3269            .add_code_unit(code_unit.clone(), node, self.source, None, None);
3270        let signature = if recovered_constraint_constructor.is_some() {
3271            normalize_cpp_whitespace(node_text(function_declarator, self.source))
3272        } else {
3273            render_cpp_function_display_signature_from_node(
3274                node,
3275                self.source,
3276                scope.template_signature.as_deref(),
3277                true,
3278            )
3279        };
3280        self.parsed.add_signature_with_metadata(
3281            code_unit.clone(),
3282            cpp_signature_metadata(signature, function_declarator, self.source)
3283                .with_declaration_only(false)
3284                .with_callable_linkage(cpp_callable_linkage(node, self.source)),
3285        );
3286        if let Some(parent) = &scope.class_unit {
3287            self.parsed.add_child(parent.clone(), code_unit);
3288        } else if let Some(module) = &scope.module {
3289            self.parsed.add_child(module.clone(), code_unit);
3290        }
3291    }
3292
3293    /// Recover the namespace lost when tree-sitter promotes an export-macro
3294    /// class definition to a root-level `function_definition`.  Only a
3295    /// body-bearing, top-level recovery may borrow a namespace, and only when
3296    /// one earlier namespace-scope forward declaration proves the identity.
3297    fn scope_for_recovered_exported_class(
3298        &self,
3299        node: Node<'_>,
3300        name: &str,
3301        definition_body_present: bool,
3302        scope: &ScopeInfo,
3303    ) -> ScopeInfo {
3304        if !definition_body_present
3305            || !scope.package_name.is_empty()
3306            || scope.class_unit.is_some()
3307            || !(is_recovered_exported_class_container(node, self.source)
3308                || matches!(node.kind(), "declaration" | "field_declaration")
3309                    && recover_exported_class_declaration(node, self.source).is_some()
3310                || matches!(
3311                    node.kind(),
3312                    "class_specifier" | "struct_specifier" | "union_specifier"
3313                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
3314                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
3315                        name_node,
3316                        self.source,
3317                    )))
3318                }) || node.parent().is_some_and(|parent| {
3319                    matches!(parent.kind(), "declaration" | "field_declaration")
3320                        && recover_exported_class_declaration(parent, self.source).is_some()
3321                        || is_recovered_exported_class_container(parent, self.source)
3322                })) && class_like_name(node, self.source).as_deref() == Some(name))
3323        {
3324            return scope.clone();
3325        }
3326        let Some(package_name) = unique_earlier_cpp_namespace_forward(node, name, self.source)
3327        else {
3328            return scope.clone();
3329        };
3330
3331        let module = CodeUnit::new_fq(
3332            self.file.clone(),
3333            CodeUnitType::Module,
3334            "",
3335            package_name.clone(),
3336            cpp_namespace_fq(&package_name),
3337        );
3338        let mut recovered = scope.clone();
3339        recovered.package_name = package_name;
3340        recovered.module = Some(module);
3341        recovered
3342    }
3343
3344    fn visit_malformed_function_definition_container<'tree>(
3345        &mut self,
3346        node: Node<'tree>,
3347        scope: &ScopeInfo,
3348        stack: &mut Vec<CppWork<'tree>>,
3349    ) {
3350        let Some(body) = cpp_body_node(node) else {
3351            return;
3352        };
3353        if !cpp_contains_namespace_definition(body) {
3354            return;
3355        }
3356        stack.push(CppWork::Container(CppContainer {
3357            node: body,
3358            scope: scope.clone(),
3359        }));
3360    }
3361
3362    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
3363    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
3364    /// emits for a sentinel-prefixed region, reparse the interior after the
3365    /// sentinel identifier as real C++ items -- confined to the region so
3366    /// every reparsed node keeps its original byte/line position -- and run the
3367    /// ordinary container visitation over the result. Returns `true` when it fired
3368    /// (the caller must then skip normal function processing). Nested sentinel
3369    /// regions recover recursively: the reparsed interior is walked through the
3370    /// same `visit_function_definition` path, so a sentinel inside the region hits
3371    /// this recovery again.
3372    /// Runs `reparse_walk` and records every declaration it mints as a
3373    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
3374    /// `recovery` (issue #1657). A reparsed sentinel region has no single
3375    /// recovered envelope unit: the ordinary visitors mint namespaces,
3376    /// classes, and members directly from the reparsed tree, so the walk's
3377    /// declaration delta is the recovered set. Records are ordered by
3378    /// declaration start byte so the parse product stays deterministic.
3379    fn record_recovered_declarations(
3380        &mut self,
3381        recovery: Range,
3382        reparse_walk: impl FnOnce(&mut Self),
3383    ) {
3384        let before = self.parsed.declarations().clone();
3385        reparse_walk(self);
3386        let mut minted: Vec<CodeUnit> = self
3387            .parsed
3388            .declarations()
3389            .iter()
3390            .filter(|unit| !before.contains(*unit))
3391            .cloned()
3392            .collect();
3393        minted.sort_by_cached_key(|unit| {
3394            let start = self
3395                .parsed
3396                .declaration_ranges(unit)
3397                .first()
3398                .map(|range| range.start_byte)
3399                .unwrap_or(usize::MAX);
3400            (start, unit.fq_name().to_string())
3401        });
3402        for unit in minted {
3403            self.parsed
3404                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3405                    recovery,
3406                    unit,
3407                });
3408        }
3409    }
3410
3411    fn visit_sentinel_macro_region<'tree>(
3412        &mut self,
3413        node: Node<'tree>,
3414        scope: &ScopeInfo,
3415        stack: &mut Vec<CppWork<'tree>>,
3416    ) -> bool {
3417        if self.visit_nested_namespace_sentinel(node, scope) {
3418            return true;
3419        }
3420        if let Some((
3421            reparse_start,
3422            class_start,
3423            body_start,
3424            class_close_start,
3425            class_close_end,
3426            class_close_line,
3427        )) = cpp_sentinel_macro_class_region(node, self.source)
3428        {
3429            let Some(class_tree) =
3430                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
3431            else {
3432                return false;
3433            };
3434            let class_root = class_tree.root_node();
3435            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
3436            let Some(reparsed_class) =
3437                cpp_sentinel_reparsed_class(class_root, template_node, self.source)
3438            else {
3439                return false;
3440            };
3441            let class_node = reparsed_class.declaration_node;
3442            let name = reparsed_class.name;
3443            let mut class_scope = scope.clone();
3444            if let Some(template_node) = template_node {
3445                class_scope.template_signature =
3446                    cpp_template_signature(template_node, class_node, self.source);
3447                class_scope.template_metadata =
3448                    cpp_template_metadata(template_node, class_node, self.source);
3449            }
3450            let Some(body_tree) =
3451                cpp_reparse_region_items(self.source, body_start, class_close_start)
3452            else {
3453                return false;
3454            };
3455            let raw_supertypes = reparsed_class.raw_supertypes;
3456            let class_range = Range {
3457                start_byte: class_start,
3458                end_byte: class_close_end,
3459                start_line: class_node.start_position().row + 1,
3460                end_line: class_close_line,
3461            };
3462            let class_scope =
3463                self.scope_for_recovered_exported_class(class_node, &name, true, &class_scope);
3464            let mut class_stack = Vec::new();
3465            let class_unit = self.visit_named_class_like_shape(
3466                class_node,
3467                name,
3468                None,
3469                true,
3470                Some(class_range),
3471                raw_supertypes,
3472                &class_scope,
3473                &mut class_stack,
3474            );
3475            self.parsed
3476                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3477                    recovery: class_range,
3478                    unit: class_unit.clone(),
3479                });
3480            let member_scope = ScopeInfo {
3481                package_name: class_scope.package_name.clone(),
3482                module: class_scope.module.clone(),
3483                class_unit: Some(class_unit),
3484                template_signature: class_scope.template_signature.clone(),
3485                template_metadata: None,
3486                declarations_are_fields: true,
3487                recovered_specialization_member_scope: false,
3488                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
3489            };
3490            self.run_container_work(body_tree.root_node(), member_scope);
3491            // Register only after the padded body reparse: its nodes deliberately
3492            // retain offsets inside the consumed region and must be visited first.
3493            self.consumed_fragment_regions
3494                .push((node.start_byte(), class_close_end));
3495            // An ERROR envelope can hold real sibling declarations after the
3496            // recovered class's close (the suffix-reparse boundary in
3497            // `cpp_sentinel_macro_class_region` partitions, it does not
3498            // consume). Walk the envelope's remaining children normally; the
3499            // consumed region above keeps the recovered class from being
3500            // indexed twice.
3501            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
3502                stack.push(CppWork::Container(CppContainer {
3503                    node,
3504                    scope: scope.clone(),
3505                }));
3506            }
3507            return true;
3508        }
3509        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
3510            return false;
3511        };
3512        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
3513            return false;
3514        };
3515        let root = tree.root_node();
3516        if !cpp_reparsed_items_are_indexable(root, self.source) {
3517            return false;
3518        }
3519        let recovery = cpp_recovery_window(self.source, start, end);
3520        self.record_recovered_declarations(recovery, |visitor| {
3521            visitor.visit_container(
3522                root,
3523                &scope.package_name,
3524                scope.module.clone(),
3525                scope.class_unit.clone(),
3526                scope.template_signature.clone(),
3527                scope.visible_using_namespaces.clone(),
3528            );
3529        });
3530        if end > node.end_byte() {
3531            self.consumed_fragment_regions
3532                .push((node.start_byte(), end));
3533        } else if node.kind() == "ERROR" && node.end_byte() > end {
3534            // The sentinel region ended at the first recovered class-like item
3535            // but the ERROR envelope keeps real sibling declarations after it
3536            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
3537            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
3538            // envelope's remaining children normally; the consumed region
3539            // keeps the reparsed prefix from being indexed twice.
3540            self.consumed_fragment_regions
3541                .push((node.start_byte(), end));
3542            stack.push(CppWork::Container(CppContainer {
3543                node,
3544                scope: scope.clone(),
3545            }));
3546        }
3547        true
3548    }
3549
3550    /// Re-own complete class declarations from the structured Abseil
3551    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
3552    /// its direct CST children already prove both namespace components and the
3553    /// class bodies, so the ordinary class/member visitor can retain ownership
3554    /// and exact source ranges without admitting unrelated callable bodies.
3555    fn visit_nested_namespace_sentinel(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
3556        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source) else {
3557            return false;
3558        };
3559
3560        let mut package_name = scope.package_name.clone();
3561        let mut module = scope.module.clone();
3562        for component in recovered.namespace_components {
3563            package_name = if package_name.is_empty() {
3564                component
3565            } else {
3566                format!("{package_name}::{component}")
3567            };
3568            let namespace_module = CodeUnit::new_fq(
3569                self.file.clone(),
3570                CodeUnitType::Module,
3571                "",
3572                package_name.clone(),
3573                cpp_namespace_fq(&package_name),
3574            );
3575            if !self.parsed.contains_declaration(&namespace_module) {
3576                self.parsed.add_code_unit(
3577                    namespace_module.clone(),
3578                    recovered.function,
3579                    self.source,
3580                    None,
3581                    None,
3582                );
3583            }
3584            module = Some(namespace_module);
3585        }
3586
3587        let recovered_scope = ScopeInfo {
3588            package_name,
3589            module,
3590            class_unit: scope.class_unit.clone(),
3591            template_signature: scope.template_signature.clone(),
3592            template_metadata: scope.template_metadata.clone(),
3593            declarations_are_fields: false,
3594            recovered_specialization_member_scope: false,
3595            visible_using_namespaces: scope.visible_using_namespaces.clone(),
3596        };
3597        if let Some(fragmented) =
3598            cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, self.source)
3599        {
3600            let mut class_scope = recovered_scope.clone();
3601            if let Some(template_node) = fragmented.template_node {
3602                class_scope.template_signature =
3603                    cpp_template_signature(template_node, fragmented.class_node, self.source);
3604                class_scope.template_metadata =
3605                    cpp_template_metadata(template_node, fragmented.class_node, self.source);
3606            }
3607            if let Some(outcome) = self
3608                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
3609            {
3610                let mut class_stack = Vec::new();
3611                let class_unit = self.visit_named_class_like_shape(
3612                    fragmented.class_node,
3613                    fragmented.name.clone(),
3614                    None,
3615                    true,
3616                    Some(fragmented.fragmented.class_range),
3617                    fragmented.raw_supertypes.clone(),
3618                    &class_scope,
3619                    &mut class_stack,
3620                );
3621                self.parsed
3622                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
3623                        recovery: fragmented.fragmented.class_range,
3624                        unit: class_unit.clone(),
3625                    });
3626                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
3627                    self.consumed_fragment_regions.push((
3628                        fragmented.consumed_start,
3629                        fragmented.fragmented.class_range.end_byte,
3630                    ));
3631                }
3632            }
3633        }
3634        // The class requirement above is the admission gate; once admitted,
3635        // traverse the whole proven inner namespace body so sibling aliases,
3636        // functions, and variables are not silently discarded.
3637        self.run_container_work(recovered.body, recovered_scope);
3638        true
3639    }
3640
3641    fn visit_declaration<'tree>(
3642        &mut self,
3643        node: Node<'tree>,
3644        scope: &ScopeInfo,
3645        in_class_body: bool,
3646        stack: &mut Vec<CppWork<'tree>>,
3647    ) {
3648        if self.visit_sentinel_macro_region(node, scope, stack) {
3649            return;
3650        }
3651        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
3652            !cpp_active_template_type_parameter(
3653                node,
3654                node_text(declarator, self.source),
3655                self.source,
3656            )
3657        }) {
3658            return;
3659        }
3660        if in_class_body
3661            && let Some(parent) = scope.class_unit.as_ref()
3662            && let Some(call) =
3663                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
3664        {
3665            self.visit_recovered_macro_qualified_constructor_definition(node, call, scope);
3666            return;
3667        }
3668        if in_class_body
3669            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
3670        {
3671            self.visit_recovered_macro_qualified_function_declaration(node, call, scope);
3672            return;
3673        }
3674        if in_class_body
3675            && let Some(declarators) =
3676                recovered_macro_qualified_field_declarators(node, self.source)
3677        {
3678            for declarator in declarators {
3679                self.visit_variable_declaration(node, declarator, scope, true);
3680            }
3681            return;
3682        }
3683        let recovered_alias_names = recovered_type_alias_names(node, self.source);
3684        if !recovered_alias_names.is_empty() {
3685            self.add_type_aliases(node, scope, recovered_alias_names);
3686            return;
3687        }
3688
3689        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
3690            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
3691                // Issue #938: the members tree-sitter scattered out of the fragmented
3692                // multiple-base export node are reparsed from their true body region
3693                // and re-owned as members of the recovered class, with an explicit
3694                // navigation range spanning to the displaced closing brace.
3695                if let Some(outcome) =
3696                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
3697                {
3698                    let consumed_region = (
3699                        recovered.declaration_node.end_byte(),
3700                        fragmented.class_range.end_byte,
3701                    );
3702                    let code_unit = self.visit_named_class_like_shape(
3703                        recovered.declaration_node,
3704                        recovered.name,
3705                        None,
3706                        true,
3707                        Some(fragmented.class_range),
3708                        recovered.raw_supertypes,
3709                        scope,
3710                        stack,
3711                    );
3712                    self.parsed.record_materialization(
3713                        MaterializationRecord::RecoveredDeclaration {
3714                            recovery: fragmented.class_range,
3715                            unit: code_unit.clone(),
3716                        },
3717                    );
3718                    let consume_fragment =
3719                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
3720                    // Everything between the fragmented declaration and its displaced
3721                    // closing brace now belongs to the recovered class; keep the
3722                    // ordinary walk from re-indexing those scattered siblings at top
3723                    // level. Register the consumed region only after indexing because
3724                    // the reparsed nodes retain byte offsets inside that same region.
3725                    if consume_fragment {
3726                        self.consumed_fragment_regions.push(consumed_region);
3727                    }
3728                    return;
3729                }
3730            }
3731            let uses_initializer_body = recovered.uses_initializer_body;
3732            let definition_body_present = recovered.body.is_some();
3733            let class_unit = self.visit_named_class_like_shape(
3734                recovered.declaration_node,
3735                recovered.name,
3736                recovered.body,
3737                definition_body_present,
3738                None,
3739                recovered.raw_supertypes,
3740                scope,
3741                stack,
3742            );
3743            self.parsed
3744                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3745                    recovery: cpp_declaration_range(node),
3746                    unit: class_unit,
3747                });
3748            if uses_initializer_body {
3749                return;
3750            }
3751        }
3752
3753        let mut handled_function = false;
3754        let mut handled_declarator = false;
3755        let mut cursor = node.walk();
3756        for child in node.named_children(&mut cursor) {
3757            if matches!(
3758                child.kind(),
3759                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3760            ) {
3761                // A named class-like definition remains a declaration even when
3762                // the same statement also declares an object, for example
3763                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
3764                // declaration's type and `kind` as its declarator.  Dropping the
3765                // type here loses both its nested owner and every later lexical
3766                // reference to it.  A body is the structured proof that this is
3767                // a definition rather than an elaborated type use such as
3768                // `class Kind value;`.
3769                if cpp_body_node(child).is_some() {
3770                    self.visit_class_like(child, scope, stack);
3771                }
3772                continue;
3773            }
3774        }
3775
3776        let mut cursor = node.walk();
3777        for child in node.children_by_field_name("declarator", &mut cursor) {
3778            if crate::structural::is_recovered_designator_init_declarator(child) {
3779                handled_declarator = true;
3780                continue;
3781            }
3782            if let Some(kind) = classify_declarator(child) {
3783                handled_declarator = true;
3784                match kind {
3785                    DeclaratorKind::Function(function_declarator) => {
3786                        handled_function = true;
3787                        self.visit_function_declaration(node, function_declarator, scope);
3788                    }
3789                    DeclaratorKind::Variable(variable_declarator) => {
3790                        self.visit_variable_declaration(
3791                            node,
3792                            variable_declarator,
3793                            scope,
3794                            in_class_body,
3795                        );
3796                    }
3797                }
3798            }
3799        }
3800
3801        if !handled_declarator {
3802            let mut cursor = node.walk();
3803            for child in node.named_children(&mut cursor) {
3804                if crate::structural::is_recovered_designator_init_declarator(child) {
3805                    handled_declarator = true;
3806                    continue;
3807                }
3808                if !is_unfielded_declarator_candidate(child) {
3809                    continue;
3810                }
3811                let Some(kind) = classify_declarator(child) else {
3812                    continue;
3813                };
3814                handled_declarator = true;
3815                match kind {
3816                    DeclaratorKind::Function(function_declarator) => {
3817                        handled_function = true;
3818                        self.visit_function_declaration(node, function_declarator, scope);
3819                    }
3820                    DeclaratorKind::Variable(variable_declarator) => {
3821                        self.visit_variable_declaration(
3822                            node,
3823                            variable_declarator,
3824                            scope,
3825                            in_class_body,
3826                        );
3827                    }
3828                }
3829            }
3830        }
3831
3832        if handled_function {
3833            return;
3834        }
3835
3836        if !handled_declarator {
3837            if in_class_body {
3838                self.visit_class_members_from_declaration(node, scope);
3839            } else {
3840                self.visit_global_variables_from_declaration(node, scope);
3841            }
3842        }
3843    }
3844
3845    fn visit_function_declaration(
3846        &mut self,
3847        declaration_node: Node<'_>,
3848        declarator: Node<'_>,
3849        scope: &ScopeInfo,
3850    ) {
3851        let Some(function) = extract_function_info(declarator, self.source, scope) else {
3852            return;
3853        };
3854        let code_unit =
3855            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
3856        if self.parsed.contains_declaration(&code_unit) {
3857            self.parsed
3858                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3859            return;
3860        }
3861        self.parsed
3862            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3863        let signature = render_cpp_function_display_signature_from_node(
3864            declaration_node,
3865            self.source,
3866            scope.template_signature.as_deref(),
3867            false,
3868        );
3869        self.parsed.add_signature_with_metadata(
3870            code_unit.clone(),
3871            cpp_signature_metadata(signature, declarator, self.source)
3872                .with_declaration_only(true)
3873                .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source)),
3874        );
3875        if let Some(parent) = &scope.class_unit {
3876            self.parsed.add_child(parent.clone(), code_unit);
3877        } else if let Some(module) = &scope.module {
3878            self.parsed.add_child(module.clone(), code_unit);
3879        }
3880    }
3881
3882    fn visit_recovered_macro_qualified_function_declaration(
3883        &mut self,
3884        declaration_node: Node<'_>,
3885        call: Node<'_>,
3886        scope: &ScopeInfo,
3887    ) {
3888        let Some(parent) = &scope.class_unit else {
3889            return;
3890        };
3891        let Some(name_node) = call.child_by_field_name("function") else {
3892            return;
3893        };
3894        let Some(arguments) = call.child_by_field_name("arguments") else {
3895            return;
3896        };
3897        let Some((signature, parameter_labels)) =
3898            recovered_macro_qualified_function_parameters(arguments, self.source)
3899        else {
3900            return;
3901        };
3902        let arity = parameter_labels.len();
3903        let function = FunctionInfo {
3904            package_name: scope.package_name.clone(),
3905            owner_path: Some(parent.short_name().to_string()),
3906            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
3907            signature,
3908        };
3909        if function.name.is_empty() {
3910            return;
3911        }
3912        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3913        if self.parsed.contains_declaration(&code_unit) {
3914            self.parsed
3915                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3916            return;
3917        }
3918        self.parsed
3919            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3920        let signature_label = render_cpp_function_display_signature_from_node(
3921            declaration_node,
3922            self.source,
3923            scope.template_signature.as_deref(),
3924            false,
3925        );
3926        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3927            .with_declaration_only(true)
3928            .with_callable_arity(CallableArity::exact(arity))
3929            .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3930        self.parsed
3931            .add_signature_with_metadata(code_unit.clone(), metadata);
3932        self.parsed.add_child(parent.clone(), code_unit);
3933    }
3934
3935    fn visit_recovered_macro_qualified_constructor_definition(
3936        &mut self,
3937        declaration_node: Node<'_>,
3938        call: Node<'_>,
3939        scope: &ScopeInfo,
3940    ) {
3941        let Some(parent) = &scope.class_unit else {
3942            return;
3943        };
3944        let Some(arguments) = call.child_by_field_name("arguments") else {
3945            return;
3946        };
3947        let Some((mut signature, parameter_labels)) =
3948            recovered_macro_qualified_function_parameters(arguments, self.source)
3949        else {
3950            return;
3951        };
3952        if let Some(template_signature) = &scope.template_signature {
3953            signature = format!("{template_signature}{signature}");
3954        }
3955        let arity = parameter_labels.len();
3956        let function = FunctionInfo {
3957            package_name: scope.package_name.clone(),
3958            owner_path: Some(parent.short_name().to_string()),
3959            name: parent.identifier().to_string(),
3960            signature,
3961        };
3962        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3963        self.parsed
3964            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3965        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
3966        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3967            .with_declaration_only(false)
3968            .with_callable_arity(CallableArity::exact(arity))
3969            .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3970        self.parsed
3971            .add_signature_with_metadata(code_unit.clone(), metadata);
3972        self.parsed.add_child(parent.clone(), code_unit);
3973    }
3974
3975    fn visit_variable_declaration(
3976        &mut self,
3977        declaration_node: Node<'_>,
3978        declarator: Node<'_>,
3979        scope: &ScopeInfo,
3980        in_class_body: bool,
3981    ) {
3982        let Some(name) = extract_variable_name(declarator, self.source) else {
3983            return;
3984        };
3985        let parent = if in_class_body {
3986            let Some(parent) = &scope.class_unit else {
3987                return;
3988            };
3989            Some(parent)
3990        } else {
3991            None
3992        };
3993        let short_name = match parent {
3994            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
3995            None => name.clone(),
3996        };
3997        let fq = cpp_leaf_fq(
3998            &scope.package_name,
3999            parent,
4000            &name,
4001            SegmentKind::Member,
4002            SegmentKind::Member,
4003        );
4004        let code_unit = CodeUnit::new_fq(
4005            self.file.clone(),
4006            CodeUnitType::Field,
4007            scope.package_name.clone(),
4008            short_name,
4009            fq,
4010        );
4011        if self.parsed.contains_declaration(&code_unit) {
4012            return;
4013        }
4014        self.parsed
4015            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4016        self.parsed.add_signature_with_metadata(
4017            code_unit.clone(),
4018            SignatureMetadata::new(
4019                render_cpp_field_signature(declaration_node, declarator, self.source),
4020                Vec::new(),
4021            )
4022            .with_cpp_field_linkage(cpp_field_declaration_linkage(declaration_node, self.source)),
4023        );
4024        if let Some(parent) = &scope.class_unit {
4025            self.parsed.add_child(parent.clone(), code_unit);
4026        } else if let Some(module) = &scope.module {
4027            self.parsed.add_child(module.clone(), code_unit);
4028        }
4029    }
4030
4031    fn visit_class_members_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4032        let mut cursor = node.walk();
4033        for child in node.named_children(&mut cursor) {
4034            if child.kind() == "init_declarator"
4035                && let Some(inner) = child.child_by_field_name("declarator")
4036            {
4037                self.visit_variable_declaration(node, inner, scope, true);
4038            } else if matches!(
4039                child.kind(),
4040                "identifier"
4041                    | "field_identifier"
4042                    | "pointer_declarator"
4043                    | "reference_declarator"
4044                    | "array_declarator"
4045                    | "parenthesized_declarator"
4046            ) {
4047                self.visit_variable_declaration(node, child, scope, true);
4048            }
4049        }
4050    }
4051
4052    fn visit_global_variables_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4053        let mut cursor = node.walk();
4054        for child in node.named_children(&mut cursor) {
4055            if child.kind() == "init_declarator"
4056                && let Some(inner) = child.child_by_field_name("declarator")
4057            {
4058                self.visit_variable_declaration(node, inner, scope, false);
4059            } else if matches!(
4060                child.kind(),
4061                "identifier"
4062                    | "field_identifier"
4063                    | "pointer_declarator"
4064                    | "reference_declarator"
4065                    | "array_declarator"
4066                    | "parenthesized_declarator"
4067            ) {
4068                self.visit_variable_declaration(node, child, scope, false);
4069            }
4070        }
4071    }
4072
4073    fn visit_include(&mut self, node: Node<'_>) {
4074        let raw = normalize_cpp_whitespace(node_text(node, self.source));
4075        self.parsed.imports.push(ImportInfo {
4076            raw_snippet: raw,
4077            is_wildcard: false,
4078            is_global: false,
4079            identifier: None,
4080            alias: None,
4081            path: None,
4082            binder_span: None,
4083        });
4084    }
4085
4086    fn visit_type_declaration<'tree>(
4087        &mut self,
4088        node: Node<'tree>,
4089        scope: &ScopeInfo,
4090        stack: &mut Vec<CppWork<'tree>>,
4091    ) {
4092        if let Some(type_node) = node.child_by_field_name("type")
4093            && matches!(
4094                type_node.kind(),
4095                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
4096            )
4097        {
4098            self.visit_class_like(type_node, scope, stack);
4099        }
4100
4101        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
4102            let range = Range {
4103                start_byte: node.start_byte(),
4104                end_byte: recovered.end_node.end_byte(),
4105                start_line: node.start_position().row + 1,
4106                end_line: recovered.end_node.end_position().row + 1,
4107            };
4108            let signature = self
4109                .source
4110                .get(range.start_byte..range.end_byte)
4111                .map(normalize_cpp_whitespace)
4112                .unwrap_or_default();
4113            self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
4114            return;
4115        }
4116
4117        let alias_names = match node.kind() {
4118            "alias_declaration" => extract_alias_declaration_name(node, self.source)
4119                .into_iter()
4120                .collect::<Vec<_>>(),
4121            "type_definition" => extract_typedef_alias_names(node, self.source),
4122            _ => Vec::new(),
4123        };
4124        self.add_type_aliases(node, scope, alias_names);
4125    }
4126
4127    fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
4128        let signature = normalize_cpp_whitespace(node_text(node, self.source));
4129        self.record_type_aliases(
4130            node,
4131            scope,
4132            alias_names,
4133            signature,
4134            cpp_declaration_range(node),
4135        );
4136    }
4137
4138    fn record_type_aliases(
4139        &mut self,
4140        node: Node<'_>,
4141        scope: &ScopeInfo,
4142        alias_names: Vec<String>,
4143        signature: String,
4144        range: Range,
4145    ) {
4146        if signature.is_empty() {
4147            return;
4148        }
4149        let type_name = node
4150            .child_by_field_name("type")
4151            .and_then(|type_node| type_node.child_by_field_name("name"))
4152            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
4153        for alias_name in alias_names {
4154            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
4155                continue;
4156            }
4157            let short_name = if let Some(parent) = &scope.class_unit {
4158                cpp_join_nested_short(parent.short_name(), &alias_name)
4159            } else {
4160                alias_name.clone()
4161            };
4162            let fq = cpp_leaf_fq(
4163                &scope.package_name,
4164                scope.class_unit.as_ref(),
4165                &alias_name,
4166                SegmentKind::Nested,
4167                SegmentKind::Type,
4168            );
4169            let code_unit = CodeUnit::with_signature_and_fq(
4170                self.file.clone(),
4171                CodeUnitType::Class,
4172                scope.package_name.clone(),
4173                short_name,
4174                Some(signature.clone()),
4175                false,
4176                fq,
4177            );
4178            // Declaration identity does not include the alias signature. Keep
4179            // each physical range so conditional aliases retain their guards.
4180            self.parsed
4181                .add_code_unit_with_range(code_unit.clone(), range, None, None);
4182            self.parsed
4183                .add_signature(code_unit.clone(), signature.clone());
4184            if let Some(metadata) = &scope.template_metadata {
4185                let mut metadata = metadata.clone();
4186                metadata.primary_fq_name = code_unit.fq_name();
4187                self.parsed
4188                    .set_cpp_template_metadata(code_unit.clone(), metadata);
4189            }
4190            if let Some(parent) = &scope.class_unit {
4191                self.parsed.add_child(parent.clone(), code_unit.clone());
4192            } else if let Some(module) = &scope.module {
4193                self.parsed.add_child(module.clone(), code_unit.clone());
4194            }
4195            self.parsed.mark_type_alias(code_unit);
4196        }
4197    }
4198
4199    fn visit_macro(&mut self, node: Node<'_>) {
4200        let Some(name) = extract_macro_name(node, self.source) else {
4201            return;
4202        };
4203        let signature = node_text(node, self.source).trim_end().to_string();
4204        if signature.is_empty() {
4205            return;
4206        }
4207        let fq = cpp_member_fq("", &name);
4208        let code_unit = CodeUnit::new_fq(self.file.clone(), CodeUnitType::Macro, "", name, fq);
4209        if self.parsed.contains_declaration_identity(&code_unit) {
4210            return;
4211        }
4212        self.parsed
4213            .add_code_unit(code_unit.clone(), node, self.source, None, None);
4214        let name_range = node
4215            .child_by_field_name("name")
4216            .map(cpp_declaration_range)
4217            .unwrap_or_else(|| cpp_declaration_range(node));
4218        self.parsed
4219            .record_materialization(MaterializationRecord::GeneratedDeclaration {
4220                site: cpp_declaration_range(node),
4221                argument: name_range,
4222                kind: GenerationKind::PreprocessorDefinition,
4223                unit: code_unit.clone(),
4224            });
4225        self.parsed.add_signature(code_unit, signature);
4226    }
4227}
4228
4229/// Classify a C++ field while its declaration syntax is already available.
4230///
4231/// The persisted result lets later visibility queries avoid reparsing the
4232/// complete source file only to recover linkage.
4233pub fn cpp_field_declaration_linkage(declaration: Node<'_>, source: &str) -> CppFieldLinkage {
4234    let mut current = declaration.parent();
4235    let mut enclosed_by_class = false;
4236    while let Some(node) = current {
4237        if node.kind() == "namespace_definition"
4238            && node
4239                .child_by_field_name("name")
4240                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4241        {
4242            return CppFieldLinkage::Internal;
4243        }
4244        if matches!(
4245            node.kind(),
4246            "class_specifier" | "struct_specifier" | "union_specifier"
4247        ) && node
4248            .child_by_field_name("name")
4249            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4250        {
4251            return CppFieldLinkage::Internal;
4252        }
4253        if matches!(
4254            node.kind(),
4255            "class_specifier" | "struct_specifier" | "union_specifier"
4256        ) {
4257            enclosed_by_class = true;
4258        }
4259        if matches!(node.kind(), "function_definition" | "lambda_expression") {
4260            return CppFieldLinkage::Internal;
4261        }
4262        current = node.parent();
4263    }
4264    if enclosed_by_class {
4265        return CppFieldLinkage::External;
4266    }
4267    let mut cursor = declaration.walk();
4268    let mut has_static = false;
4269    let mut has_extern = false;
4270    let mut has_inline = false;
4271    let mut has_const = false;
4272    let mut has_constexpr = false;
4273    for child in declaration.named_children(&mut cursor) {
4274        let text = normalize_cpp_whitespace(node_text(child, source));
4275        match (child.kind(), text.as_str()) {
4276            ("storage_class_specifier", "static") => has_static = true,
4277            ("storage_class_specifier", "extern") => has_extern = true,
4278            ("storage_class_specifier", "inline") => has_inline = true,
4279            ("storage_class_specifier", "constexpr") => has_constexpr = true,
4280            ("type_qualifier", "const") => has_const = true,
4281            ("type_qualifier", "constexpr") => has_constexpr = true,
4282            _ => {}
4283        }
4284    }
4285    if has_static {
4286        CppFieldLinkage::Internal
4287    } else if has_extern || has_inline {
4288        CppFieldLinkage::External
4289    } else if has_const || has_constexpr {
4290        CppFieldLinkage::InternalUnlessExternalPeer
4291    } else {
4292        CppFieldLinkage::External
4293    }
4294}
4295
4296fn cpp_declaration_range(node: Node<'_>) -> Range {
4297    Range {
4298        start_byte: node.start_byte(),
4299        end_byte: node.end_byte(),
4300        start_line: node.start_position().row + 1,
4301        end_line: node.end_position().row + 1,
4302    }
4303}
4304
4305/// A recovery interval as a [`Range`], for materialization records whose
4306/// window is a byte region rather than one parser node (the sentinel-macro
4307/// region reparses, issue #941/#1657).
4308fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
4309    let line_at = |byte: usize| {
4310        source.as_bytes()[..byte]
4311            .iter()
4312            .filter(|&&b| b == b'\n')
4313            .count()
4314            + 1
4315    };
4316    Range {
4317        start_byte,
4318        end_byte,
4319        start_line: line_at(start_byte),
4320        end_line: line_at(end_byte),
4321    }
4322}
4323
4324pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
4325    let mut in_block_comment = false;
4326    for line in source.lines() {
4327        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
4328        let trimmed = stripped.trim();
4329        if !looks_like_quoted_include_line(trimmed) {
4330            continue;
4331        }
4332
4333        let raw = normalize_cpp_whitespace(trimmed);
4334        // The tree-sitter walk already recorded every `#include` it could see;
4335        // this line scan only recovers the ones a parse error hid, so skip a
4336        // snippet that is already an import binding.
4337        if parsed
4338            .imports
4339            .iter()
4340            .any(|import| import.raw_snippet == raw)
4341        {
4342            continue;
4343        }
4344
4345        parsed.imports.push(ImportInfo {
4346            raw_snippet: raw,
4347            is_wildcard: false,
4348            is_global: false,
4349            identifier: None,
4350            alias: None,
4351            path: None,
4352            binder_span: None,
4353        });
4354    }
4355}
4356
4357fn looks_like_quoted_include_line(line: &str) -> bool {
4358    let Some(rest) = line.trim_start().strip_prefix('#') else {
4359        return false;
4360    };
4361    let Some(rest) = rest.trim_start().strip_prefix("include") else {
4362        return false;
4363    };
4364    rest.trim_start().starts_with('"')
4365}
4366
4367fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
4368    let mut raw = Vec::new();
4369    let mut cursor = node.walk();
4370    for child in node.named_children(&mut cursor) {
4371        if child.kind() == "base_class_clause" {
4372            collect_cpp_base_nodes(child, source, &mut raw);
4373        }
4374    }
4375    raw
4376}
4377
4378fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
4379    walk_named_tree_preorder(node, false, |child| match child.kind() {
4380        "type_identifier" | "qualified_identifier" | "template_type" => {
4381            let text = normalize_cpp_whitespace(node_text(child, source));
4382            if !text.is_empty() {
4383                raw.push(text);
4384            }
4385            WalkControl::SkipChildren
4386        }
4387        _ => WalkControl::Continue,
4388    });
4389}
4390
4391fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
4392    let mut out = String::new();
4393    let chars: Vec<char> = line.chars().collect();
4394    let mut index = 0;
4395    let mut in_string = false;
4396    let mut in_char = false;
4397    let mut escape = false;
4398
4399    while index < chars.len() {
4400        let ch = chars[index];
4401        let next = chars.get(index + 1).copied();
4402
4403        if *in_block_comment {
4404            if ch == '*' && next == Some('/') {
4405                *in_block_comment = false;
4406                index += 2;
4407            } else {
4408                index += 1;
4409            }
4410            continue;
4411        }
4412
4413        if in_string {
4414            out.push(ch);
4415            if escape {
4416                escape = false;
4417            } else if ch == '\\' {
4418                escape = true;
4419            } else if ch == '"' {
4420                in_string = false;
4421            }
4422            index += 1;
4423            continue;
4424        }
4425
4426        if in_char {
4427            out.push(ch);
4428            if escape {
4429                escape = false;
4430            } else if ch == '\\' {
4431                escape = true;
4432            } else if ch == '\'' {
4433                in_char = false;
4434            }
4435            index += 1;
4436            continue;
4437        }
4438
4439        if ch == '/' && next == Some('/') {
4440            break;
4441        }
4442        if ch == '/' && next == Some('*') {
4443            *in_block_comment = true;
4444            index += 2;
4445            continue;
4446        }
4447        if ch == '"' {
4448            in_string = true;
4449            out.push(ch);
4450            index += 1;
4451            continue;
4452        }
4453        if ch == '\'' {
4454            in_char = true;
4455            out.push(ch);
4456            index += 1;
4457            continue;
4458        }
4459
4460        out.push(ch);
4461        index += 1;
4462    }
4463
4464    out
4465}
4466
4467#[derive(Clone)]
4468struct FunctionInfo {
4469    package_name: String,
4470    owner_path: Option<String>,
4471    name: String,
4472    signature: String,
4473}
4474
4475enum DeclaratorKind<'a> {
4476    Function(Node<'a>),
4477    Variable(Node<'a>),
4478}
4479
4480impl FunctionInfo {
4481    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
4482        self.code_unit_with_synthetic(file, false)
4483    }
4484
4485    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
4486        let short_name = if let Some(owner) = &self.owner_path {
4487            format!("{owner}.{}", self.name)
4488        } else {
4489            self.name.clone()
4490        };
4491        let fq = cpp_member_fq(&self.package_name, &short_name);
4492        CodeUnit::with_signature_and_fq(
4493            file,
4494            CodeUnitType::Function,
4495            self.package_name.clone(),
4496            short_name,
4497            Some(self.signature.clone()),
4498            synthetic,
4499            fq,
4500        )
4501    }
4502}
4503
4504fn extract_function_info(
4505    declarator: Node<'_>,
4506    source: &str,
4507    scope: &ScopeInfo,
4508) -> Option<FunctionInfo> {
4509    let parameters_node = declarator.child_by_field_name("parameters")?;
4510    let declarator_name_node = declarator
4511        .child_by_field_name("declarator")
4512        .or_else(|| parameters_node.prev_named_sibling())?;
4513    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
4514}
4515
4516fn extract_function_info_from_name(
4517    declarator: Node<'_>,
4518    declarator_name_node: Node<'_>,
4519    source: &str,
4520    scope: &ScopeInfo,
4521) -> Option<FunctionInfo> {
4522    let parameters_node = declarator.child_by_field_name("parameters")?;
4523    let parameters_text = cpp_parameter_signature(parameters_node, source);
4524    let recovered_specialization_member = scope
4525        .recovered_specialization_member_scope
4526        .then(|| {
4527            let terminal = declarator_name_node
4528                .child_by_field_name("name")
4529                .unwrap_or(declarator_name_node);
4530            let name = canonical_cpp_qualified_component(terminal, source)?.name;
4531            let owner = scope.class_unit.as_ref()?;
4532            Some((
4533                Some(owner.short_name().to_string()),
4534                name,
4535                scope.package_name.clone(),
4536            ))
4537        })
4538        .flatten();
4539    let (owner_path, name, package_name) = if let Some(parts) = recovered_specialization_member {
4540        parts
4541    } else if let Some(parts) =
4542        split_structured_templated_cpp_name(declarator_name_node, source, scope)
4543    {
4544        parts
4545    } else {
4546        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
4547            declarator_name_node,
4548            source,
4549        )?);
4550        if raw_name.is_empty() {
4551            return None;
4552        }
4553        split_cpp_name(&raw_name, scope)
4554    };
4555    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
4556    let mut signature = if suffix.is_empty() {
4557        parameters_text
4558    } else {
4559        format!("{parameters_text} {suffix}")
4560    };
4561    if let Some(template_signature) = &scope.template_signature {
4562        signature = format!("{template_signature}{signature}");
4563    }
4564
4565    Some(FunctionInfo {
4566        package_name,
4567        owner_path,
4568        name,
4569        signature,
4570    })
4571}
4572
4573/// Recover the semantic return type and callable name when a declaration macro
4574/// occupies a function definition's `type` field. Tree-sitter either exposes a
4575/// scalar return as the declarator's apparent name and the callable as the sole
4576/// identifier in an `ERROR`, or joins a template return and callable into a
4577/// qualified identifier with a missing `::`. Both shapes retain the complete
4578/// parameter list and body; a concrete separator remains an out-of-line member.
4579fn cpp_macro_displaced_callable_parts<'tree>(
4580    function_declarator: Node<'tree>,
4581    source: &str,
4582) -> Option<(Node<'tree>, Node<'tree>)> {
4583    let definition = function_declarator.parent()?;
4584    if definition.kind() != "function_definition"
4585        || definition.child_by_field_name("declarator") != Some(function_declarator)
4586        || definition
4587            .child_by_field_name("body")
4588            .is_none_or(|body| body.kind() != "compound_statement")
4589    {
4590        return None;
4591    }
4592    let macro_type = definition.child_by_field_name("type")?;
4593    if macro_type.kind() != "type_identifier"
4594        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
4595    {
4596        return None;
4597    }
4598
4599    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
4600    if apparent_return_type.kind() == "qualified_identifier"
4601        && let (Some(return_type), Some(callable_name)) = (
4602            apparent_return_type.child_by_field_name("scope"),
4603            apparent_return_type.child_by_field_name("name"),
4604        )
4605        && return_type.kind() == "template_type"
4606        && matches!(callable_name.kind(), "identifier" | "field_identifier")
4607        && (0..apparent_return_type.child_count())
4608            .filter_map(|index| apparent_return_type.child(index))
4609            .any(|child| child.kind() == "::" && child.is_missing())
4610        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
4611        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
4612    {
4613        return Some((return_type, callable_name));
4614    }
4615    if !matches!(
4616        apparent_return_type.kind(),
4617        "identifier" | "field_identifier" | "type_identifier"
4618    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
4619    {
4620        return None;
4621    }
4622    let parameters = function_declarator.child_by_field_name("parameters")?;
4623    let mut cursor = function_declarator.walk();
4624    let between = function_declarator
4625        .named_children(&mut cursor)
4626        .filter(|child| child.kind() != "comment")
4627        .filter(|child| {
4628            child.start_byte() >= apparent_return_type.end_byte()
4629                && child.end_byte() <= parameters.start_byte()
4630                && !same_node(*child, apparent_return_type)
4631                && !same_node(*child, parameters)
4632        })
4633        .collect::<Vec<_>>();
4634    let [name_error] = between.as_slice() else {
4635        return None;
4636    };
4637    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
4638        return None;
4639    }
4640    let callable_name = name_error.named_child(0)?;
4641    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
4642        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
4643    {
4644        return None;
4645    }
4646    Some((apparent_return_type, callable_name))
4647}
4648
4649/// The part of a `function_declarator` after its parameter list that belongs to
4650/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
4651/// specification, a trailing return type and a trailing requires-clause.
4652///
4653/// The grammar makes each of these a distinct sibling of the `parameters`
4654/// field, so they are read from the tree. Splitting the declarator's text on
4655/// the parameter list instead silently dropped every qualifier whenever the
4656/// parameter list was spelled with whitespace that normalization rewrote - a
4657/// line break or a double space was enough to make a `const` member definition
4658/// a different logical symbol from its declaration (#1827).
4659///
4660/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
4661/// are deliberately excluded. C++ does not make them part of the signature and
4662/// an out-of-line definition never repeats them, so including them would split
4663/// a declaration from its own definition.
4664fn cpp_declarator_identity_suffix(
4665    declarator: Node<'_>,
4666    parameters_node: Node<'_>,
4667    source: &str,
4668) -> String {
4669    let mut cursor = declarator.walk();
4670    let parts = declarator
4671        .named_children(&mut cursor)
4672        .filter(|child| child.start_byte() >= parameters_node.end_byte())
4673        .filter(|child| {
4674            matches!(
4675                child.kind(),
4676                "type_qualifier"
4677                    | "ref_qualifier"
4678                    | "noexcept"
4679                    | "throw_specifier"
4680                    | "trailing_return_type"
4681                    | "requires_clause"
4682            )
4683        })
4684        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
4685        .filter(|text| !text.is_empty())
4686        .collect::<Vec<_>>();
4687    normalize_cpp_qualifier_suffix(&parts.join(" "))
4688}
4689
4690fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
4691    match classify_declarator(node)? {
4692        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
4693        DeclaratorKind::Variable(_) => None,
4694    }
4695}
4696
4697fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
4698    match node.kind() {
4699        "function_declarator" => {
4700            let inner = node
4701                .child_by_field_name("declarator")
4702                .or_else(|| node.child_by_field_name("name"))
4703                .or_else(|| last_named_child(node));
4704            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
4705                Some(DeclaratorKind::Variable(node))
4706            } else {
4707                Some(DeclaratorKind::Function(node))
4708            }
4709        }
4710        "init_declarator"
4711        | "pointer_declarator"
4712        | "reference_declarator"
4713        | "parenthesized_declarator"
4714        | "array_declarator"
4715        | "attributed_declarator"
4716        | "template_function" => node
4717            .child_by_field_name("declarator")
4718            .or_else(|| node.child_by_field_name("name"))
4719            .or_else(|| last_named_child(node))
4720            .and_then(classify_declarator),
4721        "identifier" | "field_identifier" | "qualified_identifier" => {
4722            Some(DeclaratorKind::Variable(node))
4723        }
4724        _ => node
4725            .child_by_field_name("declarator")
4726            .or_else(|| node.child_by_field_name("name"))
4727            .or_else(|| last_named_child(node))
4728            .and_then(classify_declarator),
4729    }
4730}
4731
4732fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
4733    matches!(
4734        node.kind(),
4735        "function_declarator"
4736            | "init_declarator"
4737            | "pointer_declarator"
4738            | "reference_declarator"
4739            | "parenthesized_declarator"
4740            | "array_declarator"
4741            | "attributed_declarator"
4742            | "template_function"
4743            | "identifier"
4744            | "field_identifier"
4745            | "qualified_identifier"
4746    )
4747}
4748
4749fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
4750    let class_like = first_class_like_child(node);
4751    let mut cursor = node.walk();
4752    node.named_children(&mut cursor).any(|child| {
4753        matches!(
4754            child.kind(),
4755            "init_declarator"
4756                | "pointer_declarator"
4757                | "reference_declarator"
4758                | "array_declarator"
4759                | "function_declarator"
4760                | "parenthesized_declarator"
4761                | "attributed_declarator"
4762        ) || matches!(
4763            child.kind(),
4764            "identifier" | "field_identifier" | "qualified_identifier"
4765        ) && class_like.is_none_or(|class_node| {
4766            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
4767        })
4768    })
4769}
4770
4771/// Find the unique namespace-scope forward declaration that precedes a
4772/// recovered export-macro class definition.  Tree-sitter can close a malformed
4773/// class at the enclosing namespace's closing brace, leaving the later class
4774/// definitions as root-level recovered `function_definition` nodes.  A
4775/// preceding `class Name;` in the same namespace is the only structured identity
4776/// signal available in that shape.
4777///
4778/// The search is deliberately conservative: it only accepts a body-less class
4779/// specifier whose declaration has no declarator and is not nested in a function
4780/// or class body.  More than one matching namespace forward declaration is
4781/// ambiguous and returns `None` rather than guessing.
4782fn unique_earlier_cpp_namespace_forward(
4783    recovered_node: Node<'_>,
4784    name: &str,
4785    source: &str,
4786) -> Option<String> {
4787    let mut root = recovered_node;
4788    while let Some(parent) = root.parent() {
4789        root = parent;
4790    }
4791
4792    let mut candidates = Vec::new();
4793    let mut stack = vec![root];
4794    while let Some(current) = stack.pop() {
4795        if current.start_byte() < recovered_node.start_byte()
4796            && matches!(
4797                current.kind(),
4798                "class_specifier" | "struct_specifier" | "union_specifier"
4799            )
4800            && cpp_body_node(current).is_none()
4801            && current.parent().is_some_and(|parent| {
4802                parent.kind() == "declaration_list"
4803                    || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
4804            })
4805            && class_like_name(current, source).as_deref() == Some(name)
4806            && cpp_namespace_definition_for_forward(current).is_some_and(|namespace| {
4807                // Borrowing is only justified by the parser-recovery shape we
4808                // are repairing: the namespace that held the forward must
4809                // itself contain a syntax error and must have closed before
4810                // the root-level recovered class. A clean, unrelated
4811                // namespace forward is not an identity proof.
4812                namespace.has_error()
4813                    && namespace.end_byte() < recovered_node.start_byte()
4814                    && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
4815            })
4816            && let Some(package_name) = cpp_namespace_name_for_forward(current, source)
4817        {
4818            candidates.push(package_name);
4819        }
4820
4821        let mut cursor = current.walk();
4822        for child in current.named_children(&mut cursor) {
4823            if child.start_byte() < recovered_node.start_byte() {
4824                stack.push(child);
4825            }
4826        }
4827    }
4828
4829    if candidates.len() == 1 {
4830        candidates.pop()
4831    } else {
4832        None
4833    }
4834}
4835
4836fn malformed_namespace_is_nearest_recovery_region(
4837    namespace: Node<'_>,
4838    recovered_node: Node<'_>,
4839) -> bool {
4840    let mut root = recovered_node;
4841    while let Some(parent) = root.parent() {
4842        root = parent;
4843    }
4844    let mut cursor = root.walk();
4845    root.named_children(&mut cursor)
4846        .filter(|sibling| {
4847            namespace.end_byte() <= sibling.start_byte()
4848                && sibling.end_byte() <= recovered_node.start_byte()
4849        })
4850        .all(is_malformed_namespace_recovery_trivia)
4851}
4852
4853fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
4854    matches!(node.kind(), "ERROR" | "comment")
4855        || node.kind().starts_with("preproc_")
4856        || node.kind() == "expression_statement" && node.named_child_count() == 0
4857}
4858
4859/// Return the namespace path for a forward class only when the declaration is
4860/// at namespace scope.  A declaration nested in a function/class body may share
4861/// the same namespace ancestor but cannot identify a top-level class definition.
4862fn cpp_namespace_name_for_forward(node: Node<'_>, source: &str) -> Option<String> {
4863    cpp_namespace_definition_for_forward(node)?;
4864    cpp_lexical_namespace_name(node, source)
4865}
4866
4867fn cpp_namespace_definition_for_forward(node: Node<'_>) -> Option<Node<'_>> {
4868    let declaration = node.parent()?;
4869    let mut ancestor = declaration.parent();
4870    while let Some(current) = ancestor {
4871        if matches!(
4872            current.kind(),
4873            "compound_statement"
4874                | "field_declaration_list"
4875                | "class_specifier"
4876                | "struct_specifier"
4877                | "union_specifier"
4878                | "function_definition"
4879                | "lambda_expression"
4880        ) {
4881            return None;
4882        }
4883        if current.kind() == "namespace_definition" {
4884            return Some(current);
4885        }
4886        ancestor = current.parent();
4887    }
4888    None
4889}
4890
4891fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
4892    match node.kind() {
4893        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4894        "parenthesized_declarator" => node
4895            .child_by_field_name("declarator")
4896            .or_else(|| last_named_child(node))
4897            .is_some_and(is_pointer_wrapper_declarator),
4898        "template_function" => node
4899            .child_by_field_name("name")
4900            .is_some_and(is_function_pointer_like_inner_declarator),
4901        _ => false,
4902    }
4903}
4904
4905fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
4906    match node.kind() {
4907        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4908        "parenthesized_declarator" => node
4909            .child_by_field_name("declarator")
4910            .or_else(|| last_named_child(node))
4911            .is_some_and(is_pointer_wrapper_declarator),
4912        _ => false,
4913    }
4914}
4915
4916fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<String>, String, String) {
4917    let cleaned = raw_name.trim_start_matches("template ").trim();
4918    // A leading `::` is the explicit-global marker, not an empty owner segment.
4919    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
4920    // erroneous macro envelope swallowing the first identifier of an
4921    // out-of-line `X::X` constructor, chromium #1573); without this strip the
4922    // split below yields owner_parts `[""]`, constructing a unit with an empty
4923    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
4924    let cleaned = cleaned.trim_start_matches("::");
4925    // Parser recovery can preserve two adjacent scope operators around a
4926    // missing component (for example `X::/**/::method` in compiler diagnostic
4927    // fixtures). Empty components are syntax-recovery artifacts, never C++
4928    // owners. Keeping one as the final owner constructed `short_name=".method"`
4929    // and violated the structured package/short boundary during a large LLVM
4930    // workspace build. This is the same legacy-string-to-FqName bridge as the
4931    // ordinary split above; discard only components that the delimiter itself
4932    // proves empty.
4933    let parts: Vec<_> = cleaned
4934        .split("::")
4935        .filter(|component| !component.is_empty())
4936        .collect();
4937    if parts.is_empty() {
4938        return (None, cleaned.to_string(), scope.package_name.clone());
4939    }
4940    if parts.len() > 1 {
4941        let name = parts.last().unwrap_or(&cleaned).to_string();
4942        let owner_parts = &parts[..parts.len() - 1];
4943        if let Some(class_unit) = &scope.class_unit {
4944            // Lexically inside a class body: the owner is that class, whatever
4945            // the declarator re-qualifies it as.
4946            return (
4947                Some(class_unit.short_name().to_string()),
4948                name,
4949                scope.package_name.clone(),
4950            );
4951        }
4952        if !scope.package_name.is_empty() {
4953            // Out-of-line member definition written *inside* an enclosing
4954            // `namespace {}` block (scope package is that namespace). Every
4955            // owner segment before the terminal member is a class-nesting step
4956            // -- an out-of-line nested-class member `Outer::Inner::method` in
4957            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
4958            // namespace path: `using namespace` never brings nested-class
4959            // access into unqualified scope, so C++ always writes the full
4960            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
4961            // that redundantly re-states the enclosing namespace it already
4962            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
4963            // strip that re-qualifying prefix (which duplicates a suffix of the
4964            // enclosing package path) before treating what remains as the
4965            // nested-class chain, so the redundant spelling still lands on the
4966            // same `log4cxx.Foo.method` identity as its header declaration.
4967            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
4968            let owner_path = (!nested.is_empty()).then(|| nested.join("$"));
4969            return (owner_path, name, scope.package_name.clone());
4970        }
4971        // File scope (no enclosing `namespace {}` block, scope package empty).
4972        let (owner_path, package_name) = if owner_parts.len() > 1 {
4973            // A multi-segment qualifier at file scope with no enclosing
4974            // namespace: treat all but the last owner segment as the namespace
4975            // path and the last as the owning class (`ns1::ns2::Class::method`
4976            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
4977            // is really a namespace or an outer class cannot be told from the
4978            // declarator text alone here, and no enclosing namespace or
4979            // in-index owner is available at per-file extraction to confirm the
4980            // class reading, so the far-more-common namespace interpretation is
4981            // kept rather than guessed away (the nested-class-at-file-scope and
4982            // using-directive-qualified nested-class shapes remain on this
4983            // behavior; see #1121).
4984            (
4985                Some(owner_parts.last().unwrap_or(&"").to_string()),
4986                owner_parts[..owner_parts.len() - 1].join("::"),
4987            )
4988        } else {
4989            // A bare `Class::member` qualifier at file scope carries no
4990            // namespace segment of its own. The declarator alone cannot say
4991            // which namespace owns `Class` -- but a `using namespace X;`
4992            // directive already in effect at this point in the file (#1093,
4993            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
4994            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
4995            // is the remaining structural signal for it, so fall back to it
4996            // rather than leaving the definition's package empty while its
4997            // header declaration (parsed inside the `namespace {}` block) keeps
4998            // the real one -- an identity split that made the same member
4999            // unresolvable under its own displayed spelling.
5000            (
5001                Some(owner_parts[0].to_string()),
5002                cpp_using_directive_namespace_for_bare_owner(scope),
5003            )
5004        };
5005        return (owner_path, name, package_name);
5006    }
5007
5008    let package_name = scope.package_name.clone();
5009    let owner_path = scope
5010        .class_unit
5011        .as_ref()
5012        .map(|parent| parent.short_name().to_string());
5013    (owner_path, cleaned.to_string(), package_name)
5014}
5015
5016/// Drop the leading owner segments of an out-of-line member qualifier that
5017/// merely re-state the enclosing namespace the definition already sits in, so
5018/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
5019/// definition may redundantly write `a::b::Outer::Inner::method` (or the
5020/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
5021/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
5022/// noise, not class-nesting steps. Returns the owner segments with the longest
5023/// such re-qualifying prefix removed (possibly all of them, when the qualifier
5024/// names only the enclosing namespace before the terminal member -- a
5025/// re-qualified free function). `package_name` is the enclosing namespace path
5026/// in its stored `::`-joined form; both sides are split on the same delimiter
5027/// the namespace walker joined them with, so this compares namespace *segments*
5028/// rather than scanning text.
5029fn strip_redundant_namespace_prefix<'a>(
5030    owner_parts: &'a [&'a str],
5031    package_name: &str,
5032) -> &'a [&'a str] {
5033    if package_name.is_empty() {
5034        return owner_parts;
5035    }
5036    let package_segments: Vec<&str> = package_name.split("::").collect();
5037    let max_prefix = owner_parts.len().min(package_segments.len());
5038    for prefix_len in (1..=max_prefix).rev() {
5039        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
5040        if &owner_parts[..prefix_len] == package_suffix {
5041            return &owner_parts[prefix_len..];
5042        }
5043    }
5044    owner_parts
5045}
5046
5047/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
5048/// class name at file/namespace scope, from the `using namespace` directives
5049/// visible at this point in the file. Several may be in scope at once (a
5050/// primary `using namespace NS;` alongside deeper conveniences like `using
5051/// namespace NS::helpers;`); since the declarator gives no way to tell which
5052/// one actually declares the owner class, prefer the shallowest (fewest
5053/// `::`-separated segments) as the file's most likely "home" namespace,
5054/// breaking ties by declaration order. Returns an empty string (leaving the
5055/// caller's package unqualified, as before) when no using-namespace directive
5056/// is in scope.
5057fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
5058    scope
5059        .visible_using_namespaces
5060        .iter()
5061        .min_by_key(|namespace| namespace.split("::").count())
5062        .cloned()
5063        .unwrap_or_default()
5064}
5065
5066struct CppQualifiedNameComponent {
5067    name: String,
5068    is_template_id: bool,
5069}
5070
5071/// Canonical nested-class chain for an out-of-line class definition written
5072/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
5073/// component per class (`["Outer", "Inner"]`).
5074///
5075/// The enclosing namespace fixes the namespace/class boundary: after an
5076/// optional redundant spelling of that namespace, every component belongs to
5077/// the class chain. File-scope qualified class names remain untouched because
5078/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
5079///
5080/// The components stay structured (rather than being `$`-joined here) so the
5081/// fq construction can push one Type/Nested segment per class; the `$`-joined
5082/// short-name display form is derived at the call sites that need it.
5083fn qualified_class_name_chain(
5084    class_node: Node<'_>,
5085    source: &str,
5086    scope: &ScopeInfo,
5087) -> Option<Vec<String>> {
5088    if scope.package_name.is_empty() || scope.class_unit.is_some() {
5089        return None;
5090    }
5091    let name = class_node.child_by_field_name("name")?;
5092    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
5093    if explicitly_global
5094        || components.len() < 2
5095        || components.iter().any(|component| component.is_template_id)
5096    {
5097        return None;
5098    }
5099    let names = components
5100        .iter()
5101        .map(|component| component.name.as_str())
5102        .collect::<Vec<_>>();
5103    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
5104    if class_chain.is_empty() {
5105        return None;
5106    }
5107    Some(class_chain.iter().map(|name| name.to_string()).collect())
5108}
5109
5110fn structured_cpp_qualified_components(
5111    qualified_name: Node<'_>,
5112    source: &str,
5113) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
5114    if qualified_name.kind() != "qualified_identifier" {
5115        return None;
5116    }
5117
5118    let mut components = Vec::new();
5119    let mut current = qualified_name;
5120    let mut explicitly_global = false;
5121    loop {
5122        if current.kind() == "qualified_identifier" {
5123            if let Some(component) = current.child_by_field_name("scope") {
5124                components.push(canonical_cpp_qualified_component(component, source)?);
5125            } else if components.is_empty() {
5126                explicitly_global = true;
5127            } else {
5128                return None;
5129            }
5130            current = current.child_by_field_name("name")?;
5131        } else {
5132            components.push(canonical_cpp_qualified_component(current, source)?);
5133            break;
5134        }
5135    }
5136    Some((components, explicitly_global))
5137}
5138
5139fn split_structured_templated_cpp_name(
5140    declarator_name: Node<'_>,
5141    source: &str,
5142    scope: &ScopeInfo,
5143) -> Option<(Option<String>, String, String)> {
5144    let (mut components, explicitly_global) =
5145        structured_cpp_qualified_components(declarator_name, source)?;
5146
5147    let terminal = components.pop()?;
5148    let owner_start = components
5149        .iter()
5150        .position(|component| component.is_template_id)?;
5151    let explicit_package = components[..owner_start]
5152        .iter()
5153        .map(|component| component.name.as_str())
5154        .collect::<Vec<_>>()
5155        .join("::");
5156    let explicit_package_is_empty = explicit_package.is_empty();
5157    let package_name = match (
5158        explicitly_global,
5159        scope.package_name.is_empty(),
5160        explicit_package_is_empty,
5161    ) {
5162        (true, _, _) => explicit_package,
5163        (false, _, true) => scope.package_name.clone(),
5164        (false, true, false) => explicit_package,
5165        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
5166    };
5167    // Same identity-split fallback as `split_cpp_name` (#1093): a template
5168    // specialization's owner class named with no namespace segment of its own
5169    // (`explicit_package` empty) at file scope (`explicitly_global` false)
5170    // with nothing enclosing (`package_name` still empty) has no structural
5171    // signal for its namespace besides an in-scope `using namespace X;`.
5172    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
5173    {
5174        cpp_using_directive_namespace_for_bare_owner(scope)
5175    } else {
5176        package_name
5177    };
5178    let owner_path = components[owner_start..]
5179        .iter()
5180        .map(|component| component.name.as_str())
5181        .collect::<Vec<_>>()
5182        .join("$");
5183    if owner_path.is_empty() || terminal.name.is_empty() {
5184        return None;
5185    }
5186
5187    Some((Some(owner_path), terminal.name, package_name))
5188}
5189
5190fn canonical_cpp_qualified_component(
5191    mut component: Node<'_>,
5192    source: &str,
5193) -> Option<CppQualifiedNameComponent> {
5194    let mut is_template_id = false;
5195    loop {
5196        match component.kind() {
5197            "template_type" => {
5198                is_template_id = true;
5199                component = component.child_by_field_name("name")?;
5200            }
5201            "dependent_name" => component = component.named_child(0)?,
5202            "identifier"
5203            | "field_identifier"
5204            | "namespace_identifier"
5205            | "type_identifier"
5206            | "operator_name"
5207            | "destructor_name" => {
5208                let name = normalize_cpp_whitespace(node_text(component, source));
5209                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
5210                    name,
5211                    is_template_id,
5212                });
5213            }
5214            _ => component = component.child_by_field_name("name")?,
5215        }
5216    }
5217}
5218
5219fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
5220    match node.kind() {
5221        "identifier"
5222        | "field_identifier"
5223        | "type_identifier"
5224        | "operator_name"
5225        | "destructor_name"
5226        | "qualified_identifier" => node_text(node, source).to_string(),
5227        "function_declarator"
5228        | "pointer_declarator"
5229        | "reference_declarator"
5230        | "parenthesized_declarator"
5231        | "array_declarator"
5232        | "template_function" => node
5233            .child_by_field_name("declarator")
5234            .or_else(|| node.child_by_field_name("name"))
5235            .or_else(|| last_named_child(node))
5236            .map(|child| extract_declarator_name(child, source))
5237            .unwrap_or_else(|| node_text(node, source).to_string()),
5238        _ => node
5239            .child_by_field_name("name")
5240            .map(|child| extract_declarator_name(child, source))
5241            .unwrap_or_else(|| node_text(node, source).to_string()),
5242    }
5243}
5244
5245/// Extract a callable identity only through declaration-shaped AST nodes.
5246/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
5247/// expose the call's parameter list as a false function declarator; accepting
5248/// arbitrary node text there emitted bogus names such as `.*f`.
5249fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5250    match node.kind() {
5251        "identifier"
5252        | "field_identifier"
5253        | "type_identifier"
5254        | "operator_name"
5255        | "destructor_name"
5256        | "qualified_identifier" => Some(node_text(node, source).to_string()),
5257        "function_declarator"
5258        | "pointer_declarator"
5259        | "reference_declarator"
5260        | "parenthesized_declarator"
5261        | "array_declarator"
5262        | "template_function" => node
5263            .child_by_field_name("declarator")
5264            .or_else(|| node.child_by_field_name("name"))
5265            .and_then(|child| extract_callable_declarator_name(child, source)),
5266        _ => None,
5267    }
5268}
5269
5270fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
5271    match node.kind() {
5272        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
5273            let name = node_text(node, source).trim().to_string();
5274            (!name.is_empty()).then_some(name)
5275        }
5276        _ => node
5277            .child_by_field_name("declarator")
5278            .or_else(|| node.child_by_field_name("name"))
5279            .or_else(|| last_named_child(node))
5280            .and_then(|child| extract_variable_name(child, source)),
5281    }
5282}
5283
5284fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
5285    let count = node.named_child_count();
5286    if count == 0 {
5287        None
5288    } else {
5289        node.named_child(count - 1)
5290    }
5291}
5292
5293fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
5294    let name_node = node.child_by_field_name("name")?;
5295    let name = normalize_cpp_whitespace(node_text(name_node, source));
5296    (!name.is_empty()).then_some(name)
5297}
5298
5299fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5300    if node.kind() != "declaration" {
5301        return Vec::new();
5302    }
5303    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
5304        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
5305    }) else {
5306        return Vec::new();
5307    };
5308    let Some(declarator) = node.child_by_field_name("declarator") else {
5309        return Vec::new();
5310    };
5311    if node_text(keyword, source) == "using"
5312        && (declarator.kind() != "init_declarator"
5313            || declarator.child_by_field_name("value").is_none())
5314    {
5315        return Vec::new();
5316    }
5317    if node_text(keyword, source) == "typedef"
5318        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
5319    {
5320        return vec![alias_name];
5321    }
5322    extract_typedef_declarator_name(declarator, source)
5323        .into_iter()
5324        .collect()
5325}
5326
5327fn recovered_typedef_error_alias_name(
5328    declaration: Node<'_>,
5329    declarator: Node<'_>,
5330    source: &str,
5331) -> Option<String> {
5332    // An export macro between `class` and its name can make tree-sitter parse
5333    // the recovered class body as a function body. In that shape,
5334    //
5335    //     typedef spi::Filter BASE_CLASS;
5336    //
5337    // becomes a declaration whose `declarator` is the underlying qualified
5338    // type (`spi::Filter`) and whose actual alias name is displaced into the
5339    // following ERROR node. Do not publish the terminal underlying type
5340    // (`Filter`) as a false class-owned alias.
5341    if declarator.kind() != "qualified_identifier" {
5342        return None;
5343    }
5344    let mut cursor = declaration.walk();
5345    let mut errors = declaration
5346        .named_children(&mut cursor)
5347        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
5348    let error = errors.next()?;
5349    if errors.next().is_some() || error.named_child_count() != 1 {
5350        return None;
5351    }
5352    let name = error.named_child(0)?;
5353    if !matches!(
5354        name.kind(),
5355        "identifier" | "field_identifier" | "type_identifier"
5356    ) {
5357        return None;
5358    }
5359    let name = normalize_cpp_whitespace(node_text(name, source));
5360    (!name.is_empty()).then_some(name)
5361}
5362
5363fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5364    // A function-like token in the type position can make tree-sitter expose
5365    // its argument as a parenthesized declarator. Do not publish that argument
5366    // as an alias. The macro-specific recovery below handles the proven shape.
5367    if fragmented_parenthesized_typedef_type(node).is_some() {
5368        return Vec::new();
5369    }
5370    let has_function_like_macro_type = node
5371        .child_by_field_name("type")
5372        .filter(|type_node| type_node.kind() == "type_identifier")
5373        .is_some_and(|type_node| {
5374            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5375        });
5376    let mut names = Vec::new();
5377    let mut cursor = node.walk();
5378    for declarator in node.children_by_field_name("declarator", &mut cursor) {
5379        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
5380            continue;
5381        }
5382        if let Some(name) = extract_typedef_declarator_name(declarator, source)
5383            && !names.contains(&name)
5384        {
5385            names.push(name);
5386        }
5387    }
5388    names
5389}
5390
5391struct RecoveredMacroTypedefAlias<'tree> {
5392    name: String,
5393    end_node: Node<'tree>,
5394}
5395
5396/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
5397/// into an identifier expression statement. The uppercase macro token, missing
5398/// typedef terminator, and complete sibling terminator prove this exact shape.
5399fn recovered_macro_typedef_alias<'tree>(
5400    node: Node<'tree>,
5401    source: &str,
5402) -> Option<RecoveredMacroTypedefAlias<'tree>> {
5403    let type_node = fragmented_parenthesized_typedef_type(node)?;
5404    if type_node.kind() != "type_identifier"
5405        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5406    {
5407        return None;
5408    }
5409
5410    let end_node = node.next_named_sibling()?;
5411    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
5412        return None;
5413    }
5414    let name_node = end_node.named_child(0)?;
5415    if name_node.kind() != "identifier" {
5416        return None;
5417    }
5418    let has_terminator = (0..end_node.child_count()).any(|index| {
5419        end_node
5420            .child(index)
5421            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
5422    });
5423    if !has_terminator {
5424        return None;
5425    }
5426    let name = normalize_cpp_whitespace(node_text(name_node, source));
5427    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
5428}
5429
5430fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
5431    if node.kind() != "type_definition" {
5432        return None;
5433    }
5434    let mut declarator_cursor = node.walk();
5435    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
5436    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
5437        return None;
5438    }
5439    let has_missing_terminator = (0..node.child_count()).any(|index| {
5440        node.child(index)
5441            .is_some_and(|child| child.kind() == ";" && child.is_missing())
5442    });
5443    if !has_missing_terminator {
5444        return None;
5445    }
5446    node.child_by_field_name("type")
5447}
5448
5449fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5450    match node.kind() {
5451        "identifier" | "field_identifier" | "type_identifier" => {
5452            let name = normalize_cpp_whitespace(node_text(node, source));
5453            (!name.is_empty()).then_some(name)
5454        }
5455        "qualified_identifier" => node
5456            .child_by_field_name("name")
5457            .and_then(|name| extract_typedef_declarator_name(name, source)),
5458        _ => node
5459            .child_by_field_name("declarator")
5460            .or_else(|| node.child_by_field_name("name"))
5461            .or_else(|| last_named_child(node))
5462            .and_then(|child| extract_typedef_declarator_name(child, source)),
5463    }
5464}
5465
5466fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
5467    let name = node
5468        .child_by_field_name("name")
5469        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5470        .or_else(|| {
5471            let mut cursor = node.walk();
5472            node.named_children(&mut cursor)
5473                .find(|child| {
5474                    matches!(
5475                        child.kind(),
5476                        "identifier" | "field_identifier" | "type_identifier"
5477                    )
5478                })
5479                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5480        })?;
5481    (!name.is_empty()).then_some(name)
5482}
5483
5484fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
5485    left.id() == right.id()
5486}
5487
5488fn render_cpp_type_signature(
5489    node: Node<'_>,
5490    source: &str,
5491    template_signature: Option<&str>,
5492) -> String {
5493    let text = normalize_cpp_whitespace(node_text(node, source));
5494    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
5495    let rendered = if head.ends_with(';') {
5496        head.to_string()
5497    } else {
5498        format!("{head} {{")
5499    };
5500    if let Some(template_signature) = template_signature {
5501        format!("template {template_signature} {rendered}")
5502    } else {
5503        rendered
5504    }
5505}
5506
5507fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
5508    if let Some(signature) =
5509        render_recovered_macro_qualified_field_signature(node, declarator, source)
5510    {
5511        return signature;
5512    }
5513    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
5514    let prefix = cpp_declaration_prefix(node, source);
5515    let name = extract_variable_name(declarator, source).unwrap_or_default();
5516    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
5517    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
5518        || (prefix.ends_with('&') && raw_suffix == "&")
5519    {
5520        String::new()
5521    } else {
5522        raw_suffix
5523    };
5524
5525    let mut rendered = if suffix.is_empty() {
5526        format!("{prefix} {name}")
5527    } else if suffix.starts_with('*') || suffix.starts_with('&') {
5528        format!("{prefix}{suffix} {name}")
5529    } else if suffix.starts_with('[') || suffix.starts_with('(') {
5530        format!("{prefix} {name}{suffix}")
5531    } else {
5532        format!("{prefix} {suffix}{name}")
5533    };
5534    rendered = collapse_cpp_whitespace(&rendered);
5535
5536    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5537        format!("{rendered} = {initializer};")
5538    } else if declaration_text.ends_with(';') {
5539        format!("{rendered};")
5540    } else {
5541        rendered
5542    }
5543}
5544
5545fn render_recovered_macro_qualified_field_signature(
5546    node: Node<'_>,
5547    declarator: Node<'_>,
5548    source: &str,
5549) -> Option<String> {
5550    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
5551    if !recovered
5552        .iter()
5553        .any(|candidate| same_node(*candidate, declarator))
5554    {
5555        return None;
5556    }
5557    let pseudo_declarator = node.child_by_field_name("declarator")?;
5558    let mut cursor = node.walk();
5559    let clause = node
5560        .named_children(&mut cursor)
5561        .find(|child| child.kind() == "bitfield_clause")?;
5562    let mut cursor = clause.walk();
5563    let error = clause
5564        .named_children(&mut cursor)
5565        .find(|child| child.kind() == "ERROR")?;
5566    let qualified_type =
5567        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
5568    let prefix = cpp_declaration_prefix(node, source);
5569    let name = extract_variable_name(declarator, source)?;
5570    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
5571    let mut rendered = if suffix.is_empty() {
5572        format!("{prefix} {qualified_type} {name}")
5573    } else {
5574        format!("{prefix} {qualified_type} {suffix} {name}")
5575    };
5576    rendered = collapse_cpp_whitespace(&rendered);
5577
5578    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
5579        Some(format!(
5580            "{rendered} = {};",
5581            normalize_cpp_whitespace(node_text(initializer, source))
5582        ))
5583    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5584        Some(format!("{rendered} = {initializer};"))
5585    } else {
5586        Some(format!("{rendered};"))
5587    }
5588}
5589
5590fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
5591    match node.kind() {
5592        "pointer_expression" => {
5593            let operator = node
5594                .child_by_field_name("operator")
5595                .or_else(|| node.child(0))
5596                .map(|operator| node_text(operator, source))
5597                .unwrap_or("*");
5598            let argument = node
5599                .child_by_field_name("argument")
5600                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
5601                .unwrap_or_default();
5602            format!("{operator}{argument}")
5603        }
5604        "unary_expression" => {
5605            let operator = node
5606                .child_by_field_name("operator")
5607                .or_else(|| node.child(0))
5608                .map(|operator| node_text(operator, source))
5609                .unwrap_or_default();
5610            let argument = node
5611                .child_by_field_name("argument")
5612                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
5613                .unwrap_or_default();
5614            format!("{operator}{argument}")
5615        }
5616        "identifier" | "field_identifier" => String::new(),
5617        _ => cpp_declarator_suffix_without_name(node, source),
5618    }
5619}
5620
5621fn recovered_macro_qualified_field_initializer<'tree>(
5622    clause: Node<'tree>,
5623    declarator: Node<'tree>,
5624) -> Option<Node<'tree>> {
5625    let mut stack = vec![clause];
5626    while let Some(current) = stack.pop() {
5627        if current.kind() == "assignment_expression"
5628            && current
5629                .child_by_field_name("left")
5630                .is_some_and(|left| same_node(left, declarator))
5631        {
5632            return current.child_by_field_name("right");
5633        }
5634        let mut cursor = current.walk();
5635        stack.extend(current.named_children(&mut cursor));
5636    }
5637    None
5638}
5639
5640fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
5641    let text = node_text(node, source);
5642    let mut cursor = node.walk();
5643    let first_declarator = node.named_children(&mut cursor).find(|child| {
5644        matches!(
5645            child.kind(),
5646            "init_declarator"
5647                | "identifier"
5648                | "field_identifier"
5649                | "pointer_declarator"
5650                | "reference_declarator"
5651                | "array_declarator"
5652                | "function_declarator"
5653        )
5654    });
5655    let prefix = if let Some(first_declarator) = first_declarator {
5656        let end = first_declarator
5657            .start_byte()
5658            .saturating_sub(node.start_byte());
5659        let mut prefix = text.get(..end).unwrap_or(text).to_string();
5660        let declarator_suffix = match first_declarator.kind() {
5661            "init_declarator" => first_declarator
5662                .child_by_field_name("declarator")
5663                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
5664                .unwrap_or_default(),
5665            _ => cpp_declarator_suffix_without_name(first_declarator, source),
5666        };
5667        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
5668            prefix.push_str(&declarator_suffix);
5669        }
5670        return collapse_cpp_whitespace(&prefix)
5671            .trim_end_matches(',')
5672            .trim_end_matches(';')
5673            .trim()
5674            .to_string();
5675    } else {
5676        text
5677    };
5678    collapse_cpp_whitespace(prefix)
5679        .trim_end_matches(',')
5680        .trim_end_matches(';')
5681        .trim()
5682        .to_string()
5683}
5684
5685fn cpp_preserved_initializer(
5686    declaration_node: Node<'_>,
5687    declarator: Node<'_>,
5688    source: &str,
5689) -> Option<String> {
5690    let name = extract_variable_name(declarator, source)?;
5691    let mut cursor = declaration_node.walk();
5692    for child in declaration_node.named_children(&mut cursor) {
5693        if child.kind() != "init_declarator" {
5694            continue;
5695        }
5696        let Some(inner) = child.child_by_field_name("declarator") else {
5697            continue;
5698        };
5699        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
5700            continue;
5701        }
5702        let value = child.child_by_field_name("value")?;
5703        let kind = value.kind();
5704        if matches!(
5705            kind,
5706            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
5707        ) {
5708            return Some(normalize_cpp_whitespace(node_text(value, source)));
5709        }
5710        break;
5711    }
5712    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
5713    let pattern = format!(
5714        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
5715        regex::escape(&name)
5716    );
5717    Regex::new(&pattern)
5718        .ok()
5719        .and_then(|regex| regex.captures(&declaration_text))
5720        .and_then(|captures| captures.get(1))
5721        .map(|value| value.as_str().to_string())
5722}
5723
5724fn render_cpp_function_display_signature_from_node(
5725    node: Node<'_>,
5726    source: &str,
5727    template_signature: Option<&str>,
5728    has_body: bool,
5729) -> String {
5730    let root = enclosing_cpp_declaration_node(node).unwrap_or(node);
5731    let parent_text = node_text(root, source);
5732    let body_local_start = root
5733        .child_by_field_name("body")
5734        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
5735        .unwrap_or(parent_text.len());
5736    let display = parent_text
5737        .get(..body_local_start)
5738        .unwrap_or(parent_text)
5739        .trim()
5740        .trim();
5741    let display = if let Some(template_signature) = template_signature {
5742        if display.starts_with("template ") {
5743            display.to_string()
5744        } else {
5745            format!("template {template_signature} {display}")
5746        }
5747    } else {
5748        display.to_string()
5749    };
5750    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
5751    if has_body {
5752        format!("{display} {{...}}")
5753    } else {
5754        format!("{display};")
5755    }
5756}
5757
5758fn cpp_template_signature(
5759    template_node: Node<'_>,
5760    declaration_child: Node<'_>,
5761    source: &str,
5762) -> Option<String> {
5763    let text = source
5764        .get(template_node.start_byte()..declaration_child.start_byte())
5765        .unwrap_or("");
5766    let text = normalize_cpp_whitespace(text);
5767    let start = text.find('<')?;
5768    let end = text.rfind('>')?;
5769    if end < start {
5770        return None;
5771    }
5772    Some(text[start..=end].to_string())
5773}
5774
5775struct RecoveredFragmentedPartialSpecialization<'tree> {
5776    declaration_node: Node<'tree>,
5777    name: String,
5778    range: Range,
5779    prefix_members: Vec<Node<'tree>>,
5780    member_siblings: Vec<Node<'tree>>,
5781    following_declarations: Vec<Node<'tree>>,
5782}
5783
5784struct RecoveredFragmentedPreprocessorClass<'tree> {
5785    declaration_node: Node<'tree>,
5786    class_node: Node<'tree>,
5787    body: Node<'tree>,
5788    name: String,
5789    range: Range,
5790    tail_members: Vec<Node<'tree>>,
5791    member_siblings: Vec<Node<'tree>>,
5792}
5793
5794/// Recover a class whose preprocessor-fragmented parse closes at an early
5795/// member body and publishes the remaining in-class declarations as siblings
5796/// of the surrounding alternative. Primary classes are admitted only when an
5797/// earlier branch contains the matching bodyless declaration and the class
5798/// node retains the displaced `#endif`. Partial specializations instead carry
5799/// their identity structurally in the `template_type` name and template
5800/// metadata. Retain the original AST nodes and re-own only the siblings through
5801/// the displaced structural `};` terminator.
5802fn recover_fragmented_preprocessor_class<'tree>(
5803    template_node: Node<'tree>,
5804    source: &str,
5805) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
5806    let alternative = template_node.parent()?;
5807    if alternative.kind() != "preproc_else" {
5808        return None;
5809    }
5810    let conditional = alternative.parent()?;
5811    if conditional.kind() != "preproc_if" {
5812        return None;
5813    }
5814    let declaration_node = template_node
5815        .named_children(&mut template_node.walk())
5816        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
5817    let class_node = declaration_node
5818        .named_children(&mut declaration_node.walk())
5819        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
5820    let body = cpp_body_node(class_node)?;
5821    if class_node.end_byte() >= declaration_node.end_byte() {
5822        return None;
5823    }
5824    let name = class_like_name(class_node, source)?;
5825    let is_partial_specialization = class_node
5826        .child_by_field_name("name")
5827        .is_some_and(|class_name| class_name.kind() == "template_type");
5828    if is_partial_specialization {
5829        let metadata = cpp_template_metadata(template_node, class_node, source)?;
5830        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
5831            return None;
5832        }
5833    } else {
5834        if !class_has_displaced_preprocessor_terminator(class_node) {
5835            return None;
5836        }
5837        let matching_other_branch = conditional
5838            .named_children(&mut conditional.walk())
5839            .take_while(|child| !same_node(*child, alternative))
5840            .filter(|child| child.kind() == "template_declaration")
5841            .filter_map(first_class_like_child)
5842            .any(|candidate| {
5843                cpp_body_node(candidate).is_none()
5844                    && class_like_name(candidate, source).as_deref() == Some(name.as_str())
5845            });
5846        if !matching_other_branch {
5847            return None;
5848        }
5849    }
5850
5851    let mut tail_members = Vec::new();
5852    let mut saw_class = false;
5853    let mut declaration_cursor = declaration_node.walk();
5854    for child in declaration_node.named_children(&mut declaration_cursor) {
5855        if same_node(child, class_node) {
5856            saw_class = true;
5857        } else if saw_class {
5858            tail_members.push(child);
5859        }
5860    }
5861
5862    let mut member_siblings = Vec::new();
5863    let mut saw_template = false;
5864    let mut terminator = None;
5865    for index in 0..alternative.child_count() {
5866        let Some(child) = alternative.child(index) else {
5867            continue;
5868        };
5869        if same_node(child, template_node) {
5870            saw_template = true;
5871            continue;
5872        }
5873        if !saw_template {
5874            continue;
5875        }
5876        if displaced_fragmented_class_terminator(alternative, index) {
5877            terminator = alternative.child(index + 1);
5878            break;
5879        }
5880        if child.is_named() {
5881            member_siblings.push(child);
5882        }
5883    }
5884    let terminator = terminator?;
5885    Some(RecoveredFragmentedPreprocessorClass {
5886        declaration_node,
5887        class_node,
5888        body,
5889        name,
5890        range: Range {
5891            start_byte: class_node.start_byte(),
5892            end_byte: terminator.end_byte(),
5893            start_line: class_node.start_position().row + 1,
5894            end_line: terminator.end_position().row + 1,
5895        },
5896        tail_members,
5897        member_siblings,
5898    })
5899}
5900
5901fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
5902    (0..class_node.child_count()).any(|index| {
5903        class_node.child(index).is_some_and(|child| {
5904            child.kind() == "ERROR"
5905                && (0..child.child_count()).any(|error_index| {
5906                    child
5907                        .child(error_index)
5908                        .is_some_and(|token| token.kind() == "#endif")
5909                })
5910        })
5911    })
5912}
5913
5914/// The real `#endif` that tree-sitter consumed inside an error subtree.
5915///
5916/// A preprocessor directive inside a malformed array bound can cause later
5917/// declarations to remain children of the conditional. The non-missing token
5918/// still gives the exact structured boundary. Ignore nested conditionals and
5919/// select the last error-owned token. Tree-sitter can pair a later outer
5920/// `#endif` with this conditional, so the direct terminator is not necessarily
5921/// missing.
5922pub fn cpp_displaced_preprocessor_terminator<'tree>(
5923    conditional: Node<'tree>,
5924) -> Option<Node<'tree>> {
5925    if !conditional.has_error() {
5926        return None;
5927    }
5928    let has_concrete_direct_terminator = conditional
5929        .child_count()
5930        .checked_sub(1)
5931        .and_then(|index| conditional.child(index))
5932        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
5933    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
5934        // A structured alternative proves that the direct `#endif` closes
5935        // this family. An error-owned terminator inside either branch belongs
5936        // to a damaged nested conditional, not to this one.
5937        return None;
5938    }
5939    let mut displaced = None;
5940    let mut stack = (0..conditional.child_count())
5941        .filter_map(|index| conditional.child(index))
5942        .map(|child| (child, false))
5943        .collect::<Vec<_>>();
5944    while let Some((node, inside_error)) = stack.pop() {
5945        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
5946            continue;
5947        }
5948        if node.kind() == "#endif" && !node.is_missing() && inside_error {
5949            if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
5950                displaced = Some(node);
5951            }
5952            continue;
5953        }
5954        if node != conditional
5955            && matches!(
5956                node.kind(),
5957                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
5958            )
5959        {
5960            continue;
5961        }
5962        let inside_error = inside_error || node.kind() == "ERROR";
5963        for index in 0..node.child_count() {
5964            if let Some(child) = node.child(index) {
5965                stack.push((child, inside_error));
5966            }
5967        }
5968    }
5969    displaced
5970}
5971
5972/// The effective end of a conditional whose real terminator tree-sitter
5973/// displaced into declaration recovery.
5974///
5975/// Most damaged conditionals retain a concrete `#endif` token below an
5976/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
5977/// boundary. A preprocessor family that selects the middle of a declaration
5978/// can lose the directive tokens entirely. In that shape tree-sitter leaves
5979/// the declaration's `typedef` token as the sole child of the immediately
5980/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
5981/// declarator name inside the conditional's first declaration. The declaration
5982/// end is then the smallest structured boundary that contains the whole split
5983/// declaration.
5984#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5985pub struct CppDisplacedPreprocessorBoundary {
5986    pub end_byte: usize,
5987    pub end_line: usize,
5988}
5989
5990pub fn cpp_displaced_preprocessor_boundary(
5991    conditional: Node<'_>,
5992) -> Option<CppDisplacedPreprocessorBoundary> {
5993    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
5994        return Some(CppDisplacedPreprocessorBoundary {
5995            end_byte: terminator.end_byte(),
5996            end_line: terminator.end_position().row + 1,
5997        });
5998    }
5999    if let Some(declaration) = displaced_split_declaration(conditional) {
6000        return Some(CppDisplacedPreprocessorBoundary {
6001            end_byte: declaration.end_byte(),
6002            end_line: declaration.end_position().row + 1,
6003        });
6004    }
6005    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
6006        return Some(CppDisplacedPreprocessorBoundary {
6007            end_byte: terminator.end_byte(),
6008            end_line: terminator.end_position().row + 1,
6009        });
6010    }
6011    None
6012}
6013
6014fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
6015    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
6016        return None;
6017    }
6018    let mut cursor = conditional.walk();
6019    let declarations = conditional
6020        .named_children(&mut cursor)
6021        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
6022        .collect::<Vec<_>>();
6023    let declaration = *declarations.first()?;
6024    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
6025        return None;
6026    }
6027    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
6028    let mut terminator = None;
6029    let mut stack = (0..declaration.child_count())
6030        .filter_map(|index| declaration.child(index))
6031        .filter(|child| child.start_byte() < declarator_start)
6032        .map(|child| (child, false))
6033        .collect::<Vec<_>>();
6034    while let Some((node, inside_error)) = stack.pop() {
6035        let inside_error = inside_error || node.kind() == "ERROR";
6036        if inside_error && node.kind() == "#endif" && !node.is_missing() {
6037            terminator = Some(node);
6038            continue;
6039        }
6040        for index in 0..node.child_count() {
6041            if let Some(child) = node.child(index)
6042                && child.start_byte() < declarator_start
6043            {
6044                stack.push((child, inside_error));
6045            }
6046        }
6047    }
6048    terminator
6049}
6050
6051fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
6052    if !conditional.has_error()
6053        || conditional.child_by_field_name("alternative").is_some()
6054        || conditional
6055            .prev_named_sibling()
6056            .filter(|sibling| {
6057                sibling.kind() == "ERROR"
6058                    && sibling.child_count() == 1
6059                    && sibling
6060                        .child(0)
6061                        .is_some_and(|child| child.kind() == "typedef")
6062            })
6063            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
6064            .is_none()
6065    {
6066        return None;
6067    }
6068    let mut cursor = conditional.walk();
6069    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
6070    let declaration_index = children
6071        .iter()
6072        .position(|child| child.kind() == "declaration" && child.has_error())?;
6073    let declaration = children[declaration_index];
6074    if !children
6075        .iter()
6076        .skip(declaration_index + 1)
6077        .any(|child| child.end_byte() > declaration.end_byte())
6078    {
6079        return None;
6080    }
6081    let declarator = declaration.child_by_field_name("declarator")?;
6082    let mut error_end = None;
6083    let mut names = Vec::new();
6084    let mut stack = vec![declarator];
6085    while let Some(node) = stack.pop() {
6086        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
6087            error_end =
6088                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
6089            continue;
6090        }
6091        if matches!(node.kind(), "identifier" | "type_identifier") {
6092            names.push(node.start_byte());
6093        }
6094        for index in (0..node.named_child_count()).rev() {
6095            if let Some(child) = node.named_child(index) {
6096                stack.push(child);
6097            }
6098        }
6099    }
6100    let error_end = error_end?;
6101    names
6102        .into_iter()
6103        .any(|start| start >= error_end)
6104        .then_some(declaration)
6105}
6106
6107fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
6108    let Some(error) = parent.child(error_index) else {
6109        return false;
6110    };
6111    if error.kind() != "ERROR"
6112        || error.child_count() != 1
6113        || error.child(0).is_none_or(|child| child.kind() != "}")
6114    {
6115        return false;
6116    }
6117    let Some(semicolon) = parent.child(error_index + 1) else {
6118        return false;
6119    };
6120    semicolon.kind() == "expression_statement"
6121        && semicolon.child_count() == 1
6122        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
6123}
6124
6125/// Locate the real end of a class-like declaration when a macro invocation
6126/// without a source semicolon absorbs the class's `};` into its parsed field.
6127/// The grammar then keeps following namespace declarations as later children
6128/// of the same field list. The direct ERROR-plus-semicolon pair proves the
6129/// boundary structurally; no source-text delimiter scan is needed.
6130fn displaced_macro_class_tail(
6131    declaration_node: Node<'_>,
6132    body: Node<'_>,
6133    source: &str,
6134) -> Option<DisplacedMacroClassTail> {
6135    if !matches!(
6136        declaration_node.kind(),
6137        "class_specifier" | "struct_specifier" | "union_specifier"
6138    ) || body.kind() != "field_declaration_list"
6139    {
6140        return None;
6141    }
6142
6143    let child_count = body.named_child_count();
6144    for index in 0..child_count {
6145        let child = body.named_child(index)?;
6146        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
6147            continue;
6148        };
6149        let split_index = index + 1;
6150        if split_index >= child_count {
6151            return None;
6152        }
6153        let mut cursor = body.walk();
6154        if !body
6155            .named_children(&mut cursor)
6156            .skip(split_index)
6157            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
6158        {
6159            return None;
6160        }
6161        return Some(DisplacedMacroClassTail {
6162            split_index,
6163            class_range: Range {
6164                start_byte: declaration_node.start_byte(),
6165                end_byte: terminator.end_byte(),
6166                start_line: declaration_node.start_position().row + 1,
6167                end_line: terminator.end_position().row + 1,
6168            },
6169        });
6170    }
6171    None
6172}
6173
6174fn displaced_macro_field_terminator<'tree>(
6175    field: Node<'tree>,
6176    source: &str,
6177) -> Option<Node<'tree>> {
6178    if field.kind() != "field_declaration" {
6179        return None;
6180    }
6181    let macro_type = field.child_by_field_name("type")?;
6182    if macro_type.kind() != "type_identifier"
6183        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
6184        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
6185    {
6186        return None;
6187    }
6188    for index in 0..field.child_count() {
6189        let error = field.child(index)?;
6190        if error.kind() != "ERROR"
6191            || error.child_count() != 1
6192            || error.child(0).is_none_or(|child| child.kind() != "}")
6193        {
6194            continue;
6195        }
6196        let semicolon = field.child(index + 1)?;
6197        if semicolon.kind() == ";" {
6198            return Some(semicolon);
6199        }
6200    }
6201    None
6202}
6203
6204fn recover_fragmented_partial_specialization<'tree>(
6205    template_node: Node<'tree>,
6206    declaration_child: Node<'tree>,
6207    source: &str,
6208) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
6209    if declaration_child.kind() != "function_definition" {
6210        return None;
6211    }
6212    let class_node = declaration_child.child_by_field_name("type")?;
6213    if !matches!(
6214        class_node.kind(),
6215        "class_specifier" | "struct_specifier" | "union_specifier"
6216    ) || !class_node
6217        .child_by_field_name("name")
6218        .and_then(|name| direct_identifier_name(name, source))
6219        .is_some_and(|name| cpp_export_macro_token(&name))
6220    {
6221        return None;
6222    }
6223    let declarator = declaration_child.child_by_field_name("declarator")?;
6224    if declarator.kind() != "template_function" {
6225        return None;
6226    }
6227    let metadata = cpp_template_metadata(template_node, declaration_child, source)?;
6228    if metadata.specialization_arguments.is_empty() {
6229        return None;
6230    }
6231    let body = declaration_child.child_by_field_name("body")?;
6232    if body.kind() != "compound_statement" {
6233        return None;
6234    }
6235    let complete_prefix = body.named_child(0).filter(|first| {
6236        first.kind() == "labeled_statement"
6237            && first.has_error()
6238            && first
6239                .named_child(first.named_child_count().saturating_sub(1))
6240                .is_some_and(recovered_declaration_has_class_terminator)
6241    });
6242    let complete_body = complete_prefix.is_some();
6243    let mut prefix_members = Vec::new();
6244    if let Some(prefix) = complete_prefix {
6245        prefix_members.push(prefix);
6246    } else {
6247        let mut body_cursor = body.walk();
6248        for child in body.named_children(&mut body_cursor) {
6249            if !is_structurally_valid_fragmented_class_prefix_member(child) {
6250                break;
6251            }
6252            prefix_members.push(child);
6253        }
6254    }
6255    let containing_declarations = template_node.parent()?;
6256    if !matches!(
6257        containing_declarations.kind(),
6258        "declaration_list" | "compound_statement"
6259    ) {
6260        return None;
6261    }
6262    let mut member_siblings = Vec::new();
6263    let mut following_declarations = Vec::new();
6264    let terminator;
6265    if complete_body {
6266        terminator = complete_prefix?;
6267        let mut cursor = body.walk();
6268        let mut after_prefix = false;
6269        for child in body.named_children(&mut cursor) {
6270            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
6271                after_prefix = true;
6272            } else if after_prefix {
6273                following_declarations.push(child);
6274            }
6275        }
6276    } else {
6277        let mut found_template = false;
6278        let mut cursor = containing_declarations.walk();
6279        let mut class_terminator = None;
6280        for child in containing_declarations.children(&mut cursor) {
6281            if same_node(child, template_node) {
6282                found_template = true;
6283                continue;
6284            }
6285            if found_template && child.kind() == "}" {
6286                class_terminator = Some(child);
6287                break;
6288            }
6289            // A namespace can never be a class member: reaching one before the
6290            // terminator proves the class's true close was swallowed upstream
6291            // and this scan has crossed into the enclosing scope, so the
6292            // recovery cannot be bounded -- continuing re-owns the namespace
6293            // block (and its template specializations) as class members under
6294            // a re-appended package, desyncing the fq boundary (#2306).
6295            if found_template && child.kind() == "namespace_definition" {
6296                return None;
6297            }
6298            if found_template && child.is_named() {
6299                member_siblings.push(child);
6300            }
6301        }
6302        terminator = class_terminator?;
6303    }
6304    let name = format!(
6305        "{}<{}>",
6306        metadata.primary_name,
6307        metadata
6308            .specialization_arguments
6309            .iter()
6310            .map(|argument| argument.text.as_str())
6311            .collect::<Vec<_>>()
6312            .join(", ")
6313    );
6314    Some(RecoveredFragmentedPartialSpecialization {
6315        declaration_node: declaration_child,
6316        name,
6317        range: Range {
6318            start_byte: declaration_child.start_byte(),
6319            end_byte: terminator.end_byte(),
6320            start_line: declaration_child.start_position().row + 1,
6321            end_line: terminator.end_position().row + 1,
6322        },
6323        prefix_members,
6324        member_siblings,
6325        following_declarations,
6326    })
6327}
6328
6329fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
6330    if declaration.kind() != "declaration" {
6331        return false;
6332    }
6333    // With an export macro between `class` and its name, tree-sitter folds a
6334    // complete class body into a function-shaped declaration. The class's own
6335    // `};` remains structurally identifiable as a direct ERROR child holding
6336    // `}`, immediately followed by the declaration's direct `;` child.
6337    (0..declaration.child_count().saturating_sub(1)).any(|index| {
6338        let Some(error) = declaration.child(index) else {
6339            return false;
6340        };
6341        error.kind() == "ERROR"
6342            && error.child_count() == 1
6343            && error.child(0).is_some_and(|child| child.kind() == "}")
6344            && declaration
6345                .child(index + 1)
6346                .is_some_and(|child| child.kind() == ";")
6347    })
6348}
6349
6350fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
6351    if node.has_error() {
6352        return false;
6353    }
6354    match node.kind() {
6355        "declaration"
6356        | "field_declaration"
6357        | "alias_declaration"
6358        | "type_definition"
6359        | "static_assert_declaration" => true,
6360        "labeled_statement" => node
6361            .named_child(node.named_child_count().saturating_sub(1))
6362            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
6363        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
6364            matches!(
6365                child.kind(),
6366                "declaration"
6367                    | "field_declaration"
6368                    | "alias_declaration"
6369                    | "type_definition"
6370                    | "function_definition"
6371            )
6372        }),
6373        _ => false,
6374    }
6375}
6376
6377fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
6378    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
6379        .then(|| node.child_by_field_name("declarator"))
6380        .flatten()
6381        .and_then(|declarator| extract_variable_name(declarator, source))
6382}
6383
6384fn cpp_template_metadata(
6385    template_node: Node<'_>,
6386    declaration_child: Node<'_>,
6387    source: &str,
6388) -> Option<CppTemplateMetadata> {
6389    let parameters_node = template_node.child_by_field_name("parameters")?;
6390    let name_node = cpp_templated_class_name_node(declaration_child)?;
6391    let primary_node = match name_node.kind() {
6392        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
6393        _ => name_node,
6394    };
6395    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
6396    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
6397        return None;
6398    }
6399
6400    let mut parameter_nodes = Vec::new();
6401    let mut parameter_names = Vec::new();
6402    let mut cursor = parameters_node.walk();
6403    for parameter in parameters_node.named_children(&mut cursor) {
6404        if !matches!(
6405            parameter.kind(),
6406            "type_parameter_declaration"
6407                | "optional_type_parameter_declaration"
6408                | "variadic_type_parameter_declaration"
6409                | "template_template_parameter_declaration"
6410                | "parameter_declaration"
6411                | "optional_parameter_declaration"
6412                | "variadic_parameter_declaration"
6413        ) {
6414            continue;
6415        }
6416        let index = parameter_nodes.len();
6417        // An unnamed parameter still contributes template arity and kind. Use
6418        // an impossible C++ identifier so positional reconciliation can bind
6419        // it without making source expressions refer to a name that was not
6420        // written.
6421        let name = cpp_template_parameter_name(parameter, source)
6422            .unwrap_or_else(|| format!("<anonymous:{index}>"));
6423        parameter_names.push(name);
6424        parameter_nodes.push(parameter);
6425    }
6426    let parameters = parameter_nodes
6427        .into_iter()
6428        .zip(parameter_names.iter().cloned())
6429        .map(|(parameter, name)| CppTemplateParameterMetadata {
6430            name,
6431            kind: cpp_template_parameter_kind(parameter),
6432            variadic: matches!(
6433                parameter.kind(),
6434                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
6435            ),
6436            default: cpp_template_parameter_default_expression(parameter, source, &parameter_names),
6437        })
6438        .collect();
6439    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
6440        Vec::new()
6441    } else {
6442        cpp_template_argument_expressions(name_node, source, &parameter_names).unwrap_or_default()
6443    };
6444    let alias_target = (declaration_child.kind() == "alias_declaration")
6445        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names))
6446        .flatten();
6447    Some(CppTemplateMetadata {
6448        primary_name,
6449        primary_fq_name: String::new(),
6450        parameters,
6451        specialization_arguments,
6452        alias_target,
6453    })
6454}
6455
6456fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
6457    match node.kind() {
6458        "class_specifier" | "struct_specifier" | "union_specifier" => {
6459            node.child_by_field_name("name")
6460        }
6461        "function_definition" => {
6462            let declarator = node.child_by_field_name("declarator")?;
6463            if matches!(declarator.kind(), "identifier" | "template_function") {
6464                Some(declarator)
6465            } else {
6466                None
6467            }
6468        }
6469        "alias_declaration" => node.child_by_field_name("name"),
6470        _ => None,
6471    }
6472}
6473
6474fn cpp_template_alias_target(
6475    alias: Node<'_>,
6476    source: &str,
6477    parameter_names: &[String],
6478) -> Option<CppTemplateAliasTargetMetadata> {
6479    let mut type_node = alias.child_by_field_name("type")?;
6480    while type_node.kind() == "type_descriptor" {
6481        type_node = type_node.child_by_field_name("type")?;
6482    }
6483    let global = type_node.child_by_field_name("scope").is_none()
6484        && type_node.child(0).is_some_and(|child| child.kind() == "::");
6485    let mut components = Vec::new();
6486    cpp_template_target_components(type_node, source, &mut components)?;
6487    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names);
6488    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
6489        components,
6490        global,
6491        arguments,
6492    })
6493}
6494
6495fn cpp_template_target_components(
6496    node: Node<'_>,
6497    source: &str,
6498    out: &mut Vec<String>,
6499) -> Option<()> {
6500    match node.kind() {
6501        "identifier" | "namespace_identifier" | "type_identifier" => {
6502            out.push(node_text(node, source).to_string());
6503            Some(())
6504        }
6505        "template_type" => {
6506            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6507        }
6508        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
6509            if let Some(scope) = node.child_by_field_name("scope") {
6510                cpp_template_target_components(scope, source, out)?;
6511            }
6512            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6513        }
6514        _ => None,
6515    }
6516}
6517
6518fn cpp_template_argument_expressions(
6519    mut node: Node<'_>,
6520    source: &str,
6521    parameter_names: &[String],
6522) -> Option<Vec<CppTemplateExpression>> {
6523    loop {
6524        match node.kind() {
6525            "template_type" | "template_function" => {
6526                let arguments = node.child_by_field_name("arguments")?;
6527                let mut cursor = arguments.walk();
6528                return Some(
6529                    arguments
6530                        .named_children(&mut cursor)
6531                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
6532                        .map(|argument| cpp_template_expression(argument, source, parameter_names))
6533                        .collect(),
6534                );
6535            }
6536            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
6537                node = node
6538                    .child_by_field_name("name")
6539                    .or_else(|| node.child_by_field_name("type"))?;
6540            }
6541            _ => return None,
6542        }
6543    }
6544}
6545
6546fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
6547    let candidate = node
6548        .child_by_field_name("name")
6549        .or_else(|| node.child_by_field_name("declarator"))
6550        .or_else(|| {
6551            let mut cursor = node.walk();
6552            node.named_children(&mut cursor).find(|child| {
6553                matches!(
6554                    child.kind(),
6555                    "identifier" | "type_identifier" | "field_identifier"
6556                )
6557            })
6558        })?;
6559    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
6560    (!name.is_empty()).then_some(name)
6561}
6562
6563fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
6564    match node.kind() {
6565        "type_parameter_declaration"
6566        | "optional_type_parameter_declaration"
6567        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
6568        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
6569        _ => CppTemplateParameterKind::Value,
6570    }
6571}
6572
6573fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
6574    node.child_by_field_name("default_type")
6575        .or_else(|| node.child_by_field_name("default_value"))
6576}
6577
6578fn cpp_template_parameter_default_expression(
6579    parameter: Node<'_>,
6580    source: &str,
6581    parameter_names: &[String],
6582) -> Option<CppTemplateExpression> {
6583    let default = cpp_template_parameter_default(parameter)?;
6584    let base = cpp_template_expression(default, source, parameter_names);
6585    let Some(pointer_error) = parameter.next_named_sibling() else {
6586        return Some(base);
6587    };
6588    let Some(pointer_declarator) =
6589        recovered_abstract_pointer_declarator_term(pointer_error, source)
6590    else {
6591        return Some(base);
6592    };
6593    Some(CppTemplateExpression {
6594        text: format!(
6595            "{}{}",
6596            base.text,
6597            normalize_cpp_whitespace(node_text(pointer_error, source))
6598        ),
6599        term: CppTemplateTerm::Node {
6600            kind: "type_descriptor".to_string(),
6601            children: vec![base.term, pointer_declarator],
6602        },
6603    })
6604}
6605
6606fn recovered_abstract_pointer_declarator_term(
6607    node: Node<'_>,
6608    source: &str,
6609) -> Option<CppTemplateTerm> {
6610    if node.kind() != "ERROR" || node.child_count() == 0 {
6611        return None;
6612    }
6613    let mut children = Vec::new();
6614    for index in 0..node.child_count() {
6615        let child = node.child(index)?;
6616        if child.kind() != "*" {
6617            return None;
6618        }
6619        children.push(CppTemplateTerm::Atom {
6620            kind: "*".to_string(),
6621            text: normalize_cpp_whitespace(node_text(child, source)),
6622        });
6623    }
6624    Some(CppTemplateTerm::Node {
6625        kind: "abstract_pointer_declarator".to_string(),
6626        children,
6627    })
6628}
6629
6630fn cpp_template_expression(
6631    node: Node<'_>,
6632    source: &str,
6633    parameter_names: &[String],
6634) -> CppTemplateExpression {
6635    let text = normalize_cpp_whitespace(node_text(node, source));
6636    CppTemplateExpression {
6637        text,
6638        term: cpp_template_term(node, source, parameter_names),
6639    }
6640}
6641
6642pub fn cpp_template_term(
6643    node: Node<'_>,
6644    source: &str,
6645    parameter_names: &[String],
6646) -> CppTemplateTerm {
6647    enum Work<'tree> {
6648        Visit(Node<'tree>),
6649        Build { kind: String, child_count: usize },
6650    }
6651
6652    let mut work = vec![Work::Visit(node)];
6653    let mut terms = Vec::new();
6654    while let Some(next) = work.pop() {
6655        match next {
6656            Work::Visit(current) => {
6657                let text = normalize_cpp_whitespace(node_text(current, source));
6658                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names) {
6659                    terms.push(CppTemplateTerm::Parameter(text));
6660                    continue;
6661                }
6662                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
6663                    let mut cursor = current.walk();
6664                    let named = current
6665                        .named_children(&mut cursor)
6666                        .filter(|child| !child.is_extra() && child.kind() != "comment")
6667                        .collect::<Vec<_>>();
6668                    if let [child] = named.as_slice() {
6669                        work.push(Work::Visit(*child));
6670                        continue;
6671                    }
6672                }
6673                if current.child_count() == 0 {
6674                    terms.push(CppTemplateTerm::Atom {
6675                        kind: if matches!(
6676                            current.kind(),
6677                            "identifier"
6678                                | "type_identifier"
6679                                | "field_identifier"
6680                                | "namespace_identifier"
6681                        ) {
6682                            "identifier".to_string()
6683                        } else {
6684                            current.kind().to_string()
6685                        },
6686                        text,
6687                    });
6688                    continue;
6689                }
6690                let children = (0..current.child_count())
6691                    .filter_map(|index| current.child(index))
6692                    .filter(|child| !child.is_extra() && child.kind() != "comment")
6693                    .collect::<Vec<_>>();
6694                work.push(Work::Build {
6695                    kind: current.kind().to_string(),
6696                    child_count: children.len(),
6697                });
6698                work.extend(children.into_iter().rev().map(Work::Visit));
6699            }
6700            Work::Build { kind, child_count } => {
6701                let children = terms.split_off(terms.len() - child_count);
6702                terms.push(CppTemplateTerm::Node { kind, children });
6703            }
6704        }
6705    }
6706    terms.pop().expect("template term traversal emits one root")
6707}
6708
6709fn cpp_template_term_leaf_is_parameter(
6710    node: Node<'_>,
6711    text: &str,
6712    parameter_names: &[String],
6713) -> bool {
6714    if !parameter_names.iter().any(|parameter| parameter == text) {
6715        return false;
6716    }
6717    !node.parent().is_some_and(|parent| {
6718        matches!(
6719            parent.kind(),
6720            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
6721        ) && parent.child_by_field_name("scope").is_some()
6722            && parent.child_by_field_name("name") == Some(node)
6723    })
6724}
6725
6726fn enclosing_cpp_declaration_node(mut node: Node<'_>) -> Option<Node<'_>> {
6727    loop {
6728        match node.kind() {
6729            "declaration"
6730            | "function_declaration"
6731            | "field_declaration"
6732            | "function_definition" => return Some(node),
6733            _ => node = node.parent()?,
6734        }
6735    }
6736}
6737
6738fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
6739    let mut params = Vec::new();
6740    let mut cursor = parameters_node.walk();
6741    for child in parameters_node.children(&mut cursor) {
6742        match child.kind() {
6743            "parameter_declaration" | "optional_parameter_declaration" => {
6744                params.push(cpp_parameter_type(child, source));
6745            }
6746            "variadic_parameter_declaration" => {
6747                params.push(cpp_parameter_type(child, source));
6748            }
6749            "variadic_parameter" | "..." => params.push("...".to_string()),
6750            _ => {}
6751        }
6752    }
6753
6754    if params.is_empty() {
6755        "()".to_string()
6756    } else {
6757        format!("({})", params.join(", "))
6758    }
6759}
6760
6761fn cpp_signature_metadata(
6762    signature: String,
6763    function_declarator: Node<'_>,
6764    source: &str,
6765) -> SignatureMetadata {
6766    let dispatch = cpp_callable_dispatch_extensibility(function_declarator);
6767    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
6768    let return_type_text = cpp_callable_return_type_text(function_declarator, source);
6769    let return_type_identity = cpp_callable_return_type_identity(function_declarator, source);
6770    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
6771        return enrich(
6772            SignatureMetadata::new(signature, Vec::new())
6773                .with_return_type_text(return_type_text)
6774                .with_return_type_identity(return_type_identity),
6775        );
6776    };
6777    let callable_arity = cpp_callable_arity(parameters_node, source);
6778    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
6779    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
6780    let search_from = cpp_signature_search_start(&signature, function_declarator, source);
6781    let Some(relative_start) = signature
6782        .get(search_from..)
6783        .and_then(|suffix| suffix.find(&parameter_text))
6784    else {
6785        return enrich(
6786            SignatureMetadata::new(signature, Vec::new())
6787                .with_callable_arity(callable_arity)
6788                .with_callable_parameter_types(callable_parameter_types)
6789                .with_return_type_text(return_type_text)
6790                .with_return_type_identity(return_type_identity),
6791        );
6792    };
6793    let parameters_start = search_from + relative_start;
6794    let parameters_end = parameters_start + parameter_text.len();
6795    let mut search_start = parameters_start;
6796    let parameters = cpp_parameter_label_nodes(parameters_node)
6797        .into_iter()
6798        .filter_map(|label_node| {
6799            let label = normalize_cpp_whitespace(node_text(label_node, source));
6800            if label.is_empty() || search_start > parameters_end {
6801                return None;
6802            }
6803            let haystack = signature.get(search_start..parameters_end)?;
6804            let relative_start = haystack.find(&label)?;
6805            let start_byte = search_start + relative_start;
6806            let end_byte = start_byte + label.len();
6807            search_start = end_byte;
6808            Some(ParameterMetadata::new(label, start_byte, end_byte))
6809        })
6810        .collect();
6811    enrich(
6812        SignatureMetadata::new(signature, parameters)
6813            .with_callable_arity(callable_arity)
6814            .with_callable_parameter_types(callable_parameter_types)
6815            .with_return_type_text(return_type_text)
6816            .with_return_type_identity(return_type_identity),
6817    )
6818}
6819
6820fn cpp_callable_is_structural_constructor(function_declarator: Node<'_>, source: &str) -> bool {
6821    let Some(name_node) = function_declarator
6822        .child_by_field_name("declarator")
6823        .or_else(|| function_declarator.child_by_field_name("name"))
6824        .or_else(|| last_named_child(function_declarator))
6825    else {
6826        return false;
6827    };
6828    let Some(callable_name) = direct_identifier_name(name_node, source) else {
6829        return false;
6830    };
6831
6832    let mut current = function_declarator.parent();
6833    while let Some(ancestor) = current {
6834        let owner_name = match ancestor.kind() {
6835            "class_specifier" | "struct_specifier" | "union_specifier" => {
6836                class_like_name(ancestor, source)
6837            }
6838            "ERROR" => malformed_class_error_owner_name(ancestor, source),
6839            _ => None,
6840        };
6841        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
6842            return true;
6843        }
6844        current = ancestor.parent();
6845    }
6846    false
6847}
6848
6849/// Recover the owner name from the direct grammar shape retained when a later
6850/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
6851/// `ERROR` node:
6852///
6853/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
6854///
6855/// Direct-child checks keep this distinct from an unrelated nested class inside
6856/// a broader error region. The closing brace may be displaced past the error
6857/// node, so the opening body token is the available structural boundary.
6858fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
6859    if node.kind() != "ERROR" {
6860        return None;
6861    }
6862    let keyword = node.child(0)?;
6863    if !matches!(keyword.kind(), "class" | "struct" | "union") {
6864        return None;
6865    }
6866    let name_node = node.child(1)?;
6867    let name = direct_identifier_name(name_node, source)?;
6868    let has_body = (2..node.child_count())
6869        .filter_map(|index| node.child(index))
6870        .any(|child| child.kind() == "{");
6871    has_body.then_some(name)
6872}
6873
6874fn cpp_callable_return_type_identity(
6875    function_declarator: Node<'_>,
6876    source: &str,
6877) -> Option<StructuredTypeIdentity> {
6878    if cpp_callable_is_structural_constructor(function_declarator, source) {
6879        return None;
6880    }
6881    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
6882    if let Some((return_type, _)) = cpp_macro_displaced_callable_parts(function_declarator, source)
6883    {
6884        return cpp_structured_type_identity(return_type, source, &lexical_scope);
6885    }
6886    let mut cursor = function_declarator.walk();
6887    if let Some(trailing) = function_declarator
6888        .named_children(&mut cursor)
6889        .find(|child| child.kind() == "trailing_return_type")
6890        && let Some(type_descriptor) = trailing.named_child(0)
6891    {
6892        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
6893    }
6894
6895    let mut current = function_declarator;
6896    let mut wrappers = Vec::new();
6897    while let Some(parent) = current.parent() {
6898        if matches!(
6899            parent.kind(),
6900            "function_definition" | "declaration" | "field_declaration"
6901        ) {
6902            let type_node = parent.child_by_field_name("type")?;
6903            if cpp_export_macro_token(node_text(type_node, source))
6904                && (0..parent.named_child_count()).any(|index| {
6905                    parent
6906                        .named_child(index)
6907                        .is_some_and(|child| child.kind() == "ERROR")
6908                })
6909            {
6910                return None;
6911            }
6912            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
6913            for wrapper in wrappers.into_iter().rev() {
6914                identity = cpp_wrap_structured_type(identity, wrapper)?;
6915            }
6916            return Some(identity);
6917        }
6918        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
6919            || (matches!(
6920                parent.kind(),
6921                "pointer_declarator"
6922                    | "reference_declarator"
6923                    | "array_declarator"
6924                    | "parenthesized_declarator"
6925            ) && parent.named_child_count() == 1
6926                && parent.named_child(0) == Some(current));
6927        if !wraps_current_declarator {
6928            return None;
6929        }
6930        match parent.kind() {
6931            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
6932            "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
6933            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
6934            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
6935            _ => return None,
6936        }
6937        current = parent;
6938    }
6939    None
6940}
6941
6942fn cpp_structured_type_identity(
6943    node: Node<'_>,
6944    source: &str,
6945    lexical_scope: &[String],
6946) -> Option<StructuredTypeIdentity> {
6947    enum Work<'tree> {
6948        Visit(Node<'tree>),
6949        Wrap(CppStructuredTypeWrapper),
6950        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
6951        BuildGeneric { argument_count: usize },
6952    }
6953
6954    let mut work = vec![Work::Visit(node)];
6955    let mut values = Vec::new();
6956    let mut builder = StructuredTypeIdentityBuilder::default();
6957    while let Some(next) = work.pop() {
6958        match next {
6959            Work::Visit(current) => match current.kind() {
6960                "type_descriptor" => {
6961                    let type_node = current
6962                        .child_by_field_name("type")
6963                        .or_else(|| current.named_child(0))?;
6964                    let mut wrappers = Vec::new();
6965                    let mut cursor = current.walk();
6966                    for child in current.named_children(&mut cursor) {
6967                        if child.id() != type_node.id() {
6968                            wrappers.extend(cpp_structured_declarator_wrappers(child));
6969                        }
6970                    }
6971                    work.push(Work::ApplyWrappers(wrappers));
6972                    work.push(Work::Visit(type_node));
6973                }
6974                "pointer_declarator" | "abstract_pointer_declarator" => {
6975                    let child = current
6976                        .child_by_field_name("declarator")
6977                        .or_else(|| current.named_child(0))?;
6978                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
6979                    work.push(Work::Visit(child));
6980                }
6981                "reference_declarator" => {
6982                    let child = current
6983                        .child_by_field_name("declarator")
6984                        .or_else(|| current.named_child(0))?;
6985                    work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
6986                    work.push(Work::Visit(child));
6987                }
6988                "array_declarator" | "abstract_array_declarator" => {
6989                    let child = current
6990                        .child_by_field_name("declarator")
6991                        .or_else(|| current.named_child(0))?;
6992                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
6993                    work.push(Work::Visit(child));
6994                }
6995                "template_type" => {
6996                    let name_node = current.child_by_field_name("name")?;
6997                    let arguments = current
6998                        .child_by_field_name("arguments")
6999                        .map(|arguments_node| {
7000                            let mut cursor = arguments_node.walk();
7001                            arguments_node
7002                                .named_children(&mut cursor)
7003                                .filter(|child| !child.is_extra() && child.kind() != "comment")
7004                                .collect::<Vec<_>>()
7005                        })
7006                        .unwrap_or_default();
7007                    work.push(Work::BuildGeneric {
7008                        argument_count: arguments.len(),
7009                    });
7010                    work.extend(arguments.into_iter().rev().map(Work::Visit));
7011                    work.push(Work::Visit(name_node));
7012                }
7013                "qualified_identifier"
7014                | "scoped_identifier"
7015                | "scoped_type_identifier"
7016                | "type_identifier"
7017                | "field_identifier"
7018                | "identifier"
7019                | "namespace_identifier"
7020                | "primitive_type" => {
7021                    values.push(builder.named(cpp_structured_named_type(
7022                        current,
7023                        source,
7024                        lexical_scope,
7025                    )?)?);
7026                }
7027                _ => {
7028                    let child = current.child_by_field_name("type").or_else(|| {
7029                        (current.named_child_count() == 1)
7030                            .then(|| current.named_child(0))
7031                            .flatten()
7032                    })?;
7033                    work.push(Work::Visit(child));
7034                }
7035            },
7036            Work::Wrap(wrapper) => {
7037                let root = values.pop()?;
7038                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
7039            }
7040            Work::ApplyWrappers(wrappers) => {
7041                let mut root = values.pop()?;
7042                for wrapper in wrappers.into_iter().rev() {
7043                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
7044                }
7045                values.push(root);
7046            }
7047            Work::BuildGeneric { argument_count } => {
7048                let value_count = argument_count.checked_add(1)?;
7049                let start = values.len().checked_sub(value_count)?;
7050                let mut built = values.split_off(start);
7051                let base = built.remove(0);
7052                values.push(builder.generic(base, built)?);
7053            }
7054        }
7055    }
7056    (values.len() == 1)
7057        .then(|| values.pop())
7058        .flatten()
7059        .and_then(|root| builder.finish(root))
7060}
7061
7062fn cpp_structured_named_type(
7063    node: Node<'_>,
7064    source: &str,
7065    lexical_scope: &[String],
7066) -> Option<StructuredTypeName> {
7067    let path = cpp_structured_type_path(node, source)?;
7068    let absolute = node.child_by_field_name("scope").is_none()
7069        && node.child(0).is_some_and(|child| child.kind() == "::");
7070    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
7071}
7072
7073#[derive(Clone, Copy)]
7074enum CppStructuredTypeWrapper {
7075    Pointer,
7076    Reference,
7077    Array,
7078}
7079
7080fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
7081    let mut wrappers = Vec::new();
7082    let mut current = node;
7083    loop {
7084        match current.kind() {
7085            "pointer_declarator" | "abstract_pointer_declarator" => {
7086                wrappers.push(CppStructuredTypeWrapper::Pointer)
7087            }
7088            "reference_declarator" | "abstract_reference_declarator" => {
7089                wrappers.push(CppStructuredTypeWrapper::Reference)
7090            }
7091            "array_declarator" | "abstract_array_declarator" => {
7092                wrappers.push(CppStructuredTypeWrapper::Array)
7093            }
7094            _ => break,
7095        }
7096        let Some(child) = current
7097            .child_by_field_name("declarator")
7098            .or_else(|| current.named_child(0))
7099        else {
7100            break;
7101        };
7102        current = child;
7103    }
7104    wrappers
7105}
7106
7107fn cpp_wrap_structured_type(
7108    identity: StructuredTypeIdentity,
7109    wrapper: CppStructuredTypeWrapper,
7110) -> Option<StructuredTypeIdentity> {
7111    match wrapper {
7112        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
7113        CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
7114        CppStructuredTypeWrapper::Array => identity.wrap_array(),
7115    }
7116}
7117
7118fn cpp_wrap_structured_type_node(
7119    builder: &mut StructuredTypeIdentityBuilder,
7120    inner: StructuredTypeNodeId,
7121    wrapper: CppStructuredTypeWrapper,
7122) -> Option<StructuredTypeNodeId> {
7123    match wrapper {
7124        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
7125        CppStructuredTypeWrapper::Reference => builder.reference(inner),
7126        CppStructuredTypeWrapper::Array => builder.array(inner),
7127    }
7128}
7129
7130fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
7131    let mut path = Vec::new();
7132    let mut stack = vec![node];
7133    while let Some(current) = stack.pop() {
7134        match current.kind() {
7135            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
7136                let component = node_text(current, source).to_string();
7137                if component.is_empty() {
7138                    return None;
7139                }
7140                path.push(component);
7141            }
7142            "template_type" | "dependent_type" => {
7143                stack.push(current.child_by_field_name("name")?);
7144            }
7145            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
7146                stack.push(current.child_by_field_name("name")?);
7147                if let Some(scope) = current.child_by_field_name("scope") {
7148                    stack.push(scope);
7149                }
7150            }
7151            _ => return None,
7152        }
7153    }
7154    (!path.is_empty()).then_some(path)
7155}
7156
7157fn cpp_callable_lexical_scope(node: Node<'_>, source: &str) -> Vec<String> {
7158    let mut groups = Vec::new();
7159    let mut current = node.parent();
7160    while let Some(parent) = current {
7161        if matches!(
7162            parent.kind(),
7163            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
7164        ) && let Some(name_node) = parent.child_by_field_name("name")
7165            && let Some(components) = cpp_structured_type_path(name_node, source)
7166            && !components.is_empty()
7167        {
7168            groups.push(components);
7169        }
7170        current = parent.parent();
7171    }
7172    groups.reverse();
7173    groups.into_iter().flatten().collect()
7174}
7175
7176fn cpp_callable_dispatch_extensibility(function_declarator: Node<'_>) -> DispatchExtensibility {
7177    let mut declaration = None;
7178    let mut current = Some(function_declarator);
7179    while let Some(node) = current {
7180        match node.kind() {
7181            "template_declaration"
7182            | "preproc_if"
7183            | "preproc_ifdef"
7184            | "preproc_else"
7185            | "preproc_elif"
7186            | "preproc_call"
7187            | "ERROR" => return DispatchExtensibility::Open,
7188            "declaration" | "field_declaration" | "function_definition" => {
7189                declaration.get_or_insert(node);
7190            }
7191            "translation_unit" => break,
7192            _ => {}
7193        }
7194        current = node.parent();
7195    }
7196    let Some(declaration) = declaration else {
7197        return DispatchExtensibility::Open;
7198    };
7199
7200    let mut saw_virtual_boundary = false;
7201    let mut stack = vec![declaration];
7202    while let Some(node) = stack.pop() {
7203        match node.kind() {
7204            "compound_statement" | "field_declaration_list" => continue,
7205            "final" | "final_specifier" => return DispatchExtensibility::Closed,
7206            "virtual"
7207            | "override"
7208            | "virtual_specifier"
7209            | "pure_virtual_clause"
7210            | "template_parameter_list"
7211            | "template_method"
7212            | "template_function"
7213            | "ERROR" => saw_virtual_boundary = true,
7214            _ => {}
7215        }
7216        let mut cursor = node.walk();
7217        stack.extend(node.children(&mut cursor));
7218    }
7219
7220    if saw_virtual_boundary {
7221        DispatchExtensibility::Open
7222    } else {
7223        DispatchExtensibility::Closed
7224    }
7225}
7226
7227fn cpp_callable_linkage(declaration: Node<'_>, source: &str) -> CallableLinkage {
7228    let mut enclosed_by_class = false;
7229    let mut current = declaration.parent();
7230    while let Some(node) = current {
7231        if node.kind() == "namespace_definition"
7232            && node
7233                .child_by_field_name("name")
7234                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7235        {
7236            return CallableLinkage::Internal;
7237        }
7238        if matches!(
7239            node.kind(),
7240            "class_specifier" | "struct_specifier" | "union_specifier"
7241        ) {
7242            if node
7243                .child_by_field_name("name")
7244                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7245            {
7246                return CallableLinkage::Internal;
7247            }
7248            enclosed_by_class = true;
7249        }
7250        if matches!(node.kind(), "function_definition" | "lambda_expression") {
7251            return CallableLinkage::Internal;
7252        }
7253        current = node.parent();
7254    }
7255
7256    if enclosed_by_class {
7257        return CallableLinkage::External;
7258    }
7259
7260    let mut cursor = declaration.walk();
7261    if declaration.named_children(&mut cursor).any(|child| {
7262        child.kind() == "storage_class_specifier"
7263            && normalize_cpp_whitespace(node_text(child, source)) == "static"
7264    }) {
7265        CallableLinkage::Internal
7266    } else {
7267        CallableLinkage::External
7268    }
7269}
7270
7271fn cpp_callable_return_type_text(function_declarator: Node<'_>, source: &str) -> Option<String> {
7272    if cpp_callable_is_structural_constructor(function_declarator, source) {
7273        return None;
7274    }
7275    if let Some((return_type, _)) = cpp_macro_displaced_callable_parts(function_declarator, source)
7276    {
7277        let text = normalize_cpp_whitespace(node_text(return_type, source));
7278        return (!text.is_empty()).then_some(text);
7279    }
7280    let mut cursor = function_declarator.walk();
7281    if let Some(trailing) = function_declarator
7282        .named_children(&mut cursor)
7283        .find(|child| child.kind() == "trailing_return_type")
7284        && let Some(type_descriptor) = trailing.named_child(0)
7285    {
7286        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
7287        if !text.is_empty() {
7288            return Some(text);
7289        }
7290    }
7291
7292    let mut current = function_declarator;
7293    let mut indirection = String::new();
7294    while let Some(parent) = current.parent() {
7295        if matches!(
7296            parent.kind(),
7297            "function_definition" | "declaration" | "field_declaration"
7298        ) {
7299            let type_node = parent.child_by_field_name("type")?;
7300            if cpp_export_macro_token(node_text(type_node, source))
7301                && (0..parent.named_child_count()).any(|index| {
7302                    parent
7303                        .named_child(index)
7304                        .is_some_and(|child| child.kind() == "ERROR")
7305                })
7306            {
7307                // Export/decorator macros commonly occupy the grammar's `type`
7308                // field and leave the semantic return type in an ERROR sibling.
7309                // Do not persist the macro token as a return type. The malformed
7310                // declaration does not carry enough structured evidence here.
7311                return None;
7312            }
7313            let base = normalize_cpp_whitespace(node_text(type_node, source));
7314            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
7315        }
7316        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
7317            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
7318                && parent.named_child_count() == 1
7319                && parent.named_child(0) == Some(current));
7320        if wraps_current_declarator {
7321            match parent.kind() {
7322                "pointer_declarator" => indirection.push('*'),
7323                "reference_declarator" => {
7324                    let reference = parent
7325                        .children(&mut parent.walk())
7326                        .find(|child| !child.is_named())
7327                        .map(|child| node_text(child, source))
7328                        .unwrap_or("&");
7329                    indirection.push_str(reference);
7330                }
7331                "init_declarator" | "parenthesized_declarator" => {}
7332                _ => return None,
7333            }
7334            current = parent;
7335            continue;
7336        }
7337        return None;
7338    }
7339    None
7340}
7341
7342fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
7343    let mut required = 0;
7344    let mut total = 0;
7345    let mut repeated = false;
7346    let mut cursor = parameters_node.walk();
7347    for child in parameters_node.children(&mut cursor) {
7348        match child.kind() {
7349            "parameter_declaration" => {
7350                if cpp_parameter_is_explicit_object(child, source) {
7351                    continue;
7352                }
7353                if child.child_by_field_name("declarator").is_none()
7354                    && child
7355                        .child_by_field_name("type")
7356                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
7357                {
7358                    continue;
7359                }
7360                required += 1;
7361                total += 1;
7362            }
7363            "optional_parameter_declaration" => total += 1,
7364            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7365                repeated = true;
7366            }
7367            _ => {}
7368        }
7369    }
7370    CallableArity::new(required, total, repeated)
7371}
7372
7373fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
7374    parameter
7375        .child_by_field_name("type")
7376        .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
7377        .and_then(|type_node| type_node.child_by_field_name("constraint"))
7378        .is_some_and(|constraint| {
7379            constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
7380        })
7381}
7382
7383/// One entry of a callable's invocation parameter list.
7384///
7385/// The list excludes an explicit object parameter and a lone `void`, so its
7386/// length is the callable's invocation arity. Every derivation of a parameter
7387/// type - the rendered spelling used for overload discrimination and the
7388/// structured identity used by dependency-pack production - starts from this
7389/// same sequence, so the two can never disagree about which parameters exist.
7390#[derive(Clone, Copy)]
7391enum CppParameterSlot<'tree> {
7392    Declared(Node<'tree>),
7393    Ellipsis,
7394}
7395
7396fn cpp_callable_parameter_slots<'tree>(
7397    parameters_node: Node<'tree>,
7398    source: &str,
7399) -> Vec<CppParameterSlot<'tree>> {
7400    let mut slots = Vec::new();
7401    let mut cursor = parameters_node.walk();
7402    for parameter in parameters_node.children(&mut cursor) {
7403        match parameter.kind() {
7404            "parameter_declaration" | "optional_parameter_declaration" => {
7405                if cpp_parameter_is_explicit_object(parameter, source)
7406                    || (parameter.child_by_field_name("declarator").is_none()
7407                        && parameter
7408                            .child_by_field_name("type")
7409                            .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
7410                {
7411                    continue;
7412                }
7413                slots.push(CppParameterSlot::Declared(parameter));
7414            }
7415            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7416                slots.push(CppParameterSlot::Ellipsis);
7417            }
7418            _ => {}
7419        }
7420    }
7421    slots
7422}
7423
7424fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
7425    cpp_callable_parameter_slots(parameters_node, source)
7426        .into_iter()
7427        .map(|slot| match slot {
7428            CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
7429            CppParameterSlot::Ellipsis => "...".to_string(),
7430        })
7431        .collect()
7432}
7433
7434/// One callable parameter's parser-derived type.
7435///
7436/// A rendered spelling such as `const T&` is a source text, not a type name. A
7437/// consumer that must publish a type into a structured model - a semantic-pack
7438/// type reference, for example - reads this instead.
7439#[derive(Debug, Clone, PartialEq, Eq)]
7440pub enum CppParameterType {
7441    /// The written type reduced to a structured identity. C++ cv-qualifiers
7442    /// have no place in that model and are not represented.
7443    Structured(StructuredTypeIdentity),
7444    /// A `...` pack, which declares no parameter type at all.
7445    Ellipsis,
7446    /// A written type with no structured reduction, such as a macro-obscured,
7447    /// `decltype`-computed, or function-pointer parameter.
7448    Unstructured,
7449}
7450
7451/// The structured type of each invocation parameter, in declaration order.
7452///
7453/// The result is index-parallel with the rendered
7454/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
7455/// callable.
7456pub fn cpp_callable_parameter_type_identities(
7457    function_declarator: Node<'_>,
7458    source: &str,
7459) -> Vec<CppParameterType> {
7460    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
7461        return Vec::new();
7462    };
7463    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
7464    cpp_callable_parameter_slots(parameters_node, source)
7465        .into_iter()
7466        .map(|slot| match slot {
7467            CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
7468            CppParameterSlot::Declared(parameter) => {
7469                cpp_parameter_type_identity(parameter, source, &lexical_scope)
7470                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
7471            }
7472        })
7473        .collect()
7474}
7475
7476fn cpp_parameter_type_identity(
7477    parameter: Node<'_>,
7478    source: &str,
7479    lexical_scope: &[String],
7480) -> Option<StructuredTypeIdentity> {
7481    let type_node = parameter.child_by_field_name("type")?;
7482    let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
7483    if let Some(declarator) = cpp_parameter_declarator(parameter) {
7484        for wrapper in cpp_structured_declarator_wrappers(declarator)
7485            .into_iter()
7486            .rev()
7487        {
7488            identity = cpp_wrap_structured_type(identity, wrapper)?;
7489        }
7490    }
7491    Some(identity)
7492}
7493
7494/// The callable declarator of the declaration that covers `start_byte`.
7495///
7496/// A consumer that holds a declaration's recorded byte position rather than its
7497/// syntax node - external header extraction, for instance - uses this to reach
7498/// the same `function_declarator` the declaration walk read.
7499pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
7500    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
7501    loop {
7502        if matches!(
7503            current.kind(),
7504            "declaration" | "field_declaration" | "function_definition"
7505        ) && let Some(declarator) = current
7506            .child_by_field_name("declarator")
7507            .and_then(extract_function_declarator)
7508        {
7509            return Some(declarator);
7510        }
7511        current = current.parent()?;
7512    }
7513}
7514
7515fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
7516    let mut labels = Vec::new();
7517    let mut cursor = parameters_node.walk();
7518    for child in parameters_node.children(&mut cursor) {
7519        match child.kind() {
7520            "parameter_declaration" | "optional_parameter_declaration" => {
7521                if let Some(name_node) = child
7522                    .child_by_field_name("declarator")
7523                    .and_then(cpp_declarator_label_node)
7524                {
7525                    labels.push(name_node);
7526                } else {
7527                    labels.push(child);
7528                }
7529            }
7530            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7531                labels.push(child);
7532            }
7533            _ => {}
7534        }
7535    }
7536    labels
7537}
7538
7539fn cpp_signature_search_start(
7540    signature: &str,
7541    function_declarator: Node<'_>,
7542    source: &str,
7543) -> usize {
7544    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator) else {
7545        return 0;
7546    };
7547    let raw = node_text(enclosing, source);
7548    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
7549    let offset = function_declarator
7550        .start_byte()
7551        .saturating_sub(enclosing.start_byte())
7552        .saturating_sub(leading_trim_bytes);
7553    offset.min(signature.len())
7554}
7555
7556fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
7557    match node.kind() {
7558        "identifier" | "field_identifier" => Some(node),
7559        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
7560            .child_by_field_name("declarator")
7561            .or_else(|| last_named_child(node))
7562            .and_then(cpp_declarator_label_node),
7563        "array_declarator" => node
7564            .child_by_field_name("declarator")
7565            .and_then(cpp_declarator_label_node),
7566        "function_declarator" => node
7567            .child_by_field_name("declarator")
7568            .or_else(|| node.child_by_field_name("name"))
7569            .or_else(|| last_named_child(node))
7570            .and_then(cpp_declarator_label_node),
7571        _ => None,
7572    }
7573}
7574
7575fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
7576    let base_type = parameter
7577        .child_by_field_name("type")
7578        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
7579        .unwrap_or_default();
7580    let declarator = cpp_parameter_declarator(parameter);
7581    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
7582    // are discarded, so `f(const int)` and `f(int)` declare one function. A
7583    // qualifier written next to the parameter's type is only top-level when
7584    // the declarator adds no indirection; behind a pointer, reference or array
7585    // declarator the same qualifier belongs to the pointee, referent or
7586    // element and keeps distinguishing the type (#1827).
7587    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
7588    let mut cursor = parameter.walk();
7589    let qualifiers = parameter
7590        .named_children(&mut cursor)
7591        .filter(|child| child.kind() == "type_qualifier")
7592        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
7593        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
7594        .collect::<Vec<_>>()
7595        .join(" ");
7596    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
7597        (true, _) => base_type,
7598        (_, true) => qualifiers,
7599        (false, false) => format!("{qualifiers} {base_type}"),
7600    };
7601    let declarator_suffix = declarator
7602        .map(|node| cpp_declarator_suffix_without_name(node, source))
7603        .unwrap_or_default();
7604
7605    let combined = if type_text.is_empty() {
7606        declarator_suffix
7607    } else if declarator_suffix.is_empty() {
7608        type_text
7609    } else {
7610        format!("{type_text} {declarator_suffix}")
7611    };
7612    normalize_cpp_type_text(&combined)
7613}
7614
7615fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
7616    parameter.child_by_field_name("declarator").or_else(|| {
7617        // Some unnamed prototype parameters expose their abstract declarator
7618        // as a direct named child without the grammar's `declarator` field.
7619        // Recover only the structured abstract-declarator node; the parameter's
7620        // type and qualifiers are distinct children and must not be guessed from
7621        // source text.
7622        let mut cursor = parameter.walk();
7623        parameter
7624            .named_children(&mut cursor)
7625            .find(|child| is_cpp_abstract_declarator(child.kind()))
7626    })
7627}
7628
7629/// Whether a parameter's declarator chain adds indirection - a pointer,
7630/// reference, array or function declarator - to the parameter's written type.
7631fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
7632    let mut current = Some(declarator);
7633    while let Some(node) = current {
7634        if matches!(
7635            node.kind(),
7636            "pointer_declarator"
7637                | "abstract_pointer_declarator"
7638                | "reference_declarator"
7639                | "abstract_reference_declarator"
7640                | "array_declarator"
7641                | "abstract_array_declarator"
7642                | "function_declarator"
7643                | "abstract_function_declarator"
7644        ) {
7645            return true;
7646        }
7647        current = cpp_nested_declarator(node);
7648    }
7649    false
7650}
7651
7652fn is_cpp_abstract_declarator(kind: &str) -> bool {
7653    matches!(
7654        kind,
7655        "abstract_pointer_declarator"
7656            | "abstract_reference_declarator"
7657            | "abstract_array_declarator"
7658            | "abstract_function_declarator"
7659            | "abstract_parenthesized_declarator"
7660    )
7661}
7662
7663fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
7664    node.child_by_field_name("declarator").or_else(|| {
7665        if is_cpp_abstract_declarator(node.kind()) {
7666            let mut cursor = node.walk();
7667            node.named_children(&mut cursor)
7668                .find(|child| is_cpp_abstract_declarator(child.kind()))
7669        } else {
7670            // Named declarators historically use their last named child when
7671            // tree-sitter omits the field. Keep that broad fallback for
7672            // attributed, variadic, and recovered named shapes.
7673            last_named_child(node)
7674        }
7675    })
7676}
7677
7678fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
7679    match node.kind() {
7680        "identifier" | "field_identifier" => String::new(),
7681        "pointer_declarator" | "abstract_pointer_declarator" => {
7682            let inner = cpp_nested_declarator(node)
7683                .map(|child| cpp_declarator_suffix_without_name(child, source))
7684                .unwrap_or_default();
7685            format!("*{inner}")
7686        }
7687        "reference_declarator" | "abstract_reference_declarator" => {
7688            let inner = cpp_nested_declarator(node)
7689                .map(|child| cpp_declarator_suffix_without_name(child, source))
7690                .unwrap_or_default();
7691            let reference = node
7692                .children(&mut node.walk())
7693                .find(|child| matches!(child.kind(), "&" | "&&"))
7694                .map(|child| node_text(child, source))
7695                .unwrap_or("&");
7696            format!("{reference}{inner}")
7697        }
7698        "array_declarator" | "abstract_array_declarator" => {
7699            let inner = cpp_nested_declarator(node)
7700                .map(|child| cpp_declarator_suffix_without_name(child, source))
7701                .unwrap_or_default();
7702            let size = node
7703                .child_by_field_name("size")
7704                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
7705                .unwrap_or_default();
7706            format!("{inner}[{size}]")
7707        }
7708        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
7709            let inner = cpp_nested_declarator(node);
7710            inner
7711                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
7712                .unwrap_or_default()
7713        }
7714        "function_declarator" | "abstract_function_declarator" => {
7715            let inner = cpp_nested_declarator(node)
7716                .map(|child| cpp_declarator_suffix_without_name(child, source))
7717                .unwrap_or_default();
7718            let params = node
7719                .child_by_field_name("parameters")
7720                .map(|child| cpp_parameter_signature(child, source))
7721                .unwrap_or_else(|| "()".to_string());
7722            format!("{inner}{params}")
7723        }
7724        _ => {
7725            let text = normalize_cpp_whitespace(node_text(node, source));
7726            let name = extract_declarator_name(node, source);
7727            if name.is_empty() {
7728                text
7729            } else {
7730                text.replace(&name, "").trim().to_string()
7731            }
7732        }
7733    }
7734}
7735
7736fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
7737    collapse_cpp_whitespace(
7738        suffix
7739            .trim()
7740            .trim_start_matches("->")
7741            .trim_start_matches('{')
7742            .trim_end_matches(';'),
7743    )
7744}
7745
7746pub fn normalize_cpp_whitespace(value: &str) -> String {
7747    collapse_cpp_whitespace(value)
7748}
7749
7750fn normalize_cpp_type_text(value: &str) -> String {
7751    collapse_cpp_whitespace(value)
7752        .replace(", ", ",")
7753        .replace(" <", "<")
7754        .replace("< ", "<")
7755        .replace(" >", ">")
7756}
7757
7758fn collapse_cpp_whitespace(value: &str) -> String {
7759    let mut result = String::new();
7760    let mut prev_space = false;
7761    for ch in value.chars() {
7762        if ch.is_whitespace() {
7763            if !prev_space {
7764                result.push(' ');
7765            }
7766            prev_space = true;
7767        } else {
7768            result.push(ch);
7769            prev_space = false;
7770        }
7771    }
7772    result.trim().to_string()
7773}
7774
7775pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
7776    node_source_text(node, source)
7777}
7778
7779pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
7780    walk_named_tree_preorder(node, true, |node| {
7781        match node.kind() {
7782            "type_identifier" | "identifier" | "qualified_identifier" => {
7783                let text = node_text(node, source).trim();
7784                if !text.is_empty() {
7785                    identifiers.insert(text.to_string());
7786                }
7787            }
7788            _ => {}
7789        }
7790        WalkControl::Continue
7791    });
7792}
7793
7794fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
7795    node.child_by_field_name("body").or_else(|| {
7796        let mut cursor = node.walk();
7797        node.named_children(&mut cursor).find(|child| {
7798            matches!(
7799                child.kind(),
7800                "declaration_list" | "field_declaration_list" | "enumerator_list"
7801            )
7802        })
7803    })
7804}
7805
7806/// Return a class body's actual closing brace when the parser supplied one.
7807///
7808/// A malformed namespace sentinel can leave a class node carrying unrelated
7809/// parser errors even though its own class body is complete.  `has_error()` is
7810/// therefore too coarse an admission predicate for sentinel ownership.  The
7811/// body list, however, exposes the opening and closing punctuation directly;
7812/// a real (non-missing) final `}` proves that the class did not borrow the
7813/// enclosing namespace's close.  Requiring the body to end before its parent
7814/// container also rejects a recovered node whose body swallowed that outer
7815/// boundary.
7816fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
7817    if !matches!(
7818        node.kind(),
7819        "class_specifier" | "struct_specifier" | "union_specifier"
7820    ) {
7821        return None;
7822    }
7823    let body = cpp_body_node(node)?;
7824    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
7825        return None;
7826    }
7827    let open = body.child(0)?;
7828    let close = body.child(body.child_count().checked_sub(1)?)?;
7829    if open.kind() != "{"
7830        || open.is_missing()
7831        || close.kind() != "}"
7832        || close.is_missing()
7833        || close.end_byte() != body.end_byte()
7834        || body.end_byte() > node.end_byte()
7835        || node
7836            .parent()
7837            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
7838    {
7839        return None;
7840    }
7841    Some(close)
7842}
7843
7844fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
7845    if node.kind() == "namespace_definition" {
7846        return true;
7847    }
7848    let mut cursor = node.walk();
7849    node.named_children(&mut cursor)
7850        .any(cpp_contains_namespace_definition)
7851}
7852
7853struct CppNestedNamespaceSentinel<'tree> {
7854    function: Node<'tree>,
7855    body: Node<'tree>,
7856    namespace_components: Vec<String>,
7857}
7858
7859/// Owned structural recovery metadata for a namespace-sentinel region.
7860///
7861/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
7862/// instead of the namespace/class scopes that the declaration visitor restores.
7863/// The inverted usage walk has the original CST, so it needs the same ownership
7864/// evidence without borrowing parser nodes across its file scan.  Keep this
7865/// descriptor deliberately source-range based: callers can match a reference
7866/// node by containment and then resolve its structured type spelling in the
7867/// recovered class scope.
7868#[derive(Debug, Clone)]
7869pub struct CppSentinelRecoveredOwner {
7870    pub range: Range,
7871    /// Start of the qualified owner name (`btree<P>::method`).  A leading
7872    /// return type before this byte is looked up from the namespace; parameters,
7873    /// trailing returns, and the body use the member owner scope.
7874    pub owner_name_start_byte: usize,
7875    /// Number of leading components belonging to the namespace rather than
7876    /// the qualified class owner.  A leading return type is looked up before
7877    /// every owner component, not merely before the innermost class.
7878    pub namespace_component_count: usize,
7879    pub scope_components: Vec<String>,
7880}
7881
7882#[derive(Debug, Clone)]
7883pub struct CppSentinelRecoveredClass {
7884    pub namespace_range: Range,
7885    pub namespace_scope_components: Vec<String>,
7886    pub class_range: Range,
7887    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
7888    pub scope_components: Vec<String>,
7889    /// Qualified out-of-line member definitions owned by this class.  Their
7890    /// ranges may extend beyond `class_range` when the malformed sentinel
7891    /// swallowed the namespace close and left definitions as function siblings.
7892    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
7893}
7894
7895/// Resolve the lexical scope restored for a node in a malformed
7896/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
7897/// outrank class spans, which in turn outrank the surviving namespace body.
7898/// The class ancestor suffix is recovered from the original CST so nested
7899/// members keep their complete `Outer::Inner` owner chain.
7900pub fn cpp_sentinel_recovered_scope_for_node(
7901    node: Node<'_>,
7902    source: &str,
7903    recovered_classes: &[CppSentinelRecoveredClass],
7904) -> Option<Vec<String>> {
7905    let contains =
7906        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
7907    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
7908    for recovered in recovered_classes {
7909        for owner in recovered
7910            .owner_ranges
7911            .iter()
7912            .filter(|owner| contains(owner.range))
7913        {
7914            let replace = best_owner.is_none_or(|existing| {
7915                owner.range.end_byte.saturating_sub(owner.range.start_byte)
7916                    < existing
7917                        .range
7918                        .end_byte
7919                        .saturating_sub(existing.range.start_byte)
7920            });
7921            if replace {
7922                best_owner = Some(owner);
7923            }
7924        }
7925    }
7926    if let Some(owner) = best_owner {
7927        let mut scope = owner.scope_components.clone();
7928        if node.start_byte() < owner.owner_name_start_byte {
7929            scope.truncate(owner.namespace_component_count);
7930        }
7931        return Some(scope);
7932    }
7933
7934    let class = recovered_classes
7935        .iter()
7936        .filter(|recovered| contains(recovered.class_range))
7937        .min_by_key(|recovered| {
7938            recovered
7939                .class_range
7940                .end_byte
7941                .saturating_sub(recovered.class_range.start_byte)
7942        });
7943    let class_scope = class.is_some();
7944    let mut scope = if let Some(class) = class {
7945        class.scope_components.clone()
7946    } else {
7947        let namespace = recovered_classes
7948            .iter()
7949            .filter(|recovered| contains(recovered.namespace_range))
7950            .min_by_key(|recovered| {
7951                recovered
7952                    .namespace_range
7953                    .end_byte
7954                    .saturating_sub(recovered.namespace_range.start_byte)
7955            })?;
7956        let mut scope = namespace.namespace_scope_components.clone();
7957        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
7958        let common_prefix = scope
7959            .iter()
7960            .zip(&parser_namespace)
7961            .take_while(|(recovered, parser)| recovered == parser)
7962            .count();
7963        scope.extend(parser_namespace.into_iter().skip(common_prefix));
7964        scope
7965    };
7966    if class_scope {
7967        let mut ancestor_components = Vec::new();
7968        let mut ancestor = node.parent();
7969        while let Some(current) = ancestor {
7970            if matches!(
7971                current.kind(),
7972                "class_specifier" | "struct_specifier" | "union_specifier"
7973            ) && let Some(name) = current.child_by_field_name("name")
7974                && let Some(name_components) = cpp_name_components(name, source)
7975            {
7976                ancestor_components.push(
7977                    name_components
7978                        .into_iter()
7979                        .map(|component| component.name)
7980                        .collect::<Vec<_>>(),
7981                );
7982            }
7983            ancestor = current.parent();
7984        }
7985        ancestor_components.reverse();
7986        let base_len = scope.len();
7987        for component in ancestor_components.into_iter().flatten() {
7988            if scope.len() >= base_len && scope.last() == Some(&component) {
7989                continue;
7990            }
7991            scope.push(component);
7992        }
7993    }
7994    Some(scope)
7995}
7996
7997struct CppSentinelFragmentedClassTail<'tree> {
7998    class_node: Node<'tree>,
7999    template_node: Option<Node<'tree>>,
8000    name: String,
8001    raw_supertypes: Option<Vec<String>>,
8002    fragmented: FragmentedExportBody,
8003    consumed_start: usize,
8004}
8005
8006struct CppSentinelFragmentedClassErrorPrefix<'tree> {
8007    name: String,
8008    open: Node<'tree>,
8009    raw_supertypes: Option<Vec<String>>,
8010}
8011
8012struct CppSentinelDirectBodyClassRegion {
8013    namespace_components: Vec<String>,
8014    class_start: usize,
8015    class_start_line: usize,
8016    class_close_end: usize,
8017    class_close_line: usize,
8018    name: String,
8019}
8020
8021fn cpp_sentinel_body_class_candidate<'tree>(
8022    child: Node<'tree>,
8023) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
8024    if matches!(
8025        child.kind(),
8026        "class_specifier" | "struct_specifier" | "union_specifier"
8027    ) {
8028        return Some((child, None));
8029    }
8030    if child.kind() != "template_declaration" {
8031        if child.kind() == "declaration" {
8032            return Some((first_class_like_child(child)?, None));
8033        }
8034        return None;
8035    }
8036    let mut cursor = child.walk();
8037    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
8038        if matches!(
8039            candidate.kind(),
8040            "class_specifier" | "struct_specifier" | "union_specifier"
8041        ) {
8042            Some(candidate)
8043        } else if candidate.kind() == "declaration" {
8044            first_class_like_child(candidate)
8045        } else {
8046            None
8047        }
8048    })?;
8049    Some((class_node, Some(child)))
8050}
8051
8052/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
8053/// namespace-sentinel body when a later member macro ends the bogus sentinel
8054/// function before the real class close. The anonymous class/open tokens and
8055/// direct identifier are the structural proof; a retained direct close would
8056/// be an ordinary malformed class rather than the fragmented tail handled here.
8057fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
8058    node: Node<'tree>,
8059    source: &str,
8060) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
8061    let name = malformed_class_error_owner_name(node, source)?;
8062    let mut cursor = node.walk();
8063    let children = node.children(&mut cursor).collect::<Vec<_>>();
8064    let keyword = children.first()?;
8065    let open_index = children.iter().position(|child| child.kind() == "{")?;
8066    if children[open_index + 1..]
8067        .iter()
8068        .any(|child| child.kind() == "}")
8069    {
8070        return None;
8071    }
8072    let raw_supertypes =
8073        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
8074    Some(CppSentinelFragmentedClassErrorPrefix {
8075        name,
8076        open: children[open_index],
8077        raw_supertypes,
8078    })
8079}
8080
8081fn cpp_sentinel_direct_body_class_candidate<'tree>(
8082    child: Node<'tree>,
8083) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
8084    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
8085        return Some(candidate);
8086    }
8087    if child.kind() != "template_declaration" {
8088        return None;
8089    }
8090    let mut cursor = child.walk();
8091    let wrapper = child
8092        .named_children(&mut cursor)
8093        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
8094    Some((first_class_like_child(wrapper)?, Some(child)))
8095}
8096
8097fn cpp_sentinel_direct_namespace_components(
8098    function: Node<'_>,
8099    body: Node<'_>,
8100    source: &str,
8101) -> Option<Vec<String>> {
8102    let mut cursor = function.walk();
8103    let children = function
8104        .named_children(&mut cursor)
8105        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
8106        .collect::<Vec<_>>();
8107    let sentinel_index = children.iter().rposition(|child| {
8108        direct_identifier_name(*child, source)
8109            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
8110    })?;
8111    let mut identifiers = Vec::new();
8112    let mut stack = children[sentinel_index + 1..]
8113        .iter()
8114        .rev()
8115        .copied()
8116        .collect::<Vec<_>>();
8117    while let Some(current) = stack.pop() {
8118        if let Some(name) = direct_identifier_name(current, source) {
8119            identifiers.push(name);
8120            continue;
8121        }
8122        let mut cursor = current.walk();
8123        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
8124        stack.extend(children.into_iter().rev());
8125    }
8126    let [keyword, namespace] = identifiers.as_slice() else {
8127        return None;
8128    };
8129    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
8130        .then(|| vec![namespace.clone()])
8131}
8132
8133fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
8134    let mut sibling = class_semicolon.next_named_sibling();
8135    let namespace_close = loop {
8136        let Some(current) = sibling else {
8137            return false;
8138        };
8139        sibling = current.next_named_sibling();
8140        if current.kind() != "comment" {
8141            break current;
8142        }
8143    };
8144    if !cpp_is_stray_close_brace(namespace_close, source) {
8145        return false;
8146    }
8147    loop {
8148        let Some(current) = sibling else {
8149            return false;
8150        };
8151        sibling = current.next_named_sibling();
8152        if current.kind() == "comment" {
8153            continue;
8154        }
8155        return direct_identifier_name(current, source)
8156            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
8157    }
8158}
8159
8160fn cpp_sentinel_macro_body_class_region(
8161    node: Node<'_>,
8162    source: &str,
8163) -> Option<CppSentinelDirectBodyClassRegion> {
8164    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
8165        return None;
8166    };
8167    if node.kind() != "function_definition" || !node.has_error() {
8168        return None;
8169    }
8170    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
8171    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
8172    let mut cursor = body.walk();
8173    let candidates = body
8174        .named_children(&mut cursor)
8175        .filter_map(cpp_sentinel_direct_body_class_candidate)
8176        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
8177        .collect::<Vec<_>>();
8178    let [(class_node, template_node)] = candidates.as_slice() else {
8179        return None;
8180    };
8181    let original_body = cpp_body_node(*class_node)?;
8182    let name = class_like_name(*class_node, source)?;
8183    if name.is_empty() || cpp_export_macro_token(&name) {
8184        return None;
8185    }
8186
8187    let mut sibling = node.next_named_sibling();
8188    let (class_close_start, class_close_end, class_close_line) = loop {
8189        let current = sibling?;
8190        let next = current.next_named_sibling();
8191        if cpp_is_stray_close_brace(current, source)
8192            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
8193        {
8194            let semicolon = next.expect("checked above");
8195            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
8196                return None;
8197            }
8198            break (
8199                current.start_byte(),
8200                semicolon.end_byte(),
8201                semicolon.end_position().row + 1,
8202            );
8203        }
8204        sibling = next;
8205    };
8206    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
8207    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
8208    let root = tree.root_node();
8209    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
8210    let reparsed = cpp_sentinel_reparsed_class(root, reparsed_template, source)?;
8211    if reparsed.name != name
8212        || reparsed.declaration_node.start_byte() != class_node.start_byte()
8213        || reparsed.body.start_byte() != original_body.start_byte()
8214        || class_close_start <= reparsed.body.end_byte()
8215        || class_close_end <= class_node.end_byte()
8216    {
8217        return None;
8218    }
8219    Some(CppSentinelDirectBodyClassRegion {
8220        namespace_components,
8221        class_start: reparse_start,
8222        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
8223            node.start_position().row + 1
8224        }),
8225        class_close_end,
8226        class_close_line,
8227        name,
8228    })
8229}
8230
8231/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
8232/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
8233///
8234/// The parser puts the namespace opener and the malformed function in one root
8235/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
8236/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
8237/// the malformed function must begin with an all-caps type, then an ERROR whose
8238/// sole identifier is `namespace`, followed by the inner namespace identifier
8239/// and a compound body; and that body must contain a complete named class or a
8240/// structurally fragmented class prefix. A text reparse cannot prove any of
8241/// those ownership boundaries.
8242fn cpp_nested_namespace_sentinel<'tree>(
8243    node: Node<'tree>,
8244    source: &str,
8245) -> Option<CppNestedNamespaceSentinel<'tree>> {
8246    if !node.has_error() {
8247        return None;
8248    }
8249
8250    let (function, mut namespace_components) = if node.kind() == "ERROR" {
8251        let mut cursor = node.walk();
8252        let functions = node
8253            .named_children(&mut cursor)
8254            .filter(|child| child.kind() == "function_definition")
8255            .collect::<Vec<_>>();
8256        let [function] = functions.as_slice() else {
8257            return None;
8258        };
8259        if !function.has_error() {
8260            return None;
8261        }
8262        let mut cursor = node.walk();
8263        let children = node.children(&mut cursor).collect::<Vec<_>>();
8264        let function_index = children
8265            .iter()
8266            .position(|child| same_node(*child, *function))?;
8267        let [outer_keyword, outer_name, outer_open] =
8268            children.get(function_index.checked_sub(3)?..function_index)?
8269        else {
8270            return None;
8271        };
8272        if outer_keyword.kind() != "namespace"
8273            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
8274            || outer_open.kind() != "{"
8275        {
8276            return None;
8277        }
8278        (
8279            *function,
8280            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
8281        )
8282    } else if node.kind() == "function_definition" {
8283        let declaration_list = node.parent()?;
8284        let namespace = declaration_list.parent()?;
8285        if declaration_list.kind() != "declaration_list"
8286            || namespace.kind() != "namespace_definition"
8287            || namespace.child_by_field_name("body") != Some(declaration_list)
8288        {
8289            return None;
8290        }
8291        (node, Vec::new())
8292    } else {
8293        return None;
8294    };
8295
8296    let mut cursor = function.walk();
8297    let named = function
8298        .named_children(&mut cursor)
8299        .filter(|child| child.kind() != "comment")
8300        .collect::<Vec<_>>();
8301    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
8302        return None;
8303    };
8304    if first_type.kind() != "type_identifier" {
8305        return None;
8306    }
8307    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
8308    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
8309        return None;
8310    }
8311    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
8312        return None;
8313    }
8314    let inner_keyword = inner_error.named_child(0)?;
8315    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
8316        return None;
8317    }
8318    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
8319        return None;
8320    }
8321    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
8322    if inner_name.is_empty() || body.kind() != "compound_statement" {
8323        return None;
8324    }
8325    namespace_components.push(inner_name);
8326
8327    let mut cursor = body.walk();
8328    let has_complete_class = body.named_children(&mut cursor).any(|child| {
8329        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
8330            cpp_body_node(class_node).is_some()
8331                && class_like_name(class_node, source)
8332                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
8333        })
8334    });
8335    if !has_complete_class && cpp_sentinel_fragmented_class_tail(function, *body, source).is_none()
8336    {
8337        return None;
8338    }
8339
8340    Some(CppNestedNamespaceSentinel {
8341        function,
8342        body: *body,
8343        namespace_components,
8344    })
8345}
8346
8347/// Recognize a namespace-begin sentinel directly beneath the translation unit.
8348///
8349/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
8350/// function whose type is the sentinel, whose declarator is the structured
8351/// qualified name `namespace::a::b`, and whose body contains the namespace
8352/// items. Declaration indexing already reparses this bounded region. The
8353/// inverse scanner retains the original tree, so recover the same namespace
8354/// components from the declarator fields for its lexical-scope metadata.
8355fn cpp_root_namespace_sentinel<'tree>(
8356    node: Node<'tree>,
8357    source: &str,
8358) -> Option<CppNestedNamespaceSentinel<'tree>> {
8359    if node.kind() != "function_definition"
8360        || !node.has_error()
8361        || node.parent()?.kind() != "translation_unit"
8362    {
8363        return None;
8364    }
8365    let first_type = node.child_by_field_name("type")?;
8366    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
8367    if first_type.kind() != "type_identifier"
8368        || sentinel.is_empty()
8369        || !cpp_export_macro_token(&sentinel)
8370    {
8371        return None;
8372    }
8373    let declarator = node.child_by_field_name("declarator")?;
8374    let body = node.child_by_field_name("body")?;
8375    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
8376        return None;
8377    }
8378    let mut cursor = node.walk();
8379    let named = node
8380        .named_children(&mut cursor)
8381        .filter(|child| child.kind() != "comment")
8382        .collect::<Vec<_>>();
8383    let [named_type, named_declarator, named_body] = named.as_slice() else {
8384        return None;
8385    };
8386    if !same_node(*named_type, first_type)
8387        || !same_node(*named_declarator, declarator)
8388        || !same_node(*named_body, body)
8389    {
8390        return None;
8391    }
8392    let mut declarator_components = Vec::new();
8393    let mut valid_components = true;
8394    walk_named_tree_preorder(declarator, true, |component| {
8395        if !matches!(
8396            component.kind(),
8397            "identifier" | "namespace_identifier" | "type_identifier"
8398        ) {
8399            return WalkControl::Continue;
8400        }
8401        let Some(component) = canonical_cpp_qualified_component(component, source) else {
8402            valid_components = false;
8403            return WalkControl::Break;
8404        };
8405        declarator_components.push(component.name);
8406        WalkControl::SkipChildren
8407    });
8408    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
8409        return None;
8410    }
8411    declarator_components.remove(0);
8412    let namespace_components = declarator_components;
8413    if namespace_components.is_empty()
8414        || namespace_components
8415            .iter()
8416            .any(|component| component.is_empty() || cpp_export_macro_token(component))
8417    {
8418        return None;
8419    }
8420
8421    let mut cursor = body.walk();
8422    let has_complete_class = body.named_children(&mut cursor).any(|child| {
8423        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
8424            cpp_body_node(class_node).is_some()
8425                && class_like_name(class_node, source)
8426                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
8427        })
8428    });
8429    if !has_complete_class && cpp_sentinel_fragmented_class_tail(node, body, source).is_none() {
8430        return None;
8431    }
8432
8433    Some(CppNestedNamespaceSentinel {
8434        function: node,
8435        body,
8436        namespace_components,
8437    })
8438}
8439
8440/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
8441/// malformed namespace-sentinel function.  The recovery is deliberately
8442/// structural: the class must be a direct body item, its own class node must be
8443/// erroneous and end before a unique anonymous `}` in the enclosing
8444/// declaration-list, and that namespace's next sibling must be a standalone
8445/// `;`.  The complete interior must pass the existing member-shaped reparse
8446/// gate. This avoids source brace scans and does not borrow a close from an
8447/// unrelated later declaration.
8448fn cpp_sentinel_fragmented_class_tail<'tree>(
8449    function: Node<'tree>,
8450    body: Node<'tree>,
8451    source: &str,
8452) -> Option<CppSentinelFragmentedClassTail<'tree>> {
8453    let mut cursor = body.walk();
8454    let candidates = body
8455        .named_children(&mut cursor)
8456        .filter_map(|child| {
8457            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
8458                let class_body = cpp_body_node(class_node)?;
8459                if !class_node.has_error() {
8460                    return None;
8461                }
8462                let name = class_like_name(class_node, source)?;
8463                let raw_supertypes =
8464                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
8465                        .then(|| extract_cpp_supertypes(class_node, source));
8466                return Some((
8467                    class_node,
8468                    template_node,
8469                    name,
8470                    class_body,
8471                    class_body.start_byte().checked_add(1)?,
8472                    raw_supertypes,
8473                ));
8474            }
8475            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
8476            Some((
8477                child,
8478                None,
8479                prefix.name,
8480                prefix.open,
8481                prefix.open.end_byte(),
8482                prefix.raw_supertypes,
8483            ))
8484        })
8485        .collect::<Vec<_>>();
8486    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
8487        candidates.as_slice()
8488    else {
8489        return None;
8490    };
8491    if name.is_empty() || cpp_export_macro_token(name) {
8492        return None;
8493    }
8494
8495    let (close, semicolon) =
8496        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
8497
8498    let reparse_end = close.start_byte();
8499    if *reparse_start >= reparse_end {
8500        return None;
8501    }
8502    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
8503    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
8504        return None;
8505    }
8506    let class_range = Range {
8507        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
8508        end_byte: semicolon.end_byte(),
8509        start_line: template_node.map_or(class_node.start_position().row, |node| {
8510            node.start_position().row
8511        }) + 1,
8512        end_line: semicolon.end_position().row + 1,
8513    };
8514    Some(CppSentinelFragmentedClassTail {
8515        class_node: *class_node,
8516        template_node: *template_node,
8517        name: name.clone(),
8518        raw_supertypes: raw_supertypes.clone(),
8519        fragmented: FragmentedExportBody {
8520            reparse_start: *reparse_start,
8521            reparse_end,
8522            class_range,
8523        },
8524        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
8525    })
8526}
8527
8528/// Recover the class and out-of-line owner scopes from every malformed
8529/// namespace-sentinel region in `root`.
8530///
8531/// This is the shared structural counterpart to
8532/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
8533/// reuses the visitor's sentinel/class admission predicates instead of parsing
8534/// source text a second time.  The returned values own only ranges and names, so
8535/// they can be retained by an inverted usage scan after the tree borrow ends.
8536pub fn cpp_sentinel_recovered_classes(
8537    root: Node<'_>,
8538    source: &str,
8539) -> Vec<CppSentinelRecoveredClass> {
8540    if !root.has_error() {
8541        return Vec::new();
8542    }
8543    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
8544    let mut stack = vec![root];
8545    while let Some(current) = stack.pop() {
8546        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source)
8547            .or_else(|| cpp_root_namespace_sentinel(current, source))
8548        {
8549            let namespace_components = cpp_sentinel_recovered_namespace_components(
8550                recovered.function,
8551                &recovered.namespace_components,
8552                source,
8553            );
8554            let fragmented =
8555                cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, source);
8556            let mut class_candidates = Vec::new();
8557            let mut cursor = recovered.body.walk();
8558            for (class_node, template_node) in recovered
8559                .body
8560                .named_children(&mut cursor)
8561                .filter_map(cpp_sentinel_body_class_candidate)
8562            {
8563                let Some(name) = class_like_name(class_node, source) else {
8564                    continue;
8565                };
8566                if name.is_empty() || cpp_export_macro_token(&name) {
8567                    continue;
8568                }
8569                let is_fragmented = fragmented
8570                    .as_ref()
8571                    .is_some_and(|tail| same_node(tail.class_node, class_node));
8572                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
8573                    continue;
8574                }
8575                let class_range = if is_fragmented {
8576                    fragmented
8577                        .as_ref()
8578                        .map(|tail| tail.fragmented.class_range)
8579                        .expect("fragmented class range is present when class matches")
8580                } else {
8581                    cpp_declaration_range(template_node.unwrap_or(class_node))
8582                };
8583                class_candidates.push((class_range, name));
8584            }
8585            if let Some(fragmented) = fragmented
8586                .as_ref()
8587                .filter(|tail| tail.class_node.kind() == "ERROR")
8588            {
8589                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
8590            }
8591
8592            let mut owner_ranges =
8593                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
8594            cpp_sentinel_extend_unique_owner_ranges(
8595                &mut owner_ranges,
8596                cpp_sentinel_recovered_sibling_owner_ranges(
8597                    recovered.function,
8598                    &namespace_components,
8599                    source,
8600                ),
8601            );
8602            for (class_range, name) in class_candidates {
8603                push_cpp_sentinel_recovered_class(
8604                    &mut recovered_classes,
8605                    cpp_declaration_range(recovered.body),
8606                    &namespace_components,
8607                    class_range,
8608                    name,
8609                    &owner_ranges,
8610                );
8611            }
8612
8613            if let Some(declaration_list) = recovered
8614                .function
8615                .parent()
8616                .filter(|parent| parent.kind() == "declaration_list")
8617            {
8618                let outer_namespace =
8619                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
8620                push_cpp_sentinel_sibling_classes(
8621                    &mut recovered_classes,
8622                    declaration_list,
8623                    recovered.function,
8624                    &outer_namespace,
8625                    source,
8626                );
8627            }
8628        } else if let Some(region) = cpp_sentinel_macro_body_class_region(current, source) {
8629            let namespace_components = cpp_sentinel_recovered_namespace_components(
8630                current,
8631                &region.namespace_components,
8632                source,
8633            );
8634            let owner_container = current
8635                .parent()
8636                .filter(|parent| parent.kind() == "declaration_list")
8637                .unwrap_or(current);
8638            let owner_ranges =
8639                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
8640            push_cpp_sentinel_recovered_class(
8641                &mut recovered_classes,
8642                cpp_declaration_range(owner_container),
8643                &namespace_components,
8644                Range {
8645                    start_byte: region.class_start,
8646                    end_byte: region.class_close_end,
8647                    start_line: region.class_start_line,
8648                    end_line: region.class_close_line,
8649                },
8650                region.name,
8651                &owner_ranges,
8652            );
8653        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
8654            // A generic sentinel-prefixed class can be reduced as a malformed
8655            // function/ERROR without the explicit `namespace X` token pair.
8656            // Reuse the declaration visitor's bounded reparse and retain only
8657            // the recovered class identity/range here.
8658            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
8659                region;
8660            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
8661                continue;
8662            };
8663            let root = tree.root_node();
8664            let template_node = cpp_sentinel_reparsed_leading_template(root);
8665            let Some(reparsed_class) = cpp_sentinel_reparsed_class(root, template_node, source)
8666            else {
8667                continue;
8668            };
8669            let class_node = reparsed_class.declaration_node;
8670            let name = reparsed_class.name;
8671            let namespace_components =
8672                cpp_sentinel_recovered_namespace_components(current, &[], source);
8673            let owner_container = current
8674                .parent()
8675                .filter(|parent| parent.kind() == "declaration_list")
8676                .unwrap_or(current);
8677            let mut owner_ranges =
8678                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
8679            cpp_sentinel_extend_unique_owner_ranges(
8680                &mut owner_ranges,
8681                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
8682            );
8683            push_cpp_sentinel_recovered_class(
8684                &mut recovered_classes,
8685                cpp_declaration_range(owner_container),
8686                &namespace_components,
8687                Range {
8688                    start_byte: class_start,
8689                    end_byte: close_end,
8690                    start_line: class_node.start_position().row + 1,
8691                    end_line: class_node.end_position().row + 1,
8692                },
8693                name,
8694                &owner_ranges,
8695            );
8696            if owner_container.kind() == "declaration_list" {
8697                push_cpp_sentinel_sibling_classes(
8698                    &mut recovered_classes,
8699                    owner_container,
8700                    current,
8701                    &namespace_components,
8702                    source,
8703                );
8704            }
8705        }
8706
8707        let mut cursor = current.walk();
8708        stack.extend(current.named_children(&mut cursor));
8709    }
8710    // A shallower sentinel can expose nested classes as apparent namespace
8711    // siblings even after a deeper sentinel proves that a containing class
8712    // owns their ranges. Drop those shadow descriptors; scope recovery starts
8713    // from the proven containing class and appends parser-visible class
8714    // ancestors, preserving the full `Outer::Inner` chain.
8715    let shadowed = recovered_classes
8716        .iter()
8717        .map(|candidate| {
8718            recovered_classes.iter().any(|container| {
8719                container.class_range.start_byte <= candidate.class_range.start_byte
8720                    && container.class_range.end_byte >= candidate.class_range.end_byte
8721                    && container.class_range != candidate.class_range
8722                    && container.namespace_scope_components.len()
8723                        > candidate.namespace_scope_components.len()
8724                    && container
8725                        .namespace_scope_components
8726                        .starts_with(&candidate.namespace_scope_components)
8727            })
8728        })
8729        .collect::<Vec<_>>();
8730    let mut index = 0usize;
8731    recovered_classes.retain(|_| {
8732        let keep = !shadowed[index];
8733        index += 1;
8734        keep
8735    });
8736    recovered_classes
8737}
8738
8739/// A flat sentinel can swallow the first class while leaving later classes and
8740/// their out-of-line definitions as ordinary declaration-list siblings.  Once
8741/// the malformed class proves the sentinel envelope, retain those structurally
8742/// complete sibling classes under the same surviving namespace so every member
8743/// owner in the region uses one recovery contract.
8744fn push_cpp_sentinel_sibling_classes(
8745    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
8746    declaration_list: Node<'_>,
8747    sentinel_node: Node<'_>,
8748    namespace_components: &[String],
8749    source: &str,
8750) {
8751    let owner_ranges =
8752        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
8753    let namespace_range = cpp_declaration_range(declaration_list);
8754    let mut cursor = declaration_list.walk();
8755    for (class_node, template_node) in declaration_list
8756        .named_children(&mut cursor)
8757        .filter(|child| !same_node(*child, sentinel_node))
8758        .filter_map(cpp_sentinel_body_class_candidate)
8759    {
8760        let Some(name) = class_like_name(class_node, source) else {
8761            continue;
8762        };
8763        if name.is_empty()
8764            || cpp_export_macro_token(&name)
8765            || cpp_complete_class_body_close(class_node).is_none()
8766        {
8767            continue;
8768        }
8769        push_cpp_sentinel_recovered_class(
8770            recovered_classes,
8771            namespace_range,
8772            namespace_components,
8773            cpp_declaration_range(template_node.unwrap_or(class_node)),
8774            name,
8775            &owner_ranges,
8776        );
8777    }
8778}
8779
8780fn push_cpp_sentinel_recovered_class(
8781    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
8782    namespace_range: Range,
8783    namespace_components: &[String],
8784    class_range: Range,
8785    name: String,
8786    owner_ranges: &[CppSentinelRecoveredOwner],
8787) {
8788    let mut scope_components = namespace_components.to_vec();
8789    scope_components.push(name);
8790    let owner_ranges = owner_ranges
8791        .iter()
8792        .filter(|owner| owner.scope_components.starts_with(&scope_components))
8793        .cloned()
8794        .collect::<Vec<_>>();
8795    if recovered_classes.iter().any(|existing| {
8796        existing.class_range == class_range && existing.scope_components == scope_components
8797    }) {
8798        return;
8799    }
8800    recovered_classes.push(CppSentinelRecoveredClass {
8801        namespace_range,
8802        namespace_scope_components: namespace_components.to_vec(),
8803        class_range,
8804        scope_components,
8805        owner_ranges,
8806    });
8807}
8808
8809fn cpp_sentinel_recovered_namespace_components(
8810    function: Node<'_>,
8811    recovered_components: &[String],
8812    source: &str,
8813) -> Vec<String> {
8814    let mut ancestor_components = Vec::new();
8815    let mut ancestor = function.parent();
8816    while let Some(current) = ancestor {
8817        if current.kind() == "namespace_definition"
8818            && let Some(name_node) = current.child_by_field_name("name")
8819            && let Some(components) = cpp_name_components(name_node, source)
8820        {
8821            ancestor_components.push(
8822                components
8823                    .into_iter()
8824                    .map(|component| component.name)
8825                    .collect::<Vec<_>>(),
8826            );
8827        }
8828        ancestor = current.parent();
8829    }
8830    ancestor_components.reverse();
8831    let mut ancestors = ancestor_components
8832        .into_iter()
8833        .flatten()
8834        .collect::<Vec<_>>();
8835
8836    let overlap = (0..=ancestors.len().min(recovered_components.len()))
8837        .rev()
8838        .find(|length| {
8839            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
8840        })
8841        .unwrap_or(0);
8842    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
8843    ancestors
8844}
8845
8846fn cpp_sentinel_recovered_owner_ranges(
8847    body: Node<'_>,
8848    namespace_components: &[String],
8849    source: &str,
8850) -> Vec<CppSentinelRecoveredOwner> {
8851    let mut owners = Vec::new();
8852    walk_named_tree_preorder(body, true, |node| {
8853        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
8854    });
8855    owners
8856}
8857
8858fn cpp_sentinel_collect_owner_range(
8859    node: Node<'_>,
8860    namespace_components: &[String],
8861    source: &str,
8862    owners: &mut Vec<CppSentinelRecoveredOwner>,
8863) -> WalkControl {
8864    if node.kind() != "function_definition" {
8865        return WalkControl::Continue;
8866    }
8867    let Some(function_declarator) = extract_function_declarator(node) else {
8868        return WalkControl::Continue;
8869    };
8870    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
8871        return WalkControl::Continue;
8872    };
8873    let Some(mut components) = cpp_name_components(name_node, source) else {
8874        return WalkControl::Continue;
8875    };
8876    if components.len() <= 1 {
8877        return WalkControl::Continue;
8878    }
8879    components.pop();
8880    let mut owner_components = components
8881        .into_iter()
8882        .map(|component| component.name)
8883        .collect::<Vec<_>>();
8884    let overlap = (0..=namespace_components.len().min(owner_components.len()))
8885        .rev()
8886        .find(|length| {
8887            owner_components[..*length]
8888                == namespace_components[namespace_components.len().saturating_sub(*length)..]
8889        })
8890        .unwrap_or(0);
8891    let mut scope_components = namespace_components.to_vec();
8892    scope_components.extend(owner_components.drain(overlap..));
8893    if scope_components.len() <= namespace_components.len() {
8894        return WalkControl::Continue;
8895    }
8896    let range = cpp_declaration_range(node);
8897    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
8898        existing.range == range && existing.scope_components == scope_components
8899    }) {
8900        owners.push(CppSentinelRecoveredOwner {
8901            range,
8902            owner_name_start_byte: name_node.start_byte(),
8903            namespace_component_count: namespace_components.len(),
8904            scope_components,
8905        });
8906    }
8907    WalkControl::Continue
8908}
8909
8910fn cpp_sentinel_extend_unique_owner_ranges(
8911    owners: &mut Vec<CppSentinelRecoveredOwner>,
8912    additional: Vec<CppSentinelRecoveredOwner>,
8913) {
8914    for owner in additional {
8915        if !owners.iter().any(|existing| {
8916            existing.range == owner.range && existing.scope_components == owner.scope_components
8917        }) {
8918            owners.push(owner);
8919        }
8920    }
8921}
8922
8923fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
8924    if node.kind() != "ERROR" || node.named_child_count() != 1 {
8925        return false;
8926    }
8927    let Some(end_name) = node.named_child(0) else {
8928        return false;
8929    };
8930    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
8931        return false;
8932    }
8933    let mut cursor = node.walk();
8934    node.children(&mut cursor)
8935        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
8936}
8937
8938/// Collect owner definitions that the malformed sentinel left as later
8939/// declaration-list siblings. Parser-visible namespace siblings are a hard
8940/// boundary: their declarations must keep their own lexical namespace.
8941fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
8942    parent: Node<'_>,
8943    sentinel_node: Node<'_>,
8944    namespace_components: &[String],
8945    source: &str,
8946) -> Vec<CppSentinelRecoveredOwner> {
8947    let mut owners = Vec::new();
8948    let mut after_sentinel = false;
8949    let mut cursor = parent.walk();
8950    for child in parent.named_children(&mut cursor) {
8951        if !after_sentinel {
8952            if same_node(child, sentinel_node) {
8953                after_sentinel = true;
8954            }
8955            continue;
8956        }
8957        walk_named_tree_preorder(child, true, |node| {
8958            if node.kind() == "namespace_definition" {
8959                return WalkControl::SkipChildren;
8960            }
8961            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
8962        });
8963    }
8964    owners
8965}
8966
8967/// Collect owner definitions after a malformed namespace, stopping only at
8968/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
8969/// enclosing container is not trusted to belong to the recovered namespace.
8970fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
8971    parent: Node<'_>,
8972    sentinel_node: Node<'_>,
8973    namespace_components: &[String],
8974    source: &str,
8975) -> Option<Vec<CppSentinelRecoveredOwner>> {
8976    let mut owners = Vec::new();
8977    let mut after_namespace = false;
8978    let mut cursor = parent.walk();
8979    for child in parent.named_children(&mut cursor) {
8980        if !after_namespace {
8981            if same_node(child, sentinel_node) {
8982                after_namespace = true;
8983            }
8984            continue;
8985        }
8986        if cpp_sentinel_namespace_end(child, source) {
8987            return Some(owners);
8988        }
8989        walk_named_tree_preorder(child, true, |node| {
8990            if node.kind() == "namespace_definition" {
8991                return WalkControl::SkipChildren;
8992            }
8993            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
8994        });
8995    }
8996    None
8997}
8998
8999fn cpp_sentinel_recovered_sibling_owner_ranges(
9000    sentinel_node: Node<'_>,
9001    namespace_components: &[String],
9002    source: &str,
9003) -> Vec<CppSentinelRecoveredOwner> {
9004    let Some(declaration_list) = sentinel_node
9005        .parent()
9006        .filter(|parent| parent.kind() == "declaration_list")
9007    else {
9008        return Vec::new();
9009    };
9010    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
9011        declaration_list,
9012        sentinel_node,
9013        namespace_components,
9014        source,
9015    );
9016
9017    let Some(namespace) = declaration_list
9018        .parent()
9019        .filter(|parent| parent.kind() == "namespace_definition")
9020    else {
9021        return owners;
9022    };
9023    let Some(outer_parent) = namespace.parent() else {
9024        return owners;
9025    };
9026    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
9027        outer_parent,
9028        namespace,
9029        namespace_components,
9030        source,
9031    ) {
9032        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
9033    }
9034    owners
9035}
9036
9037fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
9038    let mut current = function_declarator.child_by_field_name("declarator")?;
9039    loop {
9040        if matches!(
9041            current.kind(),
9042            "qualified_identifier"
9043                | "scoped_identifier"
9044                | "scoped_type_identifier"
9045                | "identifier"
9046                | "field_identifier"
9047                | "operator_name"
9048                | "destructor_name"
9049                | "literal_operator_name"
9050        ) {
9051            return Some(current);
9052        }
9053        current = current
9054            .child_by_field_name("declarator")
9055            .or_else(|| current.child_by_field_name("name"))
9056            .or_else(|| last_named_child(current))?;
9057    }
9058}
9059
9060fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
9061    match node.kind() {
9062        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
9063            let mut components = match node.child_by_field_name("scope") {
9064                Some(scope) => cpp_name_components(scope, source)?,
9065                None => Vec::new(),
9066            };
9067            let name = node.child_by_field_name("name")?;
9068            components.push(canonical_cpp_qualified_component(name, source)?);
9069            Some(components)
9070        }
9071        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
9072    }
9073}
9074
9075fn cpp_sentinel_fragment_boundary<'tree>(
9076    function: Node<'tree>,
9077    class_node: Node<'tree>,
9078    class_body: Node<'tree>,
9079    source: &str,
9080) -> Option<(Node<'tree>, Node<'tree>)> {
9081    let declaration_list = function.parent()?;
9082    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
9083        return None;
9084    }
9085    let namespace = declaration_list.parent()?;
9086    if namespace.kind() != "namespace_definition"
9087        || namespace.child_by_field_name("body") != Some(declaration_list)
9088    {
9089        return None;
9090    }
9091    let mut cursor = declaration_list.walk();
9092    let closes = declaration_list
9093        .children(&mut cursor)
9094        .filter(|child| {
9095            !child.is_named()
9096                && child.kind() == "}"
9097                && child.start_byte() >= function.end_byte()
9098                && child.start_byte() > class_node.end_byte()
9099                && child.start_byte() > class_body.start_byte()
9100        })
9101        .collect::<Vec<_>>();
9102    let [close] = closes.as_slice() else {
9103        return None;
9104    };
9105    let semicolon = namespace.next_named_sibling()?;
9106    if !cpp_is_stray_semicolon(semicolon, source)
9107        || close.end_byte() != namespace.end_byte()
9108        || semicolon.start_byte() < namespace.end_byte()
9109    {
9110        return None;
9111    }
9112    Some((*close, semicolon))
9113}
9114
9115/// Detect the bogus declaration/function tree that tree-sitter recovers for a
9116/// region prefixed by an object-like macro sentinel the parser cannot see
9117/// (issue #941), and return the byte range `[start, end)` of the swallowed
9118/// declaration interior to reparse.
9119///
9120/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
9121/// `function_definition` whose first non-comment named child is the sentinel
9122/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
9123/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
9124/// the real items.
9125/// `start` is the end of the sentinel identifier -- everything after it is the
9126/// genuine source. `end` is the node's end, extended across any trailing empty
9127/// `;` statement the mis-parse displaced past the node (the class/struct closing
9128/// semicolon), so the reparse sees a complete, brace-balanced item.
9129///
9130/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
9131/// node (`has_error`). Unknown annotation/export macros can make a real callable
9132/// error-recovered even though tree-sitter still preserves its declarator, so a
9133/// preserved callable is admitted only when a displaced class keyword precedes
9134/// that declarator. The clean-reparse-to-items gate in
9135/// `cpp_reparsed_items_are_indexable` is the final arbiter.
9136/// Return the reparse start and, when present, the structurally recovered class
9137/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
9138/// separately from the reparse start because an opaque template-declaration
9139/// macro may precede it.
9140fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
9141    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
9142    {
9143        return None;
9144    }
9145    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
9146    // retain a valid function declarator despite the unknown export macro making
9147    // the outer node erroneous. Remember that declarator for the ordering gate
9148    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
9149    // class keyword precedes a spurious callable assembled from a later member.
9150    let mut declarator_cursor = node.walk();
9151    let preserved_callable = node
9152        .children_by_field_name("declarator", &mut declarator_cursor)
9153        .find_map(extract_function_declarator);
9154    // Leading documentation comments are attached to the malformed
9155    // `function_definition` as named children.  They are not part of the
9156    // sentinel prefix, so select the first non-comment child structurally
9157    // rather than requiring the sentinel to be child zero.  This is the shape
9158    // emitted for nlohmann/json's `basic_json`: its class documentation comment
9159    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
9160    // envelope otherwise ends at the first nested union.
9161    let mut cursor = node.walk();
9162    let first = node
9163        .named_children(&mut cursor)
9164        .find(|child| child.kind() != "comment")?;
9165    if first.kind() != "type_identifier" {
9166        return None;
9167    }
9168    let sentinel = normalize_cpp_whitespace(node_text(first, source));
9169    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
9170        return None;
9171    }
9172    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
9173    // makes the trailing sentinel of one region and the leading sentinel of the
9174    // next both land as bare macro-token identifiers ahead of the real content.
9175    // Advance past every leading macro-token identifier so the reparse begins at
9176    // genuine source rather than another sentinel that would re-form the bogus
9177    // shape and fail the reparse gate.
9178    let mut start = first.end_byte();
9179    let mut after_first = false;
9180    let mut cursor = node.walk();
9181    for child in node.named_children(&mut cursor) {
9182        if !after_first {
9183            if same_node(child, first) {
9184                after_first = true;
9185            }
9186            continue;
9187        }
9188        if matches!(child.kind(), "identifier" | "type_identifier")
9189            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
9190        {
9191            start = child.end_byte();
9192        } else {
9193            break;
9194        }
9195    }
9196    // An additional opaque template-declaration macro before a class can be
9197    // folded into the bogus function's qualified declarator.  In that shape
9198    // the macro is not a direct sibling we can skip above; tree-sitter exposes
9199    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
9200    // Reparse from that keyword (or a real preceding `template` keyword) so the
9201    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
9202    // a class nested in a genuine sentinel-wrapped namespace lies after the
9203    // body opening and must not change the established region start.
9204    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
9205    let mut class_start = None;
9206    let mut template_start = None;
9207    let mut stack = vec![node];
9208    while let Some(current) = stack.pop() {
9209        if current.start_byte() >= prefix_end {
9210            continue;
9211        }
9212        if matches!(
9213            current.kind(),
9214            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
9215        ) {
9216            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
9217                "class" | "struct" | "union" | "enum" => {
9218                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
9219                        seen.min(current.start_byte())
9220                    }));
9221                }
9222                "template" => {
9223                    template_start =
9224                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
9225                            seen.min(current.start_byte())
9226                        }));
9227                }
9228                _ => {}
9229            }
9230        }
9231        let mut cursor = current.walk();
9232        stack.extend(current.children(&mut cursor));
9233    }
9234    if preserved_callable.is_some_and(|callable| {
9235        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
9236    }) {
9237        return None;
9238    }
9239    if let Some(class_start) = class_start {
9240        start = template_start
9241            .filter(|template_start| *template_start < class_start)
9242            .unwrap_or(class_start);
9243    }
9244    Some((start, class_start))
9245}
9246
9247/// Locate a sentinel-prefixed class whose malformed declaration was split across
9248/// root-level siblings. The true class close is represented structurally as a
9249/// lone `}` error followed by the class's displaced `;`; nested method/body
9250/// errors are not direct siblings of the sentinel node and therefore cannot
9251/// satisfy this pair.
9252fn cpp_sentinel_macro_class_region(
9253    node: Node<'_>,
9254    source: &str,
9255) -> Option<(usize, usize, usize, usize, usize, usize)> {
9256    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
9257        return None;
9258    };
9259    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
9260        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
9261        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
9262    if class_start >= body_open_start {
9263        return None;
9264    }
9265    let sibling_close = {
9266        let mut sibling = node.next_named_sibling();
9267        let mut found = None;
9268        while let Some(current) = sibling {
9269            let next = current.next_named_sibling();
9270            if cpp_is_stray_close_brace(current, source)
9271                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
9272            {
9273                let semicolon = next.expect("checked above");
9274                found = Some((
9275                    current.start_byte(),
9276                    semicolon.end_byte(),
9277                    semicolon.end_position().row + 1,
9278                ));
9279                break;
9280            }
9281            sibling = next;
9282        }
9283        found
9284    };
9285    // A stray `};` sibling is this class's close only when the bounded reparse
9286    // agrees the first body-bearing class ENDS there. When the malformed
9287    // envelope swallowed the class's true close, the scan can promote a much
9288    // later scope's close instead -- in protobuf-generated headers
9289    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
9290    // `struct TableStruct_*` paired with the first message class's `};`, making
9291    // the recovered "class body" span whole `namespace {}` blocks and minting
9292    // namespace-scope classes as nested members of the recovered class, which
9293    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
9294    // (#2275). On disagreement, fall through to the suffix-reparse boundary
9295    // below, which derives the close from the class node's own balanced body
9296    // range.
9297    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
9298        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
9299            return false;
9300        };
9301        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
9302        let Some(reparsed_class) =
9303            cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)
9304        else {
9305            return false;
9306        };
9307        let body = reparsed_class.body;
9308        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
9309    });
9310    let (class_close_start, class_close_end, class_close_line) =
9311        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
9312            (class_close_start, class_close_end, class_close_line)
9313        } else {
9314            // When the malformed envelope itself is an ERROR, tree-sitter can
9315            // leave the class's balanced close in the source while promoting
9316            // all following members to siblings. Reparse the complete suffix
9317            // and use the first body-bearing class node's own field range as
9318            // the partition boundary. This keeps balancing in tree-sitter and
9319            // preserves the source's original byte offsets.
9320            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
9321            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
9322            let reparsed_class =
9323                cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)?;
9324            let body = reparsed_class.body;
9325            let class_close_end = body.end_byte();
9326            let class_close_start = class_close_end.checked_sub(1)?;
9327            let class_close_line = body.end_position().row + 1;
9328            (class_close_start, class_close_end, class_close_line)
9329        };
9330    if class_close_start <= class_start {
9331        return None;
9332    }
9333
9334    // Reparse only far enough to expose the class body opening. This is a
9335    // structured check that the candidate really begins with a body-bearing
9336    // class-like item; the original malformed tree cannot provide that node.
9337    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
9338    let class_root = tree.root_node();
9339    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9340    let reparsed_class = cpp_sentinel_reparsed_class(class_root, template_node, source)?;
9341    let body = reparsed_class.body;
9342    // The class body opening must agree with the malformed wrapper's structured
9343    // body field. This rejects an inner nested class while permitting later
9344    // members to remain fragmented as root-level siblings in the bounded parse.
9345    if body.start_byte() != body_open_start {
9346        return None;
9347    }
9348    let body_start = body.start_byte().checked_add(1)?;
9349    (body_start < class_close_start).then_some((
9350        reparse_start,
9351        class_start,
9352        body_start,
9353        class_close_start,
9354        class_close_end,
9355        class_close_line,
9356    ))
9357}
9358
9359/// Find the `{` token immediately following the class/struct/union/enum token
9360/// at `class_start` in the malformed tree. The token is anonymous in the C++
9361/// grammar, so this deliberately walks all children (not only named children)
9362/// and relies on sibling structure rather than source-text searching.
9363fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
9364    let mut stack = vec![node];
9365    while let Some(current) = stack.pop() {
9366        if current.start_byte() == class_start
9367            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
9368        {
9369            let mut sibling = current.next_sibling();
9370            while let Some(candidate) = sibling {
9371                if candidate.kind() == "{" {
9372                    return Some(candidate.start_byte());
9373                }
9374                sibling = candidate.next_sibling();
9375            }
9376        }
9377        let mut cursor = current.walk();
9378        stack.extend(current.children(&mut cursor));
9379    }
9380    None
9381}
9382
9383/// The class body that tree-sitter displaced out of a sentinel-prefixed
9384/// declaration and left as the malformed node's next sibling.
9385///
9386/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
9387/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
9388/// the last child of that `ERROR` and its `{` opens a sibling
9389/// `compound_statement` instead. The body is still the malformed tree's own
9390/// structured token, which is what the caller's `body.start_byte() !=
9391/// body_open_start` agreement check needs; it just is not reachable by walking
9392/// forward from the class token inside the node.
9393fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
9394    node.next_named_sibling()
9395        .filter(|sibling| sibling.kind() == "compound_statement")
9396}
9397
9398fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
9399    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
9400    let mut end = if class_start.is_some() {
9401        cpp_macro_prefixed_class_end(source, start)?
9402    } else {
9403        node.end_byte()
9404    };
9405    if class_start.is_none()
9406        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
9407    {
9408        end = end.max(namespace_end);
9409    }
9410    let mut sibling = node.next_named_sibling();
9411    while let Some(current) = sibling {
9412        if !cpp_is_stray_semicolon(current, source) {
9413            break;
9414        }
9415        end = current.end_byte();
9416        sibling = current.next_named_sibling();
9417    }
9418    (start < end).then_some((start, end))
9419}
9420
9421/// Extend a sentinel reparse through a following namespace that tree-sitter
9422/// flattened into the sentinel node's sibling list.
9423///
9424/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
9425/// unknown macro becomes a false function return type and consumes the first
9426/// namespace body. A second `namespace detail` then loses its enclosing node:
9427/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
9428/// attaches its declarations to the surrounding error tree. Reparse from that
9429/// structured keyword so tree-sitter, rather than a source-text brace scan,
9430/// supplies the complete namespace boundary.
9431fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
9432    let mut sibling = node.next_sibling();
9433    let keyword = loop {
9434        let candidate = sibling?;
9435        sibling = candidate.next_sibling();
9436        if candidate.kind() != "comment" {
9437            break candidate;
9438        }
9439    };
9440    if keyword.kind() != "namespace" {
9441        return None;
9442    }
9443    let name = loop {
9444        let candidate = sibling?;
9445        sibling = candidate.next_sibling();
9446        if candidate.kind() != "comment" {
9447            break candidate;
9448        }
9449    };
9450    if cpp_namespace_name_components(name, source).is_empty() {
9451        return None;
9452    }
9453    let open = loop {
9454        let candidate = sibling?;
9455        sibling = candidate.next_sibling();
9456        if candidate.kind() != "comment" {
9457            break candidate;
9458        }
9459    };
9460    if open.kind() != "{" {
9461        return None;
9462    }
9463
9464    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
9465    let root = tree.root_node();
9466    let mut cursor = root.walk();
9467    let namespace = root
9468        .named_children(&mut cursor)
9469        .find(|candidate| candidate.kind() != "comment")?;
9470    (namespace.kind() == "namespace_definition"
9471        && namespace.start_byte() == keyword.start_byte()
9472        && namespace.child_by_field_name("body").is_some())
9473    .then_some(namespace.end_byte())
9474}
9475
9476/// Parse the source suffix beginning at a structurally recovered class/template
9477/// keyword and return the end of its first body-bearing class item.  The parser,
9478/// rather than a brace scanner, owns nested-body balancing.  This is needed when
9479/// the original error tree truncates the class and scatters later members as
9480/// top-level siblings.
9481fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
9482    let tree = cpp_reparse_region_items(source, start, source.len())?;
9483    let root = tree.root_node();
9484    let mut cursor = root.walk();
9485    for item in root.named_children(&mut cursor) {
9486        if item.end_byte() <= start || item.kind() == "comment" {
9487            continue;
9488        }
9489        let mut stack = vec![item];
9490        while let Some(current) = stack.pop() {
9491            if matches!(
9492                current.kind(),
9493                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9494            ) && cpp_body_node(current).is_some()
9495            {
9496                return Some(current.end_byte());
9497            }
9498            let mut cursor = current.walk();
9499            stack.extend(current.named_children(&mut cursor));
9500        }
9501        // The recovered prefix is required to begin with the class item.  If
9502        // the first real item is something else, fail closed rather than skip
9503        // arbitrary source looking for a later class.
9504        return None;
9505    }
9506    None
9507}
9508
9509/// An empty `;` statement: the displaced closing semicolon of a struct/class that
9510/// the sentinel mis-parse split off past the bogus function node.
9511fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
9512    node.kind() == "expression_statement"
9513        && node.named_child_count() == 0
9514        && node_text(node, source).trim() == ";"
9515}
9516
9517/// Recover the real field name when a leading object-like annotation macro
9518/// displaces a qualified type into tree-sitter's bit-field recovery shape.
9519///
9520/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
9521/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
9522/// `bitfield_clause` containing an error plus an assignment.  The assignment's
9523/// left field is the only structured declaration name in that malformed tail.
9524/// A real bit-field is excluded by the all-caps macro type and required error.
9525fn recovered_macro_qualified_field_declarators<'tree>(
9526    node: Node<'tree>,
9527    source: &str,
9528) -> Option<Vec<Node<'tree>>> {
9529    if node.kind() != "field_declaration" {
9530        return None;
9531    }
9532    let macro_type = node.child_by_field_name("type")?;
9533    if macro_type.kind() != "type_identifier"
9534        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9535    {
9536        return None;
9537    }
9538    let pseudo_declarator = node.child_by_field_name("declarator")?;
9539    if pseudo_declarator.kind() != "field_identifier" {
9540        return None;
9541    }
9542    let mut cursor = node.walk();
9543    let clause = node
9544        .named_children(&mut cursor)
9545        .find(|child| child.kind() == "bitfield_clause")?;
9546    if !(0..clause.named_child_count()).any(|index| {
9547        clause
9548            .named_child(index)
9549            .is_some_and(|child| child.kind() == "ERROR")
9550    }) {
9551        return None;
9552    }
9553    let mut recovered = Vec::new();
9554    let mut stack = vec![clause];
9555    while let Some(current) = stack.pop() {
9556        if current.kind() == "assignment_expression"
9557            && let Some(left) = current.child_by_field_name("left")
9558            && extract_variable_name(left, source).is_some()
9559        {
9560            recovered.push(left);
9561            break;
9562        }
9563        let mut cursor = current.walk();
9564        stack.extend(current.named_children(&mut cursor));
9565    }
9566    if recovered.is_empty() {
9567        return None;
9568    }
9569    let mut cursor = node.walk();
9570    recovered.extend(
9571        node.children_by_field_name("declarator", &mut cursor)
9572            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
9573    );
9574    Some(recovered)
9575}
9576
9577/// Recover a macro-qualified constructor that tree-sitter represents as one
9578/// field declaration. The constructor call remains inside the direct recovery
9579/// error, while each member initializer becomes a false function declarator.
9580/// The class owner proves the constructor name and lets the caller ignore those
9581/// initializer declarators.
9582fn recovered_macro_qualified_constructor_call<'tree>(
9583    node: Node<'tree>,
9584    class_name: &str,
9585    source: &str,
9586) -> Option<Node<'tree>> {
9587    if node.kind() != "field_declaration" {
9588        return None;
9589    }
9590    let macro_type = node.child_by_field_name("type")?;
9591    if macro_type.kind() != "type_identifier"
9592        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9593    {
9594        return None;
9595    }
9596    let mut cursor = node.walk();
9597    let bitfield = node
9598        .named_children(&mut cursor)
9599        .find(|child| child.kind() == "bitfield_clause")?;
9600    let error = bitfield
9601        .named_child(0)
9602        .filter(|child| child.kind() == "ERROR")?;
9603    let mut stack = vec![error];
9604    while let Some(current) = stack.pop() {
9605        if current.kind() == "call_expression"
9606            && current
9607                .child_by_field_name("function")
9608                .is_some_and(|function| node_text(function, source) == class_name)
9609            && current
9610                .child_by_field_name("arguments")
9611                .is_some_and(|arguments| arguments.kind() == "argument_list")
9612        {
9613            return Some(current);
9614        }
9615        let mut cursor = current.walk();
9616        stack.extend(current.named_children(&mut cursor));
9617    }
9618    None
9619}
9620
9621/// Recover a macro-qualified member function declaration that tree-sitter
9622/// represents as a pseudo-field. An object-like export macro before a qualified
9623/// return type can displace the namespace and type into an ERROR/bitfield
9624/// recovery, leaving the callable as a structured `call_expression`.
9625///
9626/// The caller must route this shape before ordinary declarator classification;
9627/// otherwise the displaced namespace identifier is published as a field.
9628fn recovered_macro_qualified_function_call<'tree>(
9629    node: Node<'tree>,
9630    source: &str,
9631) -> Option<Node<'tree>> {
9632    if node.kind() != "field_declaration" {
9633        return None;
9634    }
9635    let macro_type = node.child_by_field_name("type")?;
9636    if macro_type.kind() != "type_identifier"
9637        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9638    {
9639        return None;
9640    }
9641    let declarator = node.child_by_field_name("declarator")?;
9642    if declarator.kind() != "field_identifier" {
9643        return None;
9644    }
9645    let mut cursor = node.walk();
9646    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9647    if !named.iter().any(|child| {
9648        child.kind() == "storage_class_specifier"
9649            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
9650    }) {
9651        return None;
9652    }
9653    let bitfield = named
9654        .iter()
9655        .find(|child| child.kind() == "bitfield_clause")?;
9656    let mut bitfield_cursor = bitfield.walk();
9657    let payload = bitfield
9658        .named_children(&mut bitfield_cursor)
9659        .collect::<Vec<_>>();
9660    let [displaced_error, call] = payload.as_slice() else {
9661        return None;
9662    };
9663    if displaced_error.kind() != "ERROR"
9664        || displaced_error.named_child_count() != 1
9665        || displaced_error
9666            .named_child(0)
9667            .is_none_or(|child| child.kind() != "identifier")
9668        || call.kind() != "call_expression"
9669        || call
9670            .child_by_field_name("function")
9671            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
9672        || call
9673            .child_by_field_name("arguments")
9674            .is_none_or(|arguments| arguments.kind() != "argument_list")
9675    {
9676        return None;
9677    }
9678    Some(*call)
9679}
9680
9681fn recovered_macro_qualified_function_parameters(
9682    arguments: Node<'_>,
9683    source: &str,
9684) -> Option<(String, Vec<String>)> {
9685    if arguments.kind() != "argument_list" {
9686        return None;
9687    }
9688    let mut cursor = arguments.walk();
9689    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
9690    if named.is_empty() {
9691        return Some(("()".to_string(), Vec::new()));
9692    }
9693    let mut types = Vec::new();
9694    let mut labels = Vec::new();
9695    let mut index = 0;
9696    while index < named.len() {
9697        let parameter_type = named[index];
9698        let parameter_name = named.get(index + 1).copied()?;
9699        if !matches!(
9700            parameter_type.kind(),
9701            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
9702        ) || parameter_name.kind() != "ERROR"
9703            || parameter_name.named_child_count() != 1
9704            || parameter_name
9705                .named_child(0)
9706                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
9707        {
9708            return None;
9709        }
9710        let parameter_name = parameter_name.named_child(0)?;
9711        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
9712        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
9713        index += 2;
9714    }
9715    Some((format!("({})", types.join(", ")), labels))
9716}
9717
9718/// Recognize the phantom field tree-sitter emits for a macro-qualified
9719/// function return type.  For example,
9720/// `static API result_type ThresholdForSmallA() { ... }` can become a
9721/// `field_declaration` (`API` as the type and `result_type` as a field name)
9722/// followed by a clean `function_definition` for `ThresholdForSmallA`.
9723///
9724/// Keep this predicate entirely tied to the CST envelope: the type must be an
9725/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
9726/// the declaration must carry a missing semicolon rather than a real one, and
9727/// the immediate named sibling must expose a function declarator.  A real
9728/// macro-decorated field with an explicit semicolon therefore remains a field.
9729pub fn recovered_macro_return_type_node<'tree>(
9730    node: Node<'tree>,
9731    source: &str,
9732) -> Option<Node<'tree>> {
9733    if node.kind() != "field_declaration" {
9734        return None;
9735    }
9736    let macro_type = node.child_by_field_name("type")?;
9737    if macro_type.kind() != "type_identifier"
9738        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9739    {
9740        return None;
9741    }
9742    let declarator = node.child_by_field_name("declarator")?;
9743    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
9744        return None;
9745    }
9746    let mut has_missing_semicolon = false;
9747    let mut has_real_semicolon = false;
9748    for index in 0..node.child_count() {
9749        let Some(child) = node.child(index) else {
9750            continue;
9751        };
9752        if child.kind() != ";" {
9753            continue;
9754        }
9755        if child.is_missing() {
9756            has_missing_semicolon = true;
9757        } else {
9758            has_real_semicolon = true;
9759        }
9760    }
9761    if !has_missing_semicolon || has_real_semicolon {
9762        return None;
9763    }
9764    let mut next = node.next_named_sibling();
9765    while next.is_some_and(|sibling| sibling.kind() == "comment") {
9766        next = next.and_then(|sibling| sibling.next_named_sibling());
9767    }
9768    let next = next?;
9769    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
9770        return None;
9771    }
9772    let function_declarator = next.child_by_field_name("declarator")?;
9773    extract_function_declarator(function_declarator).map(|_| declarator)
9774}
9775
9776/// Whether `name` is a type parameter of a template declaration that lexically
9777/// encloses `node`. The malformed macro-return field uses the parameter name as
9778/// its pseudo-declarator; preserving that field is necessary to publish a
9779/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
9780/// ancestors instead of interpreting source text so nested templates and
9781/// parser-recovered regions retain their real lexical scopes.
9782pub(crate) fn cpp_active_template_type_parameter(node: Node<'_>, name: &str, source: &str) -> bool {
9783    let mut ancestor = node.parent();
9784    while let Some(current) = ancestor {
9785        if current.kind() == "template_declaration"
9786            && let Some(parameters) = current.child_by_field_name("parameters")
9787        {
9788            let mut cursor = parameters.walk();
9789            if parameters.named_children(&mut cursor).any(|parameter| {
9790                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
9791                    && cpp_template_parameter_name(parameter, source)
9792                        .is_some_and(|parameter_name| parameter_name == name)
9793            }) {
9794                return true;
9795            }
9796        }
9797        ancestor = current.parent();
9798    }
9799    false
9800}
9801
9802/// Reparse the region `[start, end)` of `source` as C++, confined to the region
9803/// via included ranges so every reparsed node keeps its original byte offset and
9804/// line number. The existing visitors read node text from the original source,
9805/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
9806/// `parse_rust_region_tree` technique.
9807fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
9808    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
9809}
9810
9811fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
9812    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
9813        return None;
9814    }
9815    let semicolon = node.next_sibling()?;
9816    if semicolon.kind() != ";" || semicolon.is_missing() {
9817        return None;
9818    }
9819    let row = node.start_position().row;
9820    let mut start = node.start_byte();
9821    let mut sibling = node.prev_sibling();
9822    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
9823        if previous.kind() == ";" {
9824            break;
9825        }
9826        start = previous.start_byte();
9827        sibling = previous.prev_sibling();
9828    }
9829    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
9830}
9831
9832fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
9833    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
9834        return false;
9835    }
9836    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
9837        return false;
9838    }
9839    let Some(declarator) = (if node.kind() == "function_definition" {
9840        node.child_by_field_name("declarator")
9841            .and_then(extract_function_declarator)
9842    } else {
9843        node.named_child(0)
9844            .filter(|child| child.kind() == "function_declarator")
9845    }) else {
9846        return false;
9847    };
9848    let Some(name) = cpp_function_declarator_name_node(declarator) else {
9849        return false;
9850    };
9851    declarator.start_byte() == node.start_byte()
9852        && name.kind() == "identifier"
9853        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
9854}
9855
9856/// Reparse a fragmented class-body interior while preserving its original byte
9857/// and line offsets. Unlike an included-range translation-unit parse, a padded
9858/// prefix keeps C++ preprocessor directives after an access label in the same
9859/// recovery shape tree-sitter produces for a complete class body.
9860fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
9861    let bytes = source.as_bytes();
9862    let prefix = bytes.get(..start)?;
9863    let interior = bytes.get(start..end)?;
9864    let mut padded = Vec::with_capacity(end);
9865    padded.extend(
9866        prefix
9867            .iter()
9868            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
9869    );
9870    padded.extend_from_slice(interior);
9871    let padded = String::from_utf8(padded).ok()?;
9872    let mut parser = Parser::new();
9873    parser
9874        .set_language(&tree_sitter_cpp::LANGUAGE.into())
9875        .ok()?;
9876    parser.parse(&padded, None)
9877}
9878
9879/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
9880/// reparsed interior is indexed only when every top-level named node is a
9881/// well-formed C++ item (or a comment) and at least one real item is present.
9882/// Expression/statement soup surfaces as a top-level `ERROR` or
9883/// `expression_statement`, neither of which is an item kind, so it is rejected.
9884///
9885/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
9886/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
9887/// swallowed by a preceding dangling sentinel) reparses to a real
9888/// `namespace_definition` whose body still holds a bogus `function_definition`,
9889/// so the subtree legitimately carries an error. Container items are admitted
9890/// even with an internal error; the inner bogus function is recovered recursively
9891/// when `visit_function_definition` walks it. Each recursion strips at least one
9892/// leading sentinel, so the region strictly shrinks and recovery terminates.
9893///
9894/// A top-level `function_definition` is the one place we stay strict: it is
9895/// admitted only when it is clean or is itself a sentinel candidate. A function
9896/// that has an error and is not a sentinel is a real callable with a broken body,
9897/// so we refuse the whole reparse and let the ordinary path handle it (preserving
9898/// its real return type rather than re-deriving an implicit one).
9899fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
9900    let mut cursor = root.walk();
9901    let mut saw_item = false;
9902    for child in root.named_children(&mut cursor) {
9903        match child.kind() {
9904            "comment" => {}
9905            "function_definition" => {
9906                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
9907                    return false;
9908                }
9909                saw_item = true;
9910            }
9911            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
9912            _ => return false,
9913        }
9914    }
9915    saw_item
9916}
9917
9918/// Robustness gate for a reparsed fragmented multiple-base export class body
9919/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
9920/// kinds a class body produces when reparsed at translation-unit scope: the
9921/// access-specifier label preceding the first member surfaces as a
9922/// `labeled_statement` wrapping that member, and members surface as
9923/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
9924/// Statement or expression soup surfaces as other top-level kinds and is rejected,
9925/// so only a genuinely member-shaped body is ever re-owned as members; anything
9926/// ambiguous falls back to indexing the class alone.
9927fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
9928    if node.kind() != "ERROR" {
9929        return false;
9930    }
9931    let mut stack = Vec::new();
9932    let mut saw_function_declarator = false;
9933    let mut cursor = node.walk();
9934    for child in node.named_children(&mut cursor) {
9935        stack.push(child);
9936    }
9937    while let Some(current) = stack.pop() {
9938        match current.kind() {
9939            // Tree-sitter may wrap adjacent copy-control declarations in a
9940            // nested ERROR. Keep descending only through ERROR wrappers; the
9941            // actual declaration payload must be a function_declarator.
9942            "ERROR" => {
9943                let mut cursor = current.walk();
9944                stack.extend(current.named_children(&mut cursor));
9945            }
9946            "function_declarator" => saw_function_declarator = true,
9947            _ => return false,
9948        }
9949    }
9950    saw_function_declarator
9951}
9952
9953fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
9954    if node.kind() != "ERROR" {
9955        return false;
9956    }
9957    let mut cursor = node.walk();
9958    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9959    let [explicit, constructor_error, destructor] = named.as_slice() else {
9960        return false;
9961    };
9962    let Some(constructor) = constructor_error.named_child(0) else {
9963        return false;
9964    };
9965    let Some(constructor_name) =
9966        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
9967    else {
9968        return false;
9969    };
9970    let Some(destructor_name) =
9971        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
9972    else {
9973        return false;
9974    };
9975    let Some(destroyed_type) = destructor_name.named_child(0) else {
9976        return false;
9977    };
9978    explicit.kind() == "explicit_function_specifier"
9979        && constructor_error.kind() == "ERROR"
9980        && constructor_error.named_child_count() == 1
9981        && constructor.kind() == "function_declarator"
9982        && constructor_name.kind() == "identifier"
9983        && destructor.kind() == "function_declarator"
9984        && destructor_name.kind() == "destructor_name"
9985        && destroyed_type.kind() == "identifier"
9986        && node_text(constructor_name, source) == node_text(destroyed_type, source)
9987}
9988
9989fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
9990    if node.kind() != "compound_statement" {
9991        return false;
9992    }
9993    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
9994        return false;
9995    };
9996    if prefix.kind() == "labeled_statement"
9997        && prefix.named_child(0).is_some_and(|label| {
9998            matches!(
9999                node_text(label, source).trim(),
10000                "public" | "private" | "protected"
10001            )
10002        })
10003    {
10004        return prefix.named_children(&mut prefix.walk()).any(|child| {
10005            child.kind() == "declaration"
10006                && child.has_error()
10007                && child
10008                    .named_children(&mut child.walk())
10009                    .any(cpp_reparsed_member_error_is_indexable)
10010        });
10011    }
10012    // A malformed constructor initializer can be split into a declaration
10013    // followed by its compound body when the class prefix already contains
10014    // realistic members. Keep this admission tied to that exact structured
10015    // declaration/error/body chain rather than accepting arbitrary blocks.
10016    prefix.kind() == "declaration"
10017        && prefix.has_error()
10018        && prefix
10019            .named_children(&mut prefix.walk())
10020            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
10021}
10022
10023fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
10024    if !cpp_reparsed_member_error_is_indexable(node) {
10025        return false;
10026    }
10027    let Some(preproc) = node.next_named_sibling() else {
10028        return false;
10029    };
10030    preproc.kind() == "preproc_if"
10031        && preproc.has_error()
10032        && preproc
10033            .named_children(&mut preproc.walk())
10034            .any(|child| child.kind() == "expression_statement" && child.has_error())
10035        && preproc
10036            .next_named_sibling()
10037            .is_some_and(|body| body.kind() == "compound_statement")
10038}
10039
10040/// Return a function body whose braces and ownership are explicit in the
10041/// reparsed class-member tree. An error below a real function envelope is
10042/// recoverable by the ordinary function visitor; a missing/deferred body is
10043/// not, because accepting it would let statement soup masquerade as a member.
10044fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
10045    if node.kind() != "function_definition" {
10046        return None;
10047    }
10048    let body = node.child_by_field_name("body")?;
10049    if body.kind() != "compound_statement" {
10050        return None;
10051    }
10052    let open = body.child(0)?;
10053    let close = body.child(body.child_count().checked_sub(1)?)?;
10054    if open.kind() != "{"
10055        || open.is_missing()
10056        || close.kind() != "}"
10057        || close.is_missing()
10058        || close.end_byte() != body.end_byte()
10059        || body.end_byte() != node.end_byte()
10060    {
10061        return None;
10062    }
10063    Some(body)
10064}
10065
10066fn cpp_reparsed_member_function_errors_are_in_body(
10067    node: Node<'_>,
10068    body: Node<'_>,
10069    source: &str,
10070) -> bool {
10071    let mut cursor = node.walk();
10072    node.children(&mut cursor).all(|child| {
10073        same_node(child, body)
10074            || cpp_reparsed_member_attribute_error(child, source)
10075            || cpp_reparsed_member_signature_identifier_errors(child)
10076            || (!child.has_error() && !child.is_error() && !child.is_missing())
10077    })
10078}
10079
10080/// A complete callable can still carry parser errors in its signature when a
10081/// project annotation is not part of the C++ grammar (`nonneg int`,
10082/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
10083/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
10084/// leaves inside the already-proven callable envelope; structured statements,
10085/// literals, missing tokens, and other malformed signature payload remain
10086/// rejected.
10087fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
10088    if !node.has_error() && !node.is_error() && !node.is_missing() {
10089        return false;
10090    }
10091    let mut stack = vec![node];
10092    let mut saw_error = false;
10093    while let Some(current) = stack.pop() {
10094        if current.is_missing() {
10095            return false;
10096        }
10097        if current.kind() == "ERROR" {
10098            saw_error = true;
10099            let mut cursor = current.walk();
10100            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
10101            if children
10102                .iter()
10103                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
10104            {
10105                return false;
10106            }
10107            stack.extend(children);
10108            continue;
10109        }
10110        let mut cursor = current.walk();
10111        stack.extend(current.children(&mut cursor));
10112    }
10113    saw_error
10114}
10115
10116fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
10117    node.kind() == "ERROR"
10118        && node.named_child_count() == 1
10119        && node.named_child(0).is_some_and(|attribute| {
10120            attribute.kind() == "identifier"
10121                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
10122        })
10123}
10124
10125/// A C++ attribute placed between a member's declarator and body can make
10126/// tree-sitter expose the callable as
10127/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
10128/// Keep this admission tied to that exact node geometry. In particular, an
10129/// arbitrary ERROR or identifier before a compound statement is not enough.
10130fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
10131    let Some(body) = cpp_reparsed_member_function_body(node) else {
10132        return false;
10133    };
10134    let mut cursor = node.walk();
10135    let named = node
10136        .named_children(&mut cursor)
10137        .filter(|child| child.kind() != "comment")
10138        .collect::<Vec<_>>();
10139    let [type_node, error, attribute, body_node] = named.as_slice() else {
10140        return false;
10141    };
10142    if !same_node(*body_node, body)
10143        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
10144        || attribute.kind() != "identifier"
10145        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
10146        || error.kind() != "ERROR"
10147        || error.named_child_count() != 1
10148    {
10149        return false;
10150    }
10151    error
10152        .named_child(0)
10153        .is_some_and(cpp_reparsed_attribute_callable_declarator)
10154}
10155
10156fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
10157    cpp_structured_type_path(node, source).is_some()
10158        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
10159}
10160
10161fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10162    let Some(body) = cpp_reparsed_member_function_body(node) else {
10163        return false;
10164    };
10165    let mut cursor = node.walk();
10166    let named = node
10167        .named_children(&mut cursor)
10168        .filter(|child| child.kind() != "comment")
10169        .collect::<Vec<_>>();
10170    let [friend, return_error, declarator, body_node] = named.as_slice() else {
10171        return false;
10172    };
10173    let Some(return_type) = return_error.named_child(0) else {
10174        return false;
10175    };
10176    same_node(*body_node, body)
10177        && friend.kind() == "type_identifier"
10178        && node_text(*friend, source) == "friend"
10179        && return_error.kind() == "ERROR"
10180        && return_error.named_child_count() == 1
10181        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
10182        && extract_function_declarator(*declarator)
10183            .and_then(cpp_function_declarator_name_node)
10184            .is_some()
10185}
10186
10187fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10188    let Some(body) = cpp_reparsed_member_function_body(node) else {
10189        return false;
10190    };
10191    let mut cursor = node.walk();
10192    let named = node
10193        .named_children(&mut cursor)
10194        .filter(|child| child.kind() != "comment")
10195        .collect::<Vec<_>>();
10196    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
10197        return false;
10198    };
10199    let Some(return_type) = return_error.named_child(0) else {
10200        return false;
10201    };
10202    same_node(*body_node, body)
10203        && prefix
10204            .iter()
10205            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
10206        && attribute.kind() == "type_identifier"
10207        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
10208        && return_error.kind() == "ERROR"
10209        && return_error.named_child_count() == 1
10210        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
10211        && extract_function_declarator(*declarator)
10212            .and_then(cpp_function_declarator_name_node)
10213            .is_some()
10214}
10215
10216/// An included-range reparse that begins inside a malformed class can merge an
10217/// access label and following template member. Tree-sitter then emits the label
10218/// as the `template_type` name, the template parameter list as its arguments,
10219/// an ERROR-wrapped return type, the callable declarator, and its complete
10220/// body. Admit only that exact structured displacement.
10221fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10222    let Some(body) = cpp_reparsed_member_function_body(node) else {
10223        return false;
10224    };
10225    let mut cursor = node.walk();
10226    let named = node
10227        .named_children(&mut cursor)
10228        .filter(|child| child.kind() != "comment")
10229        .collect::<Vec<_>>();
10230    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
10231        return false;
10232    };
10233    let Some(template_name) = template_type.child_by_field_name("name") else {
10234        return false;
10235    };
10236    let Some(arguments) = template_type.child_by_field_name("arguments") else {
10237        return false;
10238    };
10239    let Some(return_type) = return_error.named_child(0) else {
10240        return false;
10241    };
10242    let mut cursor = template_type.walk();
10243    let template_errors = template_type
10244        .named_children(&mut cursor)
10245        .filter(|child| child.kind() == "ERROR")
10246        .collect::<Vec<_>>();
10247    let [comment_error] = template_errors.as_slice() else {
10248        return false;
10249    };
10250    let mut cursor = comment_error.walk();
10251    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
10252    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
10253        return false;
10254    };
10255    same_node(*body_node, body)
10256        && template_type.kind() == "template_type"
10257        && template_name.kind() == "type_identifier"
10258        && matches!(
10259            node_text(template_name, source).trim(),
10260            "public" | "private" | "protected"
10261        )
10262        && arguments.kind() == "template_argument_list"
10263        && arguments.named_child_count() > 0
10264        && !arguments.has_error()
10265        && !colon.is_named()
10266        && colon.kind() == ":"
10267        && comments.iter().all(|child| child.kind() == "comment")
10268        && !template_keyword.is_named()
10269        && template_keyword.kind() == "template"
10270        && return_error.kind() == "ERROR"
10271        && return_error.named_child_count() == 1
10272        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
10273        && extract_function_declarator(*declarator)
10274            .and_then(cpp_function_declarator_name_node)
10275            .is_some()
10276}
10277
10278/// Return the constructor declaration tree-sitter can merge into an access
10279/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
10280/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
10281/// declaration's apparent type; the callable name must still exactly match the
10282/// recovered class, so unrelated labeled statements are never re-owned.
10283fn cpp_reparsed_preprocessor_constructor<'tree>(
10284    node: Node<'tree>,
10285    class_name: &str,
10286    source: &str,
10287) -> Option<Node<'tree>> {
10288    if node.kind() != "labeled_statement" {
10289        return None;
10290    }
10291    let mut cursor = node.walk();
10292    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10293    let [label, directive_error, declaration] = named.as_slice() else {
10294        return None;
10295    };
10296    if label.kind() != "statement_identifier"
10297        || !matches!(
10298            node_text(*label, source),
10299            "public" | "private" | "protected"
10300        )
10301        || directive_error.kind() != "ERROR"
10302        || directive_error.child_count() != 1
10303        || directive_error
10304            .child(0)
10305            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
10306        || declaration.kind() != "declaration"
10307        || declaration.named_child_count() != 2
10308    {
10309        return None;
10310    }
10311    let apparent_type = declaration.child_by_field_name("type")?;
10312    if apparent_type.kind() != "type_identifier"
10313        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
10314    {
10315        return None;
10316    }
10317    let declarator = declaration.child_by_field_name("declarator")?;
10318    let function = extract_function_declarator(declarator)?;
10319    let name = cpp_function_declarator_name_node(function)?;
10320    (node_text(name, source) == class_name).then_some(*declaration)
10321}
10322
10323fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
10324    if extract_function_declarator(node)
10325        .and_then(cpp_function_declarator_name_node)
10326        .is_some()
10327    {
10328        return true;
10329    }
10330    node.kind() == "init_declarator"
10331        && node
10332            .child_by_field_name("declarator")
10333            .is_some_and(|declarator| declarator.kind() == "identifier")
10334        && node
10335            .child_by_field_name("value")
10336            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
10337}
10338
10339/// Return true for the constrained/attribute form that tree-sitter splits into
10340/// an ERROR declaration, a preprocessor `requires` clause, and a following
10341/// compound statement. The three nodes must remain immediate named siblings;
10342/// this deliberately does not search source text or skip unrelated statements.
10343fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
10344    if node.kind() != "ERROR" || node.named_child_count() != 3 {
10345        return false;
10346    }
10347    let mut cursor = node.walk();
10348    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10349    let [type_node, function_declarator, attribute] = named.as_slice() else {
10350        return false;
10351    };
10352    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
10353        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
10354        || attribute.kind() != "identifier"
10355        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
10356    {
10357        return false;
10358    }
10359    let Some(preproc) =
10360        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
10361    else {
10362        return false;
10363    };
10364    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
10365        .filter(|sibling| sibling.kind() == "compound_statement")
10366    else {
10367        return false;
10368    };
10369    let Some(open) = body.child(0) else {
10370        return false;
10371    };
10372    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
10373        return false;
10374    };
10375    let Some(condition) = preproc.child_by_field_name("condition") else {
10376        return false;
10377    };
10378    let mut cursor = preproc.walk();
10379    let payload = preproc
10380        .named_children(&mut cursor)
10381        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
10382        .collect::<Vec<_>>();
10383    let [requires_statement] = payload.as_slice() else {
10384        return false;
10385    };
10386    let requires_clause = requires_statement.named_child(0);
10387
10388    open.kind() == "{"
10389        && !open.is_missing()
10390        && close.kind() == "}"
10391        && !close.is_missing()
10392        && close.end_byte() == body.end_byte()
10393        && requires_statement.kind() == "expression_statement"
10394        && requires_statement.named_child_count() == 1
10395        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
10396}
10397
10398fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
10399    let mut sibling = node.next_named_sibling();
10400    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
10401        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
10402    }
10403    sibling
10404}
10405
10406fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
10407    let mut sibling = node.prev_named_sibling();
10408    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
10409        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
10410    }
10411    sibling
10412}
10413
10414fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
10415    let Some(preproc) =
10416        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
10417    else {
10418        return false;
10419    };
10420    let Some(error) =
10421        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
10422    else {
10423        return false;
10424    };
10425    cpp_reparsed_attribute_requires_error(error, source)
10426}
10427
10428fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
10429    node: Node<'tree>,
10430    source: &str,
10431) -> Option<Node<'tree>> {
10432    if node.kind() != "ERROR" {
10433        return None;
10434    }
10435    let mut cursor = node.walk();
10436    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10437    let [parameter, macro_name, message] = named.as_slice() else {
10438        return None;
10439    };
10440    let parameter_name = parameter.named_child(0)?;
10441    (parameter.kind() == "type_parameter_declaration"
10442        && parameter_name.kind() == "type_identifier"
10443        && macro_name.kind() == "type_identifier"
10444        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
10445        && message.kind() == "string_literal")
10446        .then_some(parameter_name)
10447}
10448
10449/// Recognize the alternate constraint-macro prefix where tree-sitter retains
10450/// the complete qualified constraint as a fourth child instead of moving it
10451/// into the following function. Keep the gate tied to a two-type template
10452/// constraint that names the declared type parameter.
10453fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
10454    node: Node<'tree>,
10455    source: &str,
10456) -> Option<Node<'tree>> {
10457    if node.kind() != "ERROR" {
10458        return None;
10459    }
10460    let mut cursor = node.walk();
10461    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10462    let [parameter, macro_name, message, constraint] = named.as_slice() else {
10463        return None;
10464    };
10465    let parameter_name = parameter.named_child(0)?;
10466    let constraint_scope = constraint.child_by_field_name("scope")?;
10467    let constraint_template = constraint.child_by_field_name("name")?;
10468    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
10469    let mut argument_cursor = constraint_arguments.walk();
10470    let constraint_types = constraint_arguments
10471        .named_children(&mut argument_cursor)
10472        .collect::<Vec<_>>();
10473    if parameter.kind() != "type_parameter_declaration"
10474        || parameter_name.kind() != "type_identifier"
10475        || macro_name.kind() != "type_identifier"
10476        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
10477        || message.kind() != "string_literal"
10478        || constraint.kind() != "qualified_identifier"
10479        || constraint_scope.kind() != "namespace_identifier"
10480        || !matches!(
10481            constraint_template.kind(),
10482            "template_function" | "template_type"
10483        )
10484        || !matches!(constraint_types.as_slice(), [left, right]
10485            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
10486        || constraint_arguments.has_error()
10487    {
10488        return None;
10489    }
10490    let parameter_text = node_text(parameter_name, source);
10491    let mut stack = constraint_types;
10492    while let Some(current) = stack.pop() {
10493        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
10494            return Some(parameter_name);
10495        }
10496        let mut cursor = current.walk();
10497        stack.extend(current.named_children(&mut cursor));
10498    }
10499    None
10500}
10501
10502fn cpp_reparsed_template_macro_companion_is_indexable(
10503    node: Node<'_>,
10504    parameter_name: Node<'_>,
10505    source: &str,
10506) -> bool {
10507    let Some(body) = cpp_reparsed_member_function_body(node) else {
10508        return false;
10509    };
10510    let mut cursor = node.walk();
10511    let named = node
10512        .named_children(&mut cursor)
10513        .filter(|child| child.kind() != "comment")
10514        .collect::<Vec<_>>();
10515    let [
10516        constraint,
10517        close_error,
10518        storage,
10519        return_error,
10520        declarator,
10521        body_node,
10522    ] = named.as_slice()
10523    else {
10524        return false;
10525    };
10526    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
10527        return false;
10528    };
10529    let Some(constraint_template) = constraint.child_by_field_name("name") else {
10530        return false;
10531    };
10532    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
10533        return false;
10534    };
10535    let Some(return_type) = return_error.named_child(0) else {
10536        return false;
10537    };
10538    let mut cursor = constraint_arguments.walk();
10539    let constraint_types = constraint_arguments
10540        .named_children(&mut cursor)
10541        .collect::<Vec<_>>();
10542    same_node(*body_node, body)
10543        && constraint.kind() == "qualified_identifier"
10544        && constraint_scope.kind() == "namespace_identifier"
10545        && constraint_template.kind() == "template_type"
10546        && matches!(constraint_types.as_slice(), [left, right]
10547            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
10548        && !constraint_arguments.has_error()
10549        && close_error.kind() == "ERROR"
10550        && close_error.named_child_count() == 0
10551        && storage.kind() == "storage_class_specifier"
10552        && return_error.kind() == "ERROR"
10553        && return_error.named_child_count() == 1
10554        && return_type.kind() == "identifier"
10555        && node_text(return_type, source) == node_text(parameter_name, source)
10556        && extract_function_declarator(*declarator)
10557            .and_then(cpp_function_declarator_name_node)
10558            .is_some()
10559}
10560
10561fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
10562    node: Node<'tree>,
10563    parameter_name: Node<'_>,
10564    source: &str,
10565) -> Option<Node<'tree>> {
10566    let body = cpp_reparsed_member_function_body(node)?;
10567    let constraint = node.child_by_field_name("type")?;
10568    let constraint_template = constraint.child_by_field_name("name")?;
10569    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
10570    let mut argument_cursor = constraint_arguments.walk();
10571    let constraint_types = constraint_arguments
10572        .named_children(&mut argument_cursor)
10573        .collect::<Vec<_>>();
10574    if constraint.kind() != "qualified_identifier"
10575        || constraint_template.kind() != "template_type"
10576        || !matches!(constraint_types.as_slice(), [left, right]
10577            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
10578        || constraint_arguments.has_error()
10579        || node
10580            .child_by_field_name("body")
10581            .is_none_or(|candidate| !same_node(candidate, body))
10582    {
10583        return None;
10584    }
10585
10586    let mut cursor = node.walk();
10587    let recovery_errors = node
10588        .named_children(&mut cursor)
10589        .filter(|child| child.kind() == "ERROR")
10590        .collect::<Vec<_>>();
10591    if !recovery_errors
10592        .iter()
10593        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
10594        || !recovery_errors.iter().all(|error| {
10595            error.named_child_count() == 0
10596                || cpp_reparsed_constraint_macro_error(*error, source)
10597                || (error.named_child_count() == 1
10598                    && error
10599                        .named_child(0)
10600                        .is_some_and(|child| child.kind() == "function_declarator"))
10601        })
10602    {
10603        return None;
10604    }
10605
10606    let parameter_text = node_text(parameter_name, source);
10607    let mut declarators = node
10608        .child_by_field_name("declarator")
10609        .and_then(extract_function_declarator)
10610        .into_iter()
10611        .collect::<Vec<_>>();
10612    for error in recovery_errors {
10613        let mut stack = vec![error];
10614        while let Some(current) = stack.pop() {
10615            if current.kind() == "function_declarator" {
10616                declarators.push(current);
10617            }
10618            let mut cursor = current.walk();
10619            stack.extend(current.named_children(&mut cursor));
10620        }
10621    }
10622    declarators.into_iter().find(|declarator| {
10623        cpp_function_declarator_name_node(*declarator)
10624            .is_some_and(|name| name.kind() == "identifier")
10625            && declarator
10626                .child_by_field_name("parameters")
10627                .is_some_and(|parameters| {
10628                    parameters
10629                        .named_children(&mut parameters.walk())
10630                        .filter_map(|parameter| parameter.child_by_field_name("type"))
10631                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
10632                })
10633    })
10634}
10635
10636fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
10637    node: Node<'_>,
10638    parameter_name: Node<'_>,
10639    source: &str,
10640) -> bool {
10641    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
10642}
10643
10644fn cpp_reparsed_template_macro_function_companion_is_indexable(
10645    node: Node<'_>,
10646    parameter_name: Node<'_>,
10647    source: &str,
10648) -> bool {
10649    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
10650        return false;
10651    }
10652    let Some(return_type) = node.child_by_field_name("type") else {
10653        return false;
10654    };
10655    let Some(function_declarator) = node
10656        .child_by_field_name("declarator")
10657        .and_then(extract_function_declarator)
10658    else {
10659        return false;
10660    };
10661    if cpp_function_declarator_name_node(function_declarator).is_none()
10662        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
10663    {
10664        return false;
10665    }
10666    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
10667        return false;
10668    };
10669    let parameter_text = node_text(parameter_name, source);
10670    parameters
10671        .named_children(&mut parameters.walk())
10672        .any(|parameter| {
10673            parameter
10674                .child_by_field_name("type")
10675                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
10676        })
10677}
10678
10679fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
10680    if node.kind() != "ERROR" {
10681        return false;
10682    }
10683    let mut stack = vec![node];
10684    while let Some(current) = stack.pop() {
10685        let macro_shape = match current.kind() {
10686            "call_expression" => current
10687                .child_by_field_name("function")
10688                .zip(current.child_by_field_name("arguments")),
10689            "init_declarator" => current
10690                .child_by_field_name("declarator")
10691                .zip(current.child_by_field_name("value")),
10692            _ => None,
10693        };
10694        if let Some((name, arguments)) = macro_shape
10695            && name.kind() == "identifier"
10696            && arguments.kind() == "argument_list"
10697            && arguments.named_child_count() >= 2
10698            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
10699        {
10700            return true;
10701        }
10702        let mut cursor = current.walk();
10703        stack.extend(current.named_children(&mut cursor));
10704    }
10705    false
10706}
10707
10708fn cpp_recovered_template_macro_constructor<'tree>(
10709    node: Node<'tree>,
10710    source: &str,
10711) -> Option<(Node<'tree>, Node<'tree>)> {
10712    let mut prefix = node.prev_named_sibling()?;
10713    while prefix.kind() == "comment" {
10714        prefix = prefix.prev_named_sibling()?;
10715    }
10716    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
10717    let parameter = parameter_name
10718        .parent()
10719        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
10720    let declarator =
10721        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
10722    Some((declarator, parameter))
10723}
10724
10725fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
10726    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
10727        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
10728            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
10729                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
10730                    function,
10731                    parameter_name,
10732                    source,
10733                )
10734        });
10735    }
10736    let Some(parameter_name) =
10737        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
10738    else {
10739        return false;
10740    };
10741    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
10742        cpp_reparsed_template_macro_function_companion_is_indexable(
10743            function,
10744            parameter_name,
10745            source,
10746        )
10747    })
10748}
10749
10750fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10751    let function_name = node
10752        .child_by_field_name("declarator")
10753        .and_then(extract_function_declarator)
10754        .and_then(cpp_function_declarator_name_node);
10755    if let Some(body) = cpp_reparsed_member_function_body(node)
10756        && function_name.is_some()
10757        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
10758    {
10759        return true;
10760    }
10761    cpp_reparsed_attribute_member_function(node, source)
10762        || cpp_reparsed_friend_function_is_indexable(node, source)
10763        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
10764        || cpp_reparsed_access_template_function_is_indexable(node, source)
10765        || cpp_recovered_template_macro_constructor(node, source).is_some()
10766}
10767
10768/// Recognize the three top-level nodes produced when an unknown attribute
10769/// macro separates an inline member's declarator from its body in a reparsed
10770/// class interior: an errorful declaration with a missing semicolon, the macro
10771/// call expression, and the complete compound body. Their adjacency and exact
10772/// structured shapes prove one recoverable member envelope; arbitrary calls or
10773/// blocks do not pass this gate.
10774fn cpp_reparsed_macro_attribute_member_sequence(
10775    children: &[Node<'_>],
10776    index: usize,
10777    source: &str,
10778) -> bool {
10779    let Some(prefix) = children.get(index).copied() else {
10780        return false;
10781    };
10782    let declaration = if prefix.kind() == "labeled_statement" {
10783        prefix
10784            .named_child(prefix.named_child_count().saturating_sub(1))
10785            .filter(|child| child.kind() == "declaration")
10786    } else {
10787        (prefix.kind() == "declaration").then_some(prefix)
10788    };
10789    let Some(declaration) = declaration else {
10790        return false;
10791    };
10792    if !declaration.has_error()
10793        || declaration
10794            .child_by_field_name("declarator")
10795            .and_then(extract_function_declarator)
10796            .and_then(cpp_function_declarator_name_node)
10797            .is_none()
10798    {
10799        return false;
10800    }
10801    let Some(attribute_statement) = children.get(index + 1).copied() else {
10802        return false;
10803    };
10804    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
10805        .then(|| attribute_statement.named_child(0))
10806        .flatten()
10807        .filter(|child| child.kind() == "call_expression")
10808    else {
10809        return false;
10810    };
10811    let Some(attribute_name) = attribute_call
10812        .child_by_field_name("function")
10813        .filter(|function| function.kind() == "identifier")
10814        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
10815    else {
10816        return false;
10817    };
10818    if !cpp_export_macro_token(&attribute_name) {
10819        return false;
10820    }
10821    let Some(body) = children.get(index + 2).copied() else {
10822        return false;
10823    };
10824    body.kind() == "compound_statement"
10825        && body.child(0).is_some_and(|open| open.kind() == "{")
10826        && body
10827            .child(body.child_count().saturating_sub(1))
10828            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
10829        && declaration.end_byte() <= attribute_statement.start_byte()
10830        && attribute_statement.end_byte() <= body.start_byte()
10831}
10832
10833fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
10834    let mut cursor = root.walk();
10835    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
10836    let mut saw_member = false;
10837    let mut index = 0;
10838    while index < children.len() {
10839        let child = children[index];
10840        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
10841            saw_member = true;
10842            index += 3;
10843            continue;
10844        }
10845        if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
10846            let Some(tree) = cpp_reparse_fragmented_class_body(
10847                source,
10848                fragmented.reparse_start,
10849                fragmented.reparse_end,
10850            ) else {
10851                return false;
10852            };
10853            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
10854                return false;
10855            }
10856            saw_member = true;
10857            index += 1;
10858            while index < children.len()
10859                && children[index].end_byte() <= fragmented.class_range.end_byte
10860            {
10861                index += 1;
10862            }
10863            continue;
10864        }
10865        match child.kind() {
10866            "comment" => {}
10867            "labeled_statement" => saw_member = true,
10868            "function_definition" => {
10869                if child.has_error()
10870                    && !cpp_reparsed_member_function_is_indexable(child, source)
10871                    && cpp_sentinel_macro_region(child, source).is_none()
10872                {
10873                    return false;
10874                }
10875                saw_member = true;
10876            }
10877            "ERROR"
10878                if (cpp_reparsed_member_error_is_indexable(child)
10879                    || cpp_reparsed_adjacent_copy_control_error(child, source))
10880                    && (child
10881                        .next_named_sibling()
10882                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
10883                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
10884            {
10885                saw_member = true;
10886            }
10887            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
10888                saw_member = true;
10889            }
10890            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
10891                saw_member = true;
10892            }
10893            "expression_statement"
10894                if cpp_is_stray_semicolon(child, source)
10895                    && child.prev_named_sibling().is_some_and(|error| {
10896                        cpp_reparsed_member_error_is_indexable(error)
10897                            || cpp_reparsed_adjacent_copy_control_error(error, source)
10898                    }) =>
10899            {
10900                saw_member = true;
10901            }
10902            "compound_statement"
10903                if cpp_reparsed_constructor_body_is_indexable(child, source)
10904                    || cpp_reparsed_attribute_requires_body(child, source) =>
10905            {
10906                saw_member = true;
10907            }
10908            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
10909            _ => return false,
10910        }
10911        index += 1;
10912    }
10913    saw_member
10914}
10915
10916/// Detect the malformed constructor shape that tree-sitter exposes as an
10917/// access-label statement followed by initializer-looking declarations. The
10918/// declarations are not class members: visiting their `location(loc)` and
10919/// `string(s)` function declarators would publish synthetic functions. The
10920/// export-class fallback keeps the original sibling nodes and therefore avoids
10921/// this parser artifact. The returned range identifies the real constructor
10922/// header, which can be reparsed independently as a structured declarator.
10923fn cpp_reparsed_synthetic_initializer_constructor_range(
10924    root: Node<'_>,
10925    class_name: &str,
10926    source: &str,
10927    constructor_end: usize,
10928) -> Option<std::ops::Range<usize>> {
10929    let mut stack = {
10930        let mut cursor = root.walk();
10931        root.named_children(&mut cursor).collect::<Vec<_>>()
10932    };
10933    while let Some(current) = stack.pop() {
10934        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
10935            current,
10936            class_name,
10937            source,
10938            constructor_end,
10939        ) {
10940            return Some(range);
10941        }
10942        if current.kind() == "ERROR" {
10943            let mut cursor = current.walk();
10944            stack.extend(current.named_children(&mut cursor));
10945        }
10946    }
10947    None
10948}
10949
10950fn cpp_reparsed_synthetic_initializer_constructor(
10951    node: Node<'_>,
10952    class_name: &str,
10953    source: &str,
10954    constructor_end: usize,
10955) -> Option<std::ops::Range<usize>> {
10956    if node.kind() != "labeled_statement" {
10957        return None;
10958    }
10959    let mut cursor = node.walk();
10960    let named = node
10961        .named_children(&mut cursor)
10962        .filter(|child| child.kind() != "comment")
10963        .collect::<Vec<_>>();
10964    let label = named.first()?;
10965    if label.kind() != "statement_identifier"
10966        || !matches!(
10967            node_text(*label, source).trim(),
10968            "public" | "private" | "protected"
10969        )
10970    {
10971        return None;
10972    }
10973    let call_error_index = named.iter().position(|child| {
10974        if child.kind() != "ERROR" {
10975            return false;
10976        }
10977        let mut stack = vec![*child];
10978        while let Some(current) = stack.pop() {
10979            if current.kind() == "call_expression"
10980                && current
10981                    .child_by_field_name("function")
10982                    .is_some_and(|function| {
10983                        function.kind() == "identifier"
10984                            && node_text(function, source).trim() == class_name
10985                    })
10986            {
10987                return true;
10988            }
10989            let mut cursor = current.walk();
10990            stack.extend(current.named_children(&mut cursor));
10991        }
10992        false
10993    })?;
10994    let constructor_call = {
10995        let mut stack = vec![named[call_error_index]];
10996        let mut found = None;
10997        while let Some(current) = stack.pop() {
10998            if current.kind() == "call_expression"
10999                && current
11000                    .child_by_field_name("function")
11001                    .is_some_and(|function| {
11002                        function.kind() == "identifier"
11003                            && node_text(function, source).trim() == class_name
11004                    })
11005            {
11006                found = Some(current);
11007                break;
11008            }
11009            let mut cursor = current.walk();
11010            stack.extend(current.named_children(&mut cursor));
11011        }
11012        found
11013    };
11014    let constructor_call = constructor_call?;
11015    named.iter().skip(call_error_index + 1).find(|child| {
11016        child.kind() == "declaration" && child.has_error() && {
11017            let mut cursor = child.walk();
11018            child.named_children(&mut cursor).any(|declarator| {
11019                declarator.kind() == "init_declarator"
11020                    && declarator
11021                        .child_by_field_name("declarator")
11022                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
11023                    && declarator
11024                        .child_by_field_name("value")
11025                        .is_some_and(|value| value.kind() == "initializer_list")
11026            })
11027        }
11028    })?;
11029    Some(constructor_call.start_byte()..constructor_end)
11030}
11031
11032fn cpp_reparsed_exact_constructor_declarator<'tree>(
11033    root: Node<'tree>,
11034    start: usize,
11035    class_name: &str,
11036    source: &str,
11037) -> Option<Node<'tree>> {
11038    let mut candidate = None;
11039    let mut stack = vec![root];
11040    while let Some(current) = stack.pop() {
11041        if current.kind() == "function_declarator"
11042            && current.start_byte() == start
11043            && cpp_function_declarator_name_node(current)
11044                .is_some_and(|name| node_text(name, source).trim() == class_name)
11045        {
11046            if candidate.is_some() {
11047                return None;
11048            }
11049            candidate = Some(current);
11050            continue;
11051        }
11052        let mut cursor = current.walk();
11053        stack.extend(current.named_children(&mut cursor));
11054    }
11055    candidate
11056}
11057
11058fn cpp_is_indexable_item_kind(kind: &str) -> bool {
11059    matches!(
11060        kind,
11061        "namespace_definition"
11062            | "class_specifier"
11063            | "struct_specifier"
11064            | "union_specifier"
11065            | "enum_specifier"
11066            | "function_definition"
11067            | "template_declaration"
11068            | "declaration"
11069            | "field_declaration"
11070            | "alias_declaration"
11071            | "static_assert_declaration"
11072            | "type_definition"
11073            | "using_declaration"
11074            | "linkage_specification"
11075            | "preproc_def"
11076            | "preproc_function_def"
11077            | "preproc_include"
11078            | "preproc_if"
11079            | "preproc_ifdef"
11080            | "preproc_call"
11081    )
11082}
11083
11084#[cfg(test)]
11085mod tests {
11086    use super::*;
11087    use crate::adapter::parse_cpp_file;
11088    use brokk_bifrost_core::analyzer::parsed_file::{
11089        finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
11090    };
11091    use std::fmt::Write;
11092
11093    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
11094        let mut parser = tree_sitter::Parser::new();
11095        parser
11096            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11097            .unwrap();
11098        let tree = parser.parse(source, None).unwrap();
11099        let file = ProjectFile::new(std::env::temp_dir(), name);
11100        parse_cpp_file(&file, source, &tree)
11101    }
11102
11103    #[test]
11104    fn identifies_export_macro_class_base_displaced_into_declarator() {
11105        let source = r#"#define PROJECT_API_
11106namespace project {
11107namespace internal {
11108template <typename T>
11109class Base {};
11110}
11111template <typename T>
11112class Wrapper;
11113template <>
11114class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
11115}
11116"#;
11117        let mut parser = tree_sitter::Parser::new();
11118        parser
11119            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11120            .unwrap();
11121        let tree = parser.parse(source, None).unwrap();
11122        let start = source.find("internal::Base<int>").expect("base");
11123        let mut base = tree
11124            .root_node()
11125            .descendant_for_byte_range(start, start + 8)
11126            .expect("base syntax");
11127        while base.kind() != "qualified_identifier" {
11128            base = base.parent().expect("qualified base ancestor");
11129        }
11130        assert!(
11131            is_recovered_exported_class_base_type_node(base, source),
11132            "{}",
11133            tree.root_node().to_sexp()
11134        );
11135    }
11136
11137    #[test]
11138    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
11139        let source = r#"namespace control {
11140template <typename T>
11141class AnySpan;
11142template <typename T>
11143class ABSL_ATTRIBUTE_VIEW AnySpan {
11144 public:
11145  int begin() const;
11146};
11147}
11148
11149namespace absl {
11150ABSL_NAMESPACE_BEGIN
11151template <typename T>
11152class ABSL_ATTRIBUTE_VIEW Span {
11153 public:
11154  int begin() const;
11155  int back() const;
11156};
11157
11158int begin();
11159int back();
11160}
11161"#;
11162        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
11163        let declarations = parsed.declarations();
11164        assert!(
11165            declarations
11166                .iter()
11167                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
11168        );
11169        for method in ["begin", "back"] {
11170            assert!(declarations.iter().any(|unit| {
11171                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
11172            }));
11173            assert!(
11174                declarations.iter().any(|unit| {
11175                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
11176                })
11177            );
11178        }
11179        assert!(
11180            declarations
11181                .iter()
11182                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
11183        );
11184        assert!(
11185            declarations
11186                .iter()
11187                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
11188        );
11189        assert!(
11190            declarations
11191                .iter()
11192                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
11193        );
11194    }
11195
11196    #[test]
11197    fn explicit_global_member_definition_has_canonical_package_boundary() {
11198        let source = r#"
11199namespace arangodb::aql {
11200class ExecutionPlan {
11201 public:
11202  template<class... Args> Node* createNode(Args&&... args);
11203};
11204}
11205
11206template<class... Args>
11207Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
11208"#;
11209        let parsed = parse_cpp_declarations(source, "global-member.cpp");
11210
11211        assert!(parsed.declarations().iter().any(|unit| {
11212            unit.is_function()
11213                && unit.package_name() == "arangodb::aql"
11214                && unit.short_name() == "ExecutionPlan.createNode"
11215                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
11216        }));
11217    }
11218
11219    #[test]
11220    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
11221        let source = r#"
11222#ifndef TINYXML2_INCLUDED
11223#define TINYXML2_INCLUDED
11224namespace tinyxml2 {
11225class TINYXML2_LIB XMLUtil {
11226 public:
11227  static const char* SkipWhiteSpace(const char* p) {
11228    while (*p) {
11229      if (*p == ' ') {
11230        ++p;
11231      }
11232    }
11233    return p;
11234  }
11235  static bool StringEqual(const char* p, const char* q) {
11236    return p == q;
11237  }
11238  class TINYXML2_LIB Helper {
11239   public:
11240    void Touch();
11241  };
11242  static void ToStr(int value, char* buffer);
11243 private:
11244  static const char* writeBoolTrue;
11245};
11246
11247class TINYXML2_LIB XMLNode {
11248 public:
11249  virtual XMLNode* ShallowClone() const = 0;
11250  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
11251};
11252}
11253#endif
11254"#;
11255        let mut parser = tree_sitter::Parser::new();
11256        parser
11257            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11258            .unwrap();
11259        let tree = parser.parse(source, None).unwrap();
11260        let mut boundary_found = false;
11261        walk_named_tree_preorder(tree.root_node(), true, |node| {
11262            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
11263                && name == "XMLUtil"
11264            {
11265                boundary_found = fragmented_export_sibling_class_boundary(node, source)
11266                    .and_then(|boundary| {
11267                        recover_exported_class_function_definition(boundary, source)
11268                    })
11269                    .is_some_and(|(_, name, _)| name == "XMLNode");
11270            }
11271            WalkControl::Continue
11272        });
11273        assert!(
11274            boundary_found,
11275            "fixture must exercise the recovered sibling boundary"
11276        );
11277
11278        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
11279        assert!(
11280            parsed
11281                .declarations()
11282                .iter()
11283                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
11284            "{:#?}",
11285            parsed.declarations()
11286        );
11287        assert!(
11288            parsed
11289                .declarations()
11290                .iter()
11291                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
11292            "{:#?}",
11293            parsed.declarations()
11294        );
11295        assert!(parsed.declarations().iter().any(|unit| {
11296            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
11297        }));
11298        assert!(
11299            parsed
11300                .declarations()
11301                .iter()
11302                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
11303        );
11304        assert!(
11305            parsed
11306                .declarations()
11307                .iter()
11308                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
11309        );
11310    }
11311
11312    #[test]
11313    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
11314        // Clang's diagnostic suite intentionally contains this ill-formed
11315        // spelling. The analyzer must retain the parser's explicit-global AST
11316        // boundary instead of constructing `cwg311::::cwg311::X`.
11317        let parsed = parse_cpp_declarations(
11318            r#"
11319namespace cwg311 {
11320namespace X { namespace Y {} }
11321namespace ::cwg311::X {}
11322}
11323"#,
11324            "explicit-global-namespace.cpp",
11325        );
11326
11327        assert!(parsed.declarations().iter().any(|unit| {
11328            unit.kind() == CodeUnitType::Module
11329                && unit.short_name() == "cwg311::X"
11330                && unit.fq_name() == "cwg311::X"
11331        }));
11332        assert!(
11333            parsed
11334                .declarations()
11335                .iter()
11336                .all(|unit| !unit.short_name().contains("::::")),
11337            "recovered namespace names must not retain empty scope components: {:#?}",
11338            parsed.declarations()
11339        );
11340    }
11341
11342    #[test]
11343    fn repeated_scope_separator_does_not_create_empty_function_owner() {
11344        let scope = ScopeInfo {
11345            package_name: "X".to_string(),
11346            module: None,
11347            class_unit: None,
11348            template_signature: None,
11349            template_metadata: None,
11350            declarations_are_fields: false,
11351            recovered_specialization_member_scope: false,
11352            visible_using_namespaces: Vec::new(),
11353        };
11354
11355        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
11356
11357        assert_eq!(owner, None);
11358        assert_eq!(name, "doit");
11359        assert_eq!(package, "X");
11360    }
11361
11362    #[test]
11363    fn trailing_decltype_expression_is_not_a_function_declarator() {
11364        let source = r#"
11365namespace boost { namespace detail {
11366#if ! defined(BOOST_NO_SFINAE_EXPR) && \
11367    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
11368    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
11369#define BOOST_THREAD_PROVIDES_INVOKE
11370#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
11371template <class Fp, class A0, class ...Args>
11372inline auto
11373invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
11374       BOOST_THREAD_RV_REF(Args) ...args)
11375    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
11376{
11377    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
11378}
11379#endif
11380#endif
11381}}
11382"#;
11383        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
11384
11385        assert!(
11386            parsed
11387                .declarations()
11388                .iter()
11389                .all(|unit| unit.short_name() != ".*f")
11390        );
11391    }
11392
11393    fn find_class_named<'tree>(
11394        root: Node<'tree>,
11395        source: &str,
11396        expected_name: &str,
11397    ) -> Option<Node<'tree>> {
11398        let mut stack = vec![root];
11399        while let Some(node) = stack.pop() {
11400            if node.kind() == "class_specifier"
11401                && node
11402                    .child_by_field_name("name")
11403                    .is_some_and(|name| node_text(name, source) == expected_name)
11404            {
11405                return Some(node);
11406            }
11407            let mut cursor = node.walk();
11408            stack.extend(node.named_children(&mut cursor));
11409        }
11410        None
11411    }
11412
11413    #[test]
11414    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
11415        let source = r#"EXPORT void definition(struct Value value) {}
11416EXPORT void prototype(struct Value value);
11417"#;
11418        let mut parser = tree_sitter::Parser::new();
11419        parser
11420            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11421            .unwrap();
11422        let tree = parser.parse(source, None).unwrap();
11423        let root = tree.root_node();
11424        let mut cursor = root.walk();
11425        let callables = root
11426            .named_children(&mut cursor)
11427            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
11428            .collect::<Vec<_>>();
11429
11430        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
11431        for callable in callables {
11432            assert!(callable.has_error(), "fixture must exercise error recovery");
11433            assert!(
11434                cpp_sentinel_macro_parts(callable, source).is_none(),
11435                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
11436            );
11437        }
11438    }
11439
11440    #[test]
11441    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
11442        let source = r#"namespace absl {
11443ABSL_NAMESPACE_BEGIN
11444// Generate a floating-point variate conforming to a Beta distribution:
11445template <typename RealType = double>
11446class beta_distribution {
11447 public:
11448  using result_type = RealType;
11449
11450
11451  beta_distribution() : beta_distribution(1) {}
11452
11453  explicit beta_distribution(result_type alpha, result_type beta = 1)
11454      : param_(alpha, beta) {}
11455
11456  explicit beta_distribution(const param_type& p) : param_(p) {}
11457
11458  void reset() {}
11459
11460  // Generating functions
11461  template <typename URBG>
11462  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
11463    return (*this)(g, param_);
11464  }
11465
11466};
11467ABSL_NAMESPACE_END
11468}  // namespace absl
11469"#;
11470        let mut parser = tree_sitter::Parser::new();
11471        parser
11472            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11473            .unwrap();
11474        let tree = parser.parse(source, None).unwrap();
11475        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
11476        let body = namespace
11477            .child_by_field_name("body")
11478            .expect("fixture namespace body");
11479        let sentinel = body.named_child(0).expect("sentinel envelope");
11480        let callable = sentinel
11481            .child_by_field_name("declarator")
11482            .and_then(extract_function_declarator)
11483            .and_then(cpp_function_declarator_name_node)
11484            .expect("preserved callable name");
11485
11486        assert_eq!(sentinel.kind(), "function_definition");
11487        assert_eq!(callable.kind(), "operator_name");
11488        assert!(
11489            cpp_sentinel_macro_parts(sentinel, source).is_some(),
11490            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
11491        );
11492    }
11493
11494    #[test]
11495    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
11496        let source = r#"namespace absl {
11497ABSL_NAMESPACE_BEGIN
11498// absl::discrete_distribution
11499//
11500// A discrete distribution produces random integers i, where 0 <= i < n
11501template <typename IntType = int>
11502class discrete_distribution {
11503 public:
11504  using result_type = IntType;
11505  class param_type {
11506   public:
11507    param_type() { init(); }
11508    template <typename InputIterator>
11509    explicit param_type(InputIterator begin, InputIterator end)
11510        : p_(begin, end) {
11511      init();
11512    }
11513  };
11514  discrete_distribution() : param_() {}
11515  explicit discrete_distribution(const param_type& p) : param_(p) {}
11516};
11517ABSL_NAMESPACE_END
11518}  // namespace absl
11519"#;
11520        let mut parser = tree_sitter::Parser::new();
11521        parser
11522            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11523            .unwrap();
11524        let tree = parser.parse(source, None).unwrap();
11525        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
11526        let body = namespace
11527            .child_by_field_name("body")
11528            .expect("fixture namespace body");
11529        let sentinel = body.named_child(0).expect("sentinel envelope");
11530        let callable = sentinel
11531            .child_by_field_name("declarator")
11532            .and_then(extract_function_declarator)
11533            .and_then(cpp_function_declarator_name_node)
11534            .expect("preserved callable name");
11535
11536        assert_eq!(sentinel.kind(), "function_definition");
11537        assert_eq!(callable.kind(), "identifier");
11538        assert!(
11539            cpp_sentinel_macro_parts(sentinel, source).is_some(),
11540            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
11541        );
11542    }
11543
11544    #[test]
11545    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
11546        let source = r#"
11547#define CPPCHECKLIB
11548class Library {
11549    struct Container {
11550        CPPCHECKLIB static std::string toString(Yield yield);
11551        CPPCHECKLIB static std::string toString(Action action);
11552    };
11553};
11554"#;
11555        let mut parser = tree_sitter::Parser::new();
11556        parser
11557            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11558            .unwrap();
11559        let tree = parser.parse(source, None).unwrap();
11560        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
11561        let parsed = parse_cpp_file(&file, source, &tree);
11562        assert!(
11563            parsed
11564                .declarations()
11565                .iter()
11566                .all(|unit| unit.fq_name() != "Library$Container.std"),
11567            "the qualified return-type namespace must not become a field: {:#?}",
11568            parsed.declarations()
11569        );
11570        for expected in ["(Yield)", "(Action)"] {
11571            assert!(
11572                parsed.declarations().iter().any(|unit| {
11573                    unit.is_function()
11574                        && unit.fq_name() == "Library$Container.toString"
11575                        && unit.signature() == Some(expected)
11576                }),
11577                "recovered toString overload {expected} is missing: {:#?}",
11578                parsed.declarations()
11579            );
11580        }
11581    }
11582
11583    #[test]
11584    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
11585        let source = r#"
11586#define SIMPLECPP_LIB
11587namespace simplecpp {
11588using TokenString = std::string;
11589struct Location { int line{}; };
11590class SIMPLECPP_LIB Token {
11591  TokenString prefix;
11592  void prefix_method() {}
11593 public:
11594  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
11595      whitespaceahead(wsahead), location(loc), string(s)
11596      // The comment must not hide the constructor body from recovery.
11597      {
11598      flags();
11599  }
11600  TokenString string;
11601  bool whitespaceahead;
11602  Location location;
11603  Token *previous{};
11604 private:
11605  void flags() {
11606      whitespaceahead = true;
11607  }
11608};
11609}
11610"#;
11611        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
11612
11613        let location_fields = parsed
11614            .declarations()
11615            .iter()
11616            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
11617            .collect::<Vec<_>>();
11618        assert_eq!(
11619            location_fields.len(),
11620            1,
11621            "location should have one class-owned declaration: {:#?}",
11622            parsed.declarations()
11623        );
11624        assert!(
11625            location_fields[0].is_field(),
11626            "location has wrong kind: {:#?}",
11627            parsed.declarations()
11628        );
11629        assert!(
11630            parsed.declarations().iter().all(|unit| {
11631                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
11632            })
11633        );
11634        assert!(
11635            parsed.declarations().iter().all(|unit| {
11636                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
11637            })
11638        );
11639        assert!(
11640            parsed
11641                .declarations()
11642                .iter()
11643                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
11644        );
11645        assert!(
11646            parsed
11647                .declarations()
11648                .iter()
11649                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
11650            "the recovered class must retain its constructor: {:#?}",
11651            parsed.declarations()
11652        );
11653        assert!(
11654            parsed
11655                .declarations()
11656                .iter()
11657                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
11658        );
11659        assert!(parsed.declarations().iter().any(|unit| {
11660            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
11661        }));
11662        let constructor = parsed
11663            .declarations()
11664            .iter()
11665            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
11666            .expect("recovered constructor");
11667        let constructor_start = source.find("Token(const").expect("constructor start");
11668        let constructor_end = source
11669            .get(
11670                ..source
11671                    .find("  TokenString string;")
11672                    .expect("constructor end"),
11673            )
11674            .expect("constructor slice")
11675            .trim_end()
11676            .len();
11677        assert!(
11678            parsed
11679                .navigation_ranges
11680                .get(constructor)
11681                .is_some_and(|ranges| {
11682                    ranges.iter().any(|range| {
11683                        range.start_byte == constructor_start && range.end_byte == constructor_end
11684                    })
11685                }),
11686            "constructor navigation must span the full body: {:#?}",
11687            parsed.navigation_ranges
11688        );
11689        assert_eq!(
11690            parsed
11691                .signature_metadata
11692                .get(constructor)
11693                .and_then(|metadata| metadata.first())
11694                .and_then(SignatureMetadata::callable_linkage),
11695            Some(CallableLinkage::External)
11696        );
11697        let token_class = parsed
11698            .declarations()
11699            .iter()
11700            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
11701            .expect("recovered Token class");
11702        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
11703        assert!(
11704            parsed
11705                .navigation_ranges
11706                .get(token_class)
11707                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
11708            "class navigation must include the terminating semicolon: {:#?}",
11709            parsed.navigation_ranges
11710        );
11711    }
11712
11713    #[test]
11714    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
11715        let source = r#"
11716#define SIMPLECPP_LIB
11717namespace simplecpp {
11718using TokenString = std::string;
11719class Macro;
11720struct Location {
11721  unsigned int fileIndex{};
11722  unsigned int line{};
11723  unsigned int col{};
11724};
11725struct Output {
11726  int type;
11727};
11728class SIMPLECPP_LIB Token {
11729 public:
11730  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
11731      whitespaceahead(wsahead), location(loc), string(s) {
11732      flags();
11733  }
11734  Token(const Token &tok) :
11735      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
11736      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
11737      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
11738  Token &operator=(const Token &tok) = delete;
11739  const TokenString& str() const { return string; }
11740  void setstr(const std::string &s) { string = s; flags(); }
11741  bool isOneOf(const char ops[]) const;
11742  TokenString macro;
11743  char op;
11744  bool comment;
11745  bool name;
11746  bool number;
11747  bool whitespaceahead;
11748  Location location;
11749  Token *previous{};
11750  Token *next{};
11751 private:
11752  void flags() {
11753      name = !string.empty();
11754      comment = false;
11755      number = false;
11756      op = 0;
11757  }
11758  TokenString string;
11759};
11760}
11761struct Following {
11762  int type;
11763};
11764class SIMPLECPP_LIB Later {
11765 public:
11766  Later(int value) : value(value) {}
11767  int value;
11768};
11769"#;
11770        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
11771        assert!(
11772            parsed
11773                .declarations()
11774                .iter()
11775                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
11776        );
11777        assert!(
11778            !parsed
11779                .declarations()
11780                .iter()
11781                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
11782        );
11783        assert!(
11784            parsed
11785                .declarations()
11786                .iter()
11787                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
11788        );
11789        assert!(
11790            !parsed
11791                .declarations()
11792                .iter()
11793                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
11794        );
11795        assert!(
11796            parsed
11797                .declarations()
11798                .iter()
11799                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
11800        );
11801        assert!(
11802            parsed
11803                .declarations()
11804                .iter()
11805                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
11806        );
11807        assert!(
11808            parsed
11809                .declarations()
11810                .iter()
11811                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
11812        );
11813        assert!(
11814            parsed
11815                .declarations()
11816                .iter()
11817                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
11818        );
11819        assert!(
11820            parsed
11821                .declarations()
11822                .iter()
11823                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
11824        );
11825        assert!(
11826            parsed
11827                .declarations()
11828                .iter()
11829                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
11830        );
11831        assert!(parsed.declarations().iter().all(|unit| {
11832            !matches!(
11833                unit.fq_name().as_str(),
11834                "simplecpp.Token.Following" | "simplecpp.Token.Later"
11835            )
11836        }));
11837        assert!(
11838            !parsed
11839                .declarations()
11840                .iter()
11841                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
11842            "the following struct must remain outside the recovered Token class"
11843        );
11844    }
11845
11846    #[test]
11847    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
11848        let source = r#"
11849#define SIMPLECPP_LIB
11850namespace {
11851namespace simplecpp {
11852using TokenString = std::string;
11853struct Location { int line{}; };
11854class SIMPLECPP_LIB HiddenToken {
11855 public:
11856  HiddenToken(const TokenString &s, const Location &loc) :
11857      location(loc), string(s) {
11858      flags();
11859  }
11860  TokenString string;
11861  Location location;
11862  HiddenToken *previous{};
11863 private:
11864  void flags() {}
11865};
11866}
11867}
11868"#;
11869        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
11870        let constructor = parsed
11871            .declarations()
11872            .iter()
11873            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
11874            .expect("recovered anonymous-namespace constructor");
11875        assert_eq!(
11876            parsed
11877                .signature_metadata
11878                .get(constructor)
11879                .and_then(|metadata| metadata.first())
11880                .and_then(SignatureMetadata::callable_linkage),
11881            Some(CallableLinkage::Internal)
11882        );
11883    }
11884
11885    #[test]
11886    fn macro_qualified_static_field_keeps_real_declarator() {
11887        let source = r#"#define JSON_INLINE_VARIABLE
11888struct Reader {
11889static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
11890static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
11891static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
11892};"#;
11893        let mut parser = tree_sitter::Parser::new();
11894        parser
11895            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11896            .unwrap();
11897        let tree = parser.parse(source, None).unwrap();
11898        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
11899        let parsed = parse_cpp_file(&file, source, &tree);
11900        for expected in [
11901            "Reader.npos",
11902            "Reader.other",
11903            "Reader.pointer",
11904            "Reader.reference",
11905        ] {
11906            assert!(
11907                parsed
11908                    .declarations()
11909                    .iter()
11910                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
11911                "real macro-decorated field {expected} is missing: {:#?}",
11912                parsed.declarations()
11913            );
11914        }
11915        assert!(
11916            parsed
11917                .declarations()
11918                .iter()
11919                .all(|unit| unit.fq_name() != "Reader.std"),
11920            "qualified type prefix became a pseudo-field: {:#?}",
11921            parsed.declarations()
11922        );
11923        let root = tree.root_node();
11924        let mut stack = vec![root];
11925        let mut signatures = Vec::new();
11926        while let Some(current) = stack.pop() {
11927            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
11928            {
11929                signatures.extend(
11930                    declarators
11931                        .into_iter()
11932                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
11933                );
11934            }
11935            let mut cursor = current.walk();
11936            stack.extend(current.named_children(&mut cursor));
11937        }
11938        signatures.sort();
11939        assert_eq!(
11940            signatures,
11941            [
11942                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
11943                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
11944                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
11945                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
11946            ]
11947        );
11948    }
11949
11950    fn member_function_linkage(source: &str) -> CallableLinkage {
11951        let mut parser = tree_sitter::Parser::new();
11952        parser
11953            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11954            .unwrap();
11955        let tree = parser.parse(source, None).unwrap();
11956        let mut stack = vec![tree.root_node()];
11957        while let Some(node) = stack.pop() {
11958            if node.kind() == "function_definition" {
11959                let mut current = node.parent();
11960                while let Some(parent) = current {
11961                    if matches!(
11962                        parent.kind(),
11963                        "class_specifier" | "struct_specifier" | "union_specifier"
11964                    ) {
11965                        return cpp_callable_linkage(node, source);
11966                    }
11967                    current = parent.parent();
11968                }
11969            }
11970            let mut cursor = node.walk();
11971            stack.extend(node.named_children(&mut cursor));
11972        }
11973        panic!("fixture has no member function definition");
11974    }
11975
11976    #[test]
11977    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
11978        assert_eq!(
11979            member_function_linkage("struct Named { int method() { return 1; } };"),
11980            CallableLinkage::External
11981        );
11982        assert_eq!(
11983            member_function_linkage(
11984                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
11985            ),
11986            CallableLinkage::Internal
11987        );
11988        assert_eq!(
11989            member_function_linkage("struct { int method() { return 1; } } instance;"),
11990            CallableLinkage::Internal
11991        );
11992        assert_eq!(
11993            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
11994            CallableLinkage::Internal
11995        );
11996    }
11997
11998    #[test]
11999    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
12000        let source = r#"
12001#ifndef PROTON_VALUE_HPP
12002#define PROTON_VALUE_HPP
12003namespace proton {
12004namespace internal {
12005class value_base {
12006  protected:
12007    internal::data& data();
12008    internal::data data_;
12009  friend class codec::encoder;
12010  friend class codec::decoder;
12011};
12012}
12013class value : public internal::value_base, private internal::comparable<value> {
12014  private:
12015    template<class T, class U=void> struct assignable :
12016        public std::enable_if<codec::is_encodable<T>::value, U> {};
12017    template<class U> struct assignable<value, U> {};
12018  public:
12019    PN_CPP_EXTERN value();
12020    PN_CPP_EXTERN value(const value&);
12021    PN_CPP_EXTERN value& operator=(const value&);
12022    PN_CPP_EXTERN value(value&&);
12023    PN_CPP_EXTERN value& operator=(value&&);
12024    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
12025    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
12026        codec::encoder e(*this);
12027        e << x;
12028        return *this;
12029    }
12030    PN_CPP_EXTERN type_id type() const;
12031    PN_CPP_EXTERN bool empty() const;
12032    PN_CPP_EXTERN void clear();
12033    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
12034    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
12035  friend PN_CPP_EXTERN void swap(value&, value&);
12036  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
12037  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
12038  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
12039    value(pn_data_t* d);
12040    void reset(pn_data_t* d = 0);
12041};
12042}
12043#endif
12044"#;
12045        let mut parser = tree_sitter::Parser::new();
12046        parser
12047            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12048            .unwrap();
12049        let tree = parser.parse(source, None).unwrap();
12050        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
12051        let parsed = parse_cpp_file(&file, source, &tree);
12052        let macro_constructors = parsed
12053            .signature_metadata
12054            .iter()
12055            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
12056            .flat_map(|(_, metadata)| metadata)
12057            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
12058            .collect::<Vec<_>>();
12059
12060        assert_eq!(
12061            macro_constructors.len(),
12062            3,
12063            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
12064            parsed.declarations()
12065        );
12066        assert!(
12067            macro_constructors.iter().all(|metadata| {
12068                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
12069            }),
12070            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
12071        );
12072    }
12073
12074    #[test]
12075    fn recovered_export_class_typedef_uses_displaced_alias_name() {
12076        let source = r#"
12077namespace spi {
12078class Filter {
12079public:
12080    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
12081};
12082}
12083namespace filter {
12084class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
12085{
12086public:
12087    typedef spi::Filter BASE_CLASS;
12088    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
12089    BEGIN_LOG4CXX_CAST_MAP()
12090    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
12091    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
12092    END_LOG4CXX_CAST_MAP()
12093    FilterDecision decide() const;
12094};
12095}
12096"#;
12097        let mut parser = tree_sitter::Parser::new();
12098        parser
12099            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12100            .unwrap();
12101        let tree = parser.parse(source, None).unwrap();
12102        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
12103        let parsed = parse_cpp_file(&file, source, &tree);
12104        assert!(
12105            parsed.declarations().iter().any(|unit| {
12106                unit.is_class()
12107                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
12108                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
12109            }),
12110            "the displaced typedef alias must retain its declared name: {:#?}",
12111            parsed.declarations()
12112        );
12113        assert!(
12114            parsed
12115                .declarations()
12116                .iter()
12117                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
12118            "the qualified underlying type must not become a false nested alias: {:#?}",
12119            parsed.declarations()
12120        );
12121    }
12122
12123    #[test]
12124    fn exported_single_base_recovery_uses_displaced_class_name() {
12125        let source = r#"
12126class CORE_EXPORT QgsPoint : public AbstractGeometry
12127{
12128    Q_GADGET
12129
12130    Q_PROPERTY( double x READ x WRITE setX )
12131    Q_PROPERTY( double y READ y WRITE setY )
12132    Q_PROPERTY( double z READ z WRITE setZ )
12133    Q_PROPERTY( double m READ m WRITE setM )
12134
12135  public:
12136#ifndef SIP_RUN
12137    QgsPoint(
12138      double x = std::numeric_limits<double>::quiet_NaN(),
12139      double y = std::numeric_limits<double>::quiet_NaN(),
12140      double z = std::numeric_limits<double>::quiet_NaN(),
12141      double m = std::numeric_limits<double>::quiet_NaN(),
12142      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
12143    );
12144#else
12145    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 )];
12146    % MethodCode
12147    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
12148    {
12149      int state;
12150      sipIsErr = 0;
12151      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
12152      if ( !sipIsErr )
12153      {
12154        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
12155      }
12156      sipReleaseType( p, sipType_QgsPointXY, state );
12157    }
12158    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
12159    {
12160      int state;
12161      sipIsErr = 0;
12162
12163      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
12164      if ( !sipIsErr )
12165      {
12166        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
12167      }
12168      sipReleaseType( p, sipType_QPointF, state );
12169    }
12170    else if (
12171      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
12172      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
12173      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
12174      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
12175    {
12176      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
12177      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
12178      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
12179      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
12180      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
12181      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
12182    }
12183    else // Invalid ctor arguments
12184    {
12185      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
12186      sipIsErr = 1;
12187    }
12188    % End
12189#endif
12190
12191    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
12192    explicit QgsPoint( QPointF p ) SIP_SKIP;
12193    explicit QgsPoint(
12194      Qgis::WkbType wkbType,
12195      double x = std::numeric_limits<double>::quiet_NaN(),
12196      double y = std::numeric_limits<double>::quiet_NaN(),
12197      double z = std::numeric_limits<double>::quiet_NaN(),
12198      double m = std::numeric_limits<double>::quiet_NaN()
12199    ) SIP_SKIP;
12200    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
12201    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
12202    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
12203#ifndef SIP_RUN
12204  private:
12205    bool fuzzyHelper(
12206      double epsilon,
12207      const AbstractGeometry &other,
12208      bool is3DFlag,
12209      bool isMeasureFlag
12210    ) const
12211    {
12212      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
12213    }
12214#endif
12215};
12216class Ordinary : public Base { public: Ordinary(); };
12217class API_EXPORT Plain { public: Plain(); };
12218class API_EXPORT : public Base {};
12219class
12220PN_CPP_CLASS_EXTERN Sender : public Link {
12221    Sender();
12222};
12223class thread_ctx_t {};
12224class ctx_t ZMQ_FINAL : public thread_ctx_t {
12225    bool start();
12226};
12227"#;
12228        let mut parser = tree_sitter::Parser::new();
12229        parser
12230            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12231            .unwrap();
12232        let tree = parser.parse(source, None).unwrap();
12233        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
12234        let parsed = parse_cpp_file(&file, source, &tree);
12235        let declarations = parsed.declarations();
12236
12237        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
12238            assert!(
12239                declarations
12240                    .iter()
12241                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
12242                "missing recovered class {expected}: {declarations:#?}"
12243            );
12244        }
12245        let qgs_point = declarations
12246            .iter()
12247            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
12248            .expect("recovered QgsPoint class");
12249        assert_eq!(
12250            parsed.raw_supertypes.get(qgs_point),
12251            Some(&vec!["AbstractGeometry".to_string()]),
12252            "single-base export recovery must retain its displaced base"
12253        );
12254        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
12255        assert!(
12256            parsed
12257                .navigation_ranges
12258                .get(qgs_point)
12259                .is_some_and(|ranges| {
12260                    !ranges.is_empty()
12261                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
12262                }),
12263            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
12264            parsed.navigation_ranges.get(qgs_point)
12265        );
12266        let sender = declarations
12267            .iter()
12268            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
12269            .expect("recovered Sender class");
12270        assert_eq!(
12271            parsed.raw_supertypes.get(sender),
12272            Some(&vec!["Link".to_string()]),
12273            "post-declarator export recovery must retain its displaced base"
12274        );
12275        let ctx = declarations
12276            .iter()
12277            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
12278            .expect("recovered ctx_t class");
12279        assert_eq!(
12280            parsed.raw_supertypes.get(ctx),
12281            Some(&vec!["thread_ctx_t".to_string()]),
12282            "postfix export-macro recovery must retain its displaced base"
12283        );
12284        assert!(
12285            declarations.iter().any(|unit| {
12286                unit.is_function()
12287                    && unit.fq_name() == "QgsPoint.QgsPoint"
12288                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
12289            }),
12290            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
12291        );
12292        assert!(
12293            declarations.iter().all(|unit| {
12294                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
12295            }),
12296            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
12297        );
12298    }
12299
12300    #[test]
12301    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
12302        let positive_source =
12303            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
12304        let mut parser = tree_sitter::Parser::new();
12305        parser
12306            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12307            .unwrap();
12308        let positive_tree = parser.parse(positive_source, None).unwrap();
12309        assert!(cpp_reparsed_members_are_indexable(
12310            positive_tree.root_node(),
12311            positive_source
12312        ));
12313
12314        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
12315        let negative_tree = parser.parse(negative_source, None).unwrap();
12316        assert!(!cpp_reparsed_members_are_indexable(
12317            negative_tree.root_node(),
12318            negative_source
12319        ));
12320    }
12321
12322    #[test]
12323    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
12324        let copy_control_source = r#"
12325public:
12326    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
12327    explicit Token(const Token* tok);
12328    ~Token();
12329    Token* astOperand1() { return nullptr; }
12330"#;
12331        let constraint_source = r#"
12332private:
12333    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
12334    static T *tokAtImpl(T *tok, int index) {
12335        return tok;
12336    }
12337
12338    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
12339    static T *linkAtImpl(T *tok, int index) {
12340        return tok;
12341    }
12342
12343public:
12344    int late() const { return 1; }
12345"#;
12346        let mut parser = tree_sitter::Parser::new();
12347        parser
12348            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12349            .unwrap();
12350        let copy_control_tree = parser
12351            .parse(copy_control_source, None)
12352            .expect("parse copy-control fixture");
12353        assert!(
12354            copy_control_tree.root_node().has_error(),
12355            "fixture must exercise adjacent copy-control recovery"
12356        );
12357        assert!(
12358            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
12359            "a complete late getter must remain recoverable after adjacent copy-control declarations"
12360        );
12361        let mut cursor = copy_control_tree.root_node().walk();
12362        assert!(
12363            copy_control_tree
12364                .root_node()
12365                .named_children(&mut cursor)
12366                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
12367            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
12368            copy_control_tree.root_node().to_sexp()
12369        );
12370        let constraint_tree = parser
12371            .parse(constraint_source, None)
12372            .expect("parse constraint-macro fixture");
12373        assert!(constraint_tree.root_node().has_error());
12374        assert!(
12375            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
12376            "complete constraint-macro members must not hide a later ordinary member"
12377        );
12378        let mut cursor = constraint_tree.root_node().walk();
12379        assert!(
12380            constraint_tree
12381                .root_node()
12382                .named_children(&mut cursor)
12383                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
12384                    child,
12385                    constraint_source
12386                )),
12387            "fixture must retain the split constraint-macro prefix/function geometry"
12388        );
12389    }
12390
12391    #[test]
12392    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
12393        let source = r#"
12394struct Analyzer {
12395    struct Action {
12396        Action() = default;
12397        Action(const Action&) = default;
12398        Action& operator=(const Action& rhs) & = default;
12399
12400        template<class T,
12401                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
12402                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
12403        // NOLINTNEXTLINE(google-explicit-constructor)
12404        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
12405        {}
12406
12407        enum : std::uint16_t { None = 0, Read = (1 << 0) };
12408        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
12409
12410    private:
12411        unsigned int mFlag{};
12412    };
12413
12414    enum class Direction : unsigned char { Forward, Reverse };
12415    virtual Action analyze(Direction d) const = 0;
12416};
12417"#;
12418        let mut parser = tree_sitter::Parser::new();
12419        parser
12420            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12421            .unwrap();
12422        let tree = parser.parse(source, None).unwrap();
12423        assert!(tree.root_node().has_error());
12424        let root = tree.root_node();
12425        let outer = root
12426            .named_children(&mut root.walk())
12427            .find(|child| child.kind() == "ERROR")
12428            .expect("fragmented Analyzer prefix");
12429        let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
12430            .expect("structured Analyzer fragment boundary");
12431        assert_eq!(outer_name, "Analyzer");
12432        let outer_tree = cpp_reparse_fragmented_class_body(
12433            source,
12434            outer_fragment.reparse_start,
12435            outer_fragment.reparse_end,
12436        )
12437        .expect("reparse Analyzer body");
12438        let outer_root = outer_tree.root_node();
12439        let action_prefix = outer_root
12440            .named_children(&mut outer_root.walk())
12441            .find(|child| child.kind() == "ERROR")
12442            .expect("fragmented Action prefix");
12443        let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
12444            .expect("structured Action fragment boundary");
12445        assert_eq!(action_name, "Action");
12446        let action_tree = cpp_reparse_fragmented_class_body(
12447            source,
12448            action_fragment.reparse_start,
12449            action_fragment.reparse_end,
12450        )
12451        .expect("reparse Action body");
12452        let action_root = action_tree.root_node();
12453        let macro_prefix = action_root
12454            .named_children(&mut action_root.walk())
12455            .find(|child| child.kind() == "ERROR")
12456            .expect("constraint macro prefix");
12457        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
12458            .expect("structured template macro prefix");
12459        let macro_companion =
12460            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
12461        assert!(
12462            cpp_reparsed_template_macro_constructor_companion_is_indexable(
12463                macro_companion,
12464                macro_parameter,
12465                source,
12466            ),
12467            "split constrained constructor must be admitted: {}",
12468            macro_companion.to_sexp()
12469        );
12470        assert!(
12471            cpp_reparsed_members_are_indexable(action_root, source),
12472            "complete Action body must pass the recovery gate: {}",
12473            action_tree.root_node().to_sexp()
12474        );
12475        assert!(
12476            cpp_reparsed_members_are_indexable(outer_root, source),
12477            "complete Analyzer body must pass the recovery gate: {}",
12478            outer_tree.root_node().to_sexp()
12479        );
12480        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
12481        let parsed = parse_cpp_file(&file, source, &tree);
12482        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
12483            assert!(
12484                parsed
12485                    .declarations()
12486                    .iter()
12487                    .any(|unit| unit.fq_name() == expected),
12488                "missing recovered declaration {expected}: {:#?}",
12489                parsed.declarations()
12490            );
12491        }
12492        assert!(
12493            parsed
12494                .declarations()
12495                .iter()
12496                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
12497            "nested members must not remain flattened: {:#?}",
12498            parsed.declarations()
12499        );
12500    }
12501
12502    #[test]
12503    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
12504        let source = r#"
12505raw_hash_set& operator=(raw_hash_set&& that) {
12506  return move_assign(
12507      std::move(that),
12508      typename AllocTraits::propagate_on_container_move_assignment());
12509}
12510
12511iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
12512  return {};
12513}
12514
12515void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
12516
12517iterator insert(const_iterator hint, value_type&& value)
12518    ABSL_ATTRIBUTE_LIFETIME_BOUND {
12519  return {};
12520}
12521
12522friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
12523  return left.size() == right.size();
12524}
12525
12526static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
12527  return static_cast<slot_type*>(buffer);
12528}
12529
12530protected:
12531// Included-range recovery can attach this comment to the template prefix.
12532template <class K>
12533void AssertOnFind([[maybe_unused]] const K& key) {
12534  Check(key);
12535}
12536"#;
12537        let mut parser = tree_sitter::Parser::new();
12538        parser
12539            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12540            .unwrap();
12541        let tree = parser.parse(source, None).unwrap();
12542        assert!(
12543            tree.root_node().has_error(),
12544            "the fixture must exercise tree-sitter's errorful member shapes"
12545        );
12546        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
12547
12548        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
12549        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
12550        assert!(!cpp_reparsed_members_are_indexable(
12551            incomplete_tree.root_node(),
12552            incomplete_source
12553        ));
12554
12555        let outside_error_source = "int foo() stray_attribute {}\n";
12556        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
12557        assert!(outside_error_tree.root_node().has_error());
12558        assert!(!cpp_reparsed_members_are_indexable(
12559            outside_error_tree.root_node(),
12560            outside_error_source
12561        ));
12562
12563        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
12564        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
12565        assert!(!cpp_reparsed_members_are_indexable(
12566            variable_initializer_tree.root_node(),
12567            variable_initializer_source
12568        ));
12569    }
12570
12571    #[test]
12572    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
12573        let positive_source = r#"
12574std::pair<iterator, bool> insert(init_type&& value)
12575    ABSL_ATTRIBUTE_LIFETIME_BOUND
12576#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
12577  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
12578#endif
12579{
12580  return emplace(std::move(value));
12581}
12582"#;
12583        let mut parser = tree_sitter::Parser::new();
12584        parser
12585            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12586            .unwrap();
12587        let positive_tree = parser.parse(positive_source, None).unwrap();
12588        assert!(
12589            positive_tree.root_node().has_error(),
12590            "the fixture must exercise the split attribute/requires shape"
12591        );
12592        assert!(cpp_reparsed_members_are_indexable(
12593            positive_tree.root_node(),
12594            positive_source
12595        ));
12596
12597        let template_return_source = r#"
12598pair<int> insert(init_type&& value)
12599    ABSL_ATTRIBUTE_LIFETIME_BOUND
12600#if LANGUAGE_LEVEL >= 202002L
12601  requires(!Predicate<init_type>::value)
12602#endif
12603// Attributes and the function body may be separated by comments.
12604{
12605  return {};
12606}
12607"#;
12608        let template_return_tree = parser.parse(template_return_source, None).unwrap();
12609        assert!(
12610            cpp_reparsed_members_are_indexable(
12611                template_return_tree.root_node(),
12612                template_return_source
12613            ),
12614            "template-return attribute/requires tree: {}",
12615            template_return_tree.root_node().to_sexp()
12616        );
12617
12618        let no_body_source = r#"
12619std::pair<iterator, bool> insert(init_type&& value)
12620    ABSL_ATTRIBUTE_LIFETIME_BOUND
12621#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
12622  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
12623#endif
12624+ 0;
12625"#;
12626        let no_body_tree = parser.parse(no_body_source, None).unwrap();
12627        assert!(!cpp_reparsed_members_are_indexable(
12628            no_body_tree.root_node(),
12629            no_body_source
12630        ));
12631
12632        let extra_payload_source = r#"
12633pair<int> insert(init_type&& value)
12634    ABSL_ATTRIBUTE_LIFETIME_BOUND
12635#if LANGUAGE_LEVEL >= 202002L
12636  int unrelated;
12637  requires(Predicate<init_type>::value)
12638#endif
12639{
12640  return {};
12641}
12642"#;
12643        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
12644        assert!(!cpp_reparsed_members_are_indexable(
12645            extra_payload_tree.root_node(),
12646            extra_payload_source
12647        ));
12648
12649        let variable_initializer_source = r#"
12650int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
12651#if LANGUAGE_LEVEL >= 202002L
12652  requires(true)
12653#endif
12654{
12655  bad;
12656}
12657"#;
12658        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
12659        assert!(!cpp_reparsed_members_are_indexable(
12660            variable_initializer_tree.root_node(),
12661            variable_initializer_source
12662        ));
12663    }
12664
12665    #[test]
12666    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
12667        let source = r#"namespace absl {
12668ABSL_NAMESPACE_BEGIN namespace container_internal {
12669
12670class raw_hash_set : public Base {
12671 public:
12672  using value_type = int;
12673
12674  template <class U,
12675            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
12676  void insert(U value) { (void)value; }
12677
12678  struct InsertSlot {
12679    raw_hash_set& s;
12680  };
12681};
12682
12683}
12684ABSL_NAMESPACE_END
12685}"#;
12686        let mut parser = tree_sitter::Parser::new();
12687        parser
12688            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12689            .unwrap();
12690        let tree = parser.parse(source, None).unwrap();
12691        let root = tree.root_node();
12692        let outer_namespace = root
12693            .named_children(&mut root.walk())
12694            .find(|child| child.kind() == "namespace_definition")
12695            .expect("outer absl namespace");
12696        let declaration_list = outer_namespace
12697            .child_by_field_name("body")
12698            .expect("outer namespace body");
12699        let sentinel_function = declaration_list
12700            .named_children(&mut declaration_list.walk())
12701            .find(|child| child.kind() == "function_definition")
12702            .expect("malformed namespace sentinel function");
12703        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source)
12704            .expect("structured nested namespace sentinel");
12705        let fragmented =
12706            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source)
12707                .expect("fragmented raw_hash_set class");
12708        assert_eq!(fragmented.class_node.kind(), "ERROR");
12709        assert_eq!(fragmented.name, "raw_hash_set");
12710        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
12711
12712        let outer_scope =
12713            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
12714        let mut outer_siblings = Vec::new();
12715        push_cpp_sentinel_sibling_classes(
12716            &mut outer_siblings,
12717            declaration_list,
12718            sentinel.function,
12719            &outer_scope,
12720            source,
12721        );
12722        let [outer_shadow] = outer_siblings.as_slice() else {
12723            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
12724        };
12725        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
12726        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
12727
12728        let field = "    raw_hash_set& s;";
12729        let start = source.find(field).expect("InsertSlot field") + 4;
12730        let node = root
12731            .descendant_for_byte_range(start, start + "raw_hash_set".len())
12732            .expect("raw_hash_set type node");
12733        let recovered = cpp_sentinel_recovered_classes(root, source);
12734        let [deep_class] = recovered.as_slice() else {
12735            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
12736        };
12737        assert_eq!(
12738            deep_class.namespace_scope_components,
12739            vec!["absl", "container_internal"]
12740        );
12741        assert_eq!(
12742            deep_class.scope_components,
12743            vec!["absl", "container_internal", "raw_hash_set"]
12744        );
12745        assert!(
12746            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
12747                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
12748        );
12749
12750        assert_eq!(
12751            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
12752            Some(vec![
12753                "absl".to_string(),
12754                "container_internal".to_string(),
12755                "raw_hash_set".to_string(),
12756                "InsertSlot".to_string(),
12757            ])
12758        );
12759
12760        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
12761        let parsed = parse_cpp_file(&file, source, &tree);
12762        let raw_hash_set = parsed
12763            .declarations()
12764            .iter()
12765            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
12766            .expect("recovered raw_hash_set class");
12767        assert_eq!(
12768            raw_hash_set.fq_name(),
12769            "absl::container_internal.raw_hash_set",
12770            "the recovered declaration must publish under the deeper sentinel namespace"
12771        );
12772        assert_eq!(
12773            parsed.raw_supertypes.get(raw_hash_set),
12774            Some(&vec!["Base".to_string()]),
12775            "the structured base clause on the fragmented ERROR prefix must survive publication"
12776        );
12777        assert!(
12778            parsed.materialization_records.iter().any(|record| matches!(
12779                record,
12780                MaterializationRecord::RecoveredDeclaration { recovery, unit }
12781                    if unit == raw_hash_set && *recovery == deep_class.class_range
12782            )),
12783            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
12784            parsed.materialization_records
12785        );
12786    }
12787
12788    #[test]
12789    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
12790        const DISTINCT_PER_KIND: usize = 64;
12791        let mut source = String::new();
12792        for index in 0..DISTINCT_PER_KIND {
12793            writeln!(source, "typedef int Alias{index};").unwrap();
12794        }
12795        writeln!(source, "typedef long Alias0;").unwrap();
12796        for index in 0..DISTINCT_PER_KIND {
12797            writeln!(source, "#define MACRO_{index} {index}").unwrap();
12798        }
12799        writeln!(source, "#define MACRO_0 duplicate").unwrap();
12800        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
12801
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(), "dedup.cpp");
12808
12809        start_declaration_identity_comparison_probe();
12810        let parsed = parse_cpp_file(&file, &source, &tree);
12811        let comparisons = finish_declaration_identity_comparison_probe();
12812
12813        assert_eq!(
12814            DISTINCT_PER_KIND + 1,
12815            parsed
12816                .declarations()
12817                .iter()
12818                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
12819                .count(),
12820            "every physical typedef alias declaration must be retained so \
12821             conditional branch guards stay available to the resolver"
12822        );
12823        assert_eq!(
12824            DISTINCT_PER_KIND,
12825            parsed
12826                .declarations()
12827                .iter()
12828                .filter(|unit| {
12829                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
12830                })
12831                .count(),
12832            "macros should retain semantic-identity deduplication"
12833        );
12834        assert_eq!(
12835            2,
12836            parsed
12837                .declarations()
12838                .iter()
12839                .filter(|unit| {
12840                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
12841                })
12842                .count(),
12843            "function overloads must remain distinct"
12844        );
12845
12846        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
12847        assert!(
12848            comparisons <= dedup_inputs * 4,
12849            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
12850        );
12851    }
12852
12853    #[test]
12854    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
12855        let source = r#"namespace absl {
12856ABSL_NAMESPACE_BEGIN namespace container_internal {
12857template <typename T>
12858class broken {
12859 public:
12860  using value_type = T;
12861  T operator->() const { return &operator*(); }
12862  using alias = value_type;
12863};
12864}
12865}
12866"#;
12867        let mut parser = tree_sitter::Parser::new();
12868        parser
12869            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12870            .unwrap();
12871        let tree = parser.parse(source, None).unwrap();
12872        let broken = find_class_named(tree.root_node(), source, "broken")
12873            .expect("the positive fixture must expose the broken class node");
12874        assert!(
12875            broken.has_error(),
12876            "the positive fixture must retain an internal parser error"
12877        );
12878        assert!(
12879            cpp_complete_class_body_close(broken).is_some(),
12880            "the positive fixture must expose a real class body close"
12881        );
12882        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12883        assert!(
12884            recovered.iter().any(|class| {
12885                class.scope_components == ["absl", "container_internal", "broken"]
12886            }),
12887            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
12888        );
12889    }
12890
12891    #[test]
12892    fn sentinel_recovery_keeps_members_after_nested_body_close() {
12893        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
12894NLOHMANN_BASIC_JSON_TPL_DECLARATION
12895class basic_json {
12896 private:
12897  union storage {
12898    int value;
12899  } data;
12900 public:
12901  using late_alias = int;
12902  late_alias value() const;
12903};
12904NLOHMANN_JSON_NAMESPACE_END
12905"#;
12906        let mut parser = tree_sitter::Parser::new();
12907        parser
12908            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12909            .unwrap();
12910        let tree = parser.parse(source, None).unwrap();
12911        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12912        let basic_json = recovered
12913            .iter()
12914            .find(|class| {
12915                class
12916                    .scope_components
12917                    .last()
12918                    .is_some_and(|name| name == "basic_json")
12919            })
12920            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
12921        let late_alias = source
12922            .find("late_alias value")
12923            .expect("late alias reference");
12924        assert!(
12925            basic_json.class_range.start_byte < late_alias
12926                && late_alias < basic_json.class_range.end_byte,
12927            "the recovered class range must include members after a nested close: {basic_json:#?}"
12928        );
12929    }
12930
12931    #[test]
12932    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
12933        let source = r#"namespace absl {
12934ABSL_NAMESPACE_BEGIN namespace container_internal {
12935template <typename T>
12936class broken {
12937 public:
12938  using value_type = T;
12939  T operator->() const { return &operator*(); }
12940}
12941}
12942"#;
12943        let mut parser = tree_sitter::Parser::new();
12944        parser
12945            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12946            .unwrap();
12947        let tree = parser.parse(source, None).unwrap();
12948        let broken = find_class_named(tree.root_node(), source, "broken")
12949            .expect("the negative fixture must expose the malformed class node");
12950        assert!(
12951            broken.has_error(),
12952            "the negative fixture must retain a parser error"
12953        );
12954        assert!(
12955            cpp_complete_class_body_close(broken).is_none(),
12956            "the malformed class must not expose a real body close"
12957        );
12958        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12959        assert!(
12960            recovered
12961                .iter()
12962                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
12963            "an incomplete class must not borrow the namespace close: {recovered:#?}"
12964        );
12965    }
12966
12967    #[test]
12968    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
12969        let source = r#"namespace absl {
12970ABSL_NAMESPACE_BEGIN namespace container_internal {
12971template <typename T>
12972struct broken {
12973  using value_type = T;
12974};
12975}
12976
12977#ifdef OWNER_DEF
12978template <typename T>
12979typename broken<T>::value_type broken<T>::method() {
12980  value_type value{};
12981  return value;
12982}
12983#endif
12984
12985namespace sibling {
12986template <typename T>
12987typename broken<T>::value_type broken<T>::other() {
12988  value_type value{};
12989  return value;
12990}
12991}
12992
12993ABSL_NAMESPACE_END
12994}
12995"#;
12996        let mut parser = tree_sitter::Parser::new();
12997        parser
12998            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12999            .unwrap();
13000        let tree = parser.parse(source, None).unwrap();
13001        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13002        let broken = recovered
13003            .iter()
13004            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
13005            .expect("the sentinel class must be recovered");
13006        let method_start = source
13007            .find("typename broken<T>::value_type broken<T>::method()")
13008            .expect("guarded sibling owner");
13009        let method_end = source[method_start..]
13010            .find("\n}")
13011            .map(|offset| method_start + offset + 2)
13012            .expect("guarded sibling owner close");
13013        assert!(
13014            broken
13015                .owner_ranges
13016                .iter()
13017                .any(|owner| owner.range.start_byte <= method_start
13018                    && method_end <= owner.range.end_byte),
13019            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
13020        );
13021        let sibling_start = source
13022            .find("typename broken<T>::value_type broken<T>::other()")
13023            .expect("nested namespace sibling owner");
13024        assert!(
13025            broken
13026                .owner_ranges
13027                .iter()
13028                .all(|owner| owner.range.start_byte > sibling_start
13029                    || owner.range.end_byte <= sibling_start),
13030            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
13031        );
13032    }
13033
13034    #[test]
13035    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
13036        let source = r#"#ifdef OUTER
13037namespace absl {
13038ABSL_NAMESPACE_BEGIN namespace container_internal {
13039template <typename T>
13040struct broken {
13041  using value_type = T;
13042};
13043}
13044}
13045
13046#ifdef OWNER_DEF
13047template <typename T>
13048typename broken<T>::value_type broken<T>::method() {
13049  value_type value{};
13050  return value;
13051}
13052#endif
13053#endif
13054"#;
13055        let mut parser = tree_sitter::Parser::new();
13056        parser
13057            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13058            .unwrap();
13059        let tree = parser.parse(source, None).unwrap();
13060        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13061        let broken = recovered
13062            .iter()
13063            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
13064            .expect("the sentinel class must be recovered");
13065        let method_start = source
13066            .find("typename broken<T>::value_type broken<T>::method()")
13067            .expect("outer sibling owner");
13068        assert!(
13069            broken
13070                .owner_ranges
13071                .iter()
13072                .all(|owner| owner.range.start_byte > method_start
13073                    || owner.range.end_byte <= method_start),
13074            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
13075        );
13076    }
13077
13078    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
13079    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
13080        let mut signatures = parsed
13081            .declarations()
13082            .iter()
13083            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
13084            .filter_map(|unit| unit.signature().map(str::to_string))
13085            .collect::<Vec<_>>();
13086        signatures.sort();
13087        signatures.dedup();
13088        signatures
13089    }
13090
13091    #[test]
13092    fn callable_parameter_types_come_from_the_ast_parameter_list() {
13093        let source = r#"
13094template <typename T, ENABLE_BYTES(T)>
13095Vec256<T> DupOdd(Vec256<T> value) { return value; }
13096
13097struct Visitor {
13098  void fail(this auto const& self) {}
13099};
13100"#;
13101        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
13102        let dup_odd = parsed
13103            .declarations()
13104            .iter()
13105            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
13106            .expect("DupOdd declaration");
13107        assert_eq!(
13108            dup_odd.signature(),
13109            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
13110        );
13111        assert_eq!(
13112            parsed
13113                .signature_metadata
13114                .get(dup_odd)
13115                .and_then(|metadata| metadata.first())
13116                .and_then(SignatureMetadata::callable_parameter_types),
13117            Some(["Vec256<T>".to_string()].as_slice())
13118        );
13119
13120        let fail = parsed
13121            .declarations()
13122            .iter()
13123            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
13124            .expect("explicit-object member");
13125        assert_eq!(fail.signature(), Some("(const this auto &)"));
13126        let metadata = parsed
13127            .signature_metadata
13128            .get(fail)
13129            .and_then(|metadata| metadata.first())
13130            .expect("explicit-object signature metadata");
13131        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
13132        assert!(
13133            metadata
13134                .callable_arity()
13135                .is_some_and(|arity| arity.accepts(0))
13136        );
13137    }
13138
13139    #[test]
13140    fn trailing_qualifiers_survive_parameter_list_whitespace() {
13141        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
13142        // declarator's structure, so an out-of-line definition that spells its
13143        // parameter list with different whitespace than the declaration must
13144        // still carry it.
13145        let source = r#"
13146struct Widget {
13147  bool multiline(int settings, int supprs) const;
13148  bool doublespace(int settings, int supprs) const;
13149  bool noexcept_multiline(int settings, int supprs) noexcept;
13150  bool ref_multiline(int settings, int supprs) &&;
13151};
13152bool
13153Widget::multiline (int settings,
13154                   int supprs) const
13155{ return settings + supprs > 0; }
13156bool Widget::doublespace(int settings,  int supprs) const { return true; }
13157bool Widget::noexcept_multiline(int settings,
13158                                int supprs) noexcept { return true; }
13159bool Widget::ref_multiline(int settings,
13160                           int supprs) && { return true; }
13161"#;
13162        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
13163        assert_eq!(
13164            vec!["(int, int) const".to_string()],
13165            identity_signatures(&parsed, "Widget.multiline")
13166        );
13167        assert_eq!(
13168            vec!["(int, int) const".to_string()],
13169            identity_signatures(&parsed, "Widget.doublespace")
13170        );
13171        assert_eq!(
13172            vec!["(int, int) noexcept".to_string()],
13173            identity_signatures(&parsed, "Widget.noexcept_multiline")
13174        );
13175        assert_eq!(
13176            vec!["(int, int) &&".to_string()],
13177            identity_signatures(&parsed, "Widget.ref_multiline")
13178        );
13179    }
13180
13181    #[test]
13182    fn macro_fragmented_plain_class_keeps_following_member_signature() {
13183        let source = r#"
13184struct CString {};
13185class CMessage {
13186public:
13187  CString GetParams(unsigned int index, unsigned int length = -1) const
13188      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
13189    return GetParamsColon(index, length);
13190  }
13191  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
13192};
13193CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
13194  return {};
13195}
13196"#;
13197        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
13198        assert_eq!(
13199            vec!["(unsigned int, unsigned int) const".to_string()],
13200            identity_signatures(&parsed, "CMessage.GetParamsColon")
13201        );
13202    }
13203
13204    #[test]
13205    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
13206        let source = r#"
13207#pragma once
13208#define DEMO_DEPRECATED(message)
13209namespace demo {
13210struct Base {
13211    static int aligned(int value) { return value; }
13212    int legacy(int value) const
13213        DEMO_DEPRECATED("use replacement()") { return value; }
13214    int replacement() const;
13215    void run(int value);
13216};
13217struct OtherBase {
13218    void run(int value);
13219    static int aligned(int value) { return value; }
13220};
13221struct Derived : Base {};
13222struct Override : Base {
13223    void run(int value);
13224    static int aligned(int value) { return value; }
13225};
13226struct RecoveredOverride : Base {
13227    int legacy(int value) const
13228        DEMO_DEPRECATED("use replacement()") { return value; }
13229    void run(int value);
13230};
13231struct Hidden : Base {
13232    void run(int first, int second);
13233    static int aligned(int first, int second) { return first + second; }
13234};
13235struct Ambiguous : Base, OtherBase {};
13236}
13237struct Global {};
13238"#;
13239        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
13240        let declarations = parsed.declarations();
13241        let fq_names = declarations
13242            .iter()
13243            .map(|unit| unit.fq_name())
13244            .collect::<std::collections::BTreeSet<_>>();
13245
13246        for expected in [
13247            "demo.Base",
13248            "demo.Base.aligned",
13249            "demo.Base.legacy",
13250            "demo.Base.replacement",
13251            "demo.Base.run",
13252            "demo.Derived",
13253            "demo.OtherBase",
13254            "demo.Override",
13255            "demo.RecoveredOverride",
13256            "demo.Hidden",
13257            "demo.Ambiguous",
13258            "Global",
13259        ] {
13260            assert!(
13261                fq_names.contains(expected),
13262                "missing {expected} from namespaced macro fragment: {declarations:#?}"
13263            );
13264        }
13265        assert!(
13266            !fq_names.contains("Derived"),
13267            "following class escaped its namespace: {declarations:#?}"
13268        );
13269        assert!(
13270            !fq_names.contains("demo.Global"),
13271            "global class crossed the recovered namespace boundary: {declarations:#?}"
13272        );
13273    }
13274
13275    #[test]
13276    fn trailing_qualifiers_still_separate_genuine_overloads() {
13277        // The qualifier must keep distinguishing the real C++ overload sets it
13278        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
13279        let source = r#"
13280struct Widget {
13281  int* slot(int index);
13282  const int* slot(int index) const;
13283  int log(int severity) &;
13284  int log(int severity) &&;
13285};
13286"#;
13287        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
13288        assert_eq!(
13289            vec!["(int)".to_string(), "(int) const".to_string()],
13290            identity_signatures(&parsed, "Widget.slot")
13291        );
13292        assert_eq!(
13293            vec!["(int) &".to_string(), "(int) &&".to_string()],
13294            identity_signatures(&parsed, "Widget.log")
13295        );
13296    }
13297
13298    #[test]
13299    fn virtual_specifier_is_not_part_of_the_identity_signature() {
13300        // `override` never appears on the out-of-line definition, and C++ does
13301        // not make it part of the signature, so it must not split the identity.
13302        let source = r#"
13303struct Base {
13304  virtual void run(int value) const;
13305};
13306struct Widget : Base {
13307  void run(int value) const override;
13308};
13309void Widget::run(int value) const {}
13310"#;
13311        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
13312        assert_eq!(
13313            vec!["(int) const".to_string()],
13314            identity_signatures(&parsed, "Widget.run")
13315        );
13316    }
13317
13318    #[test]
13319    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
13320        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
13321        // the function type, so a declaration that spells `const int` and a
13322        // definition that spells `int` are one entity.
13323        let source = r#"
13324struct Widget {
13325  bool value_params(const int settings, const int supprs);
13326  void pointee_const(const int* p);
13327  void pointer_const(int* const p);
13328  void both_const(const int* const p);
13329  void reference_const(const int& p);
13330  void array_const(const int values[4]);
13331};
13332bool Widget::value_params(int settings, int supprs) { return true; }
13333void Widget::pointer_const(int* p) {}
13334void Widget::both_const(const int* p) {}
13335"#;
13336        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
13337        assert_eq!(
13338            vec!["(int, int)".to_string()],
13339            identity_signatures(&parsed, "Widget.value_params")
13340        );
13341        assert_eq!(
13342            vec!["(int *)".to_string()],
13343            identity_signatures(&parsed, "Widget.pointer_const")
13344        );
13345        assert_eq!(
13346            vec!["(const int *)".to_string()],
13347            identity_signatures(&parsed, "Widget.both_const")
13348        );
13349        // The const that is not top-level still distinguishes the type.
13350        assert_eq!(
13351            vec!["(const int *)".to_string()],
13352            identity_signatures(&parsed, "Widget.pointee_const")
13353        );
13354        assert_eq!(
13355            vec!["(const int &)".to_string()],
13356            identity_signatures(&parsed, "Widget.reference_const")
13357        );
13358        assert_eq!(
13359            vec!["(const int [4])".to_string()],
13360            identity_signatures(&parsed, "Widget.array_const")
13361        );
13362    }
13363
13364    #[test]
13365    fn top_level_parameter_const_still_separates_pointee_overloads() {
13366        let source = r#"
13367struct Widget {
13368  void take(const int* p);
13369  void take(int* p);
13370};
13371"#;
13372        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
13373        assert_eq!(
13374            vec!["(const int *)".to_string(), "(int *)".to_string()],
13375            identity_signatures(&parsed, "Widget.take")
13376        );
13377    }
13378}