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/// Structured name for a class-like unit: the enclosing namespace's `Package`
162/// segments followed by the `$`-joined nested-class `Type` chain in `short_name`.
163fn cpp_class_fq(package_name: &str, short_name: &str) -> FqName {
164    let mut fq = FqName::new();
165    cpp_push_package(&mut fq, package_name);
166    cpp_push_type_chain(&mut fq, short_name);
167    fq
168}
169
170/// Structured name for a member unit (function, field, enumerator). The
171/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
172/// the member has an owner, by `.member`; free functions and globals have no
173/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
174/// C++ member names never contain a literal `.`, so the single `.` (if any)
175/// separates the owner chain from the member.
176pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
177    let mut fq = FqName::new();
178    cpp_push_package(&mut fq, package_name);
179    match short_name.rsplit_once('.') {
180        Some((owner_chain, member)) => {
181            cpp_push_type_chain(&mut fq, owner_chain);
182            fq.push(cpp_segment(member, SegmentKind::Member));
183        }
184        None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
185    }
186    fq
187}
188
189#[derive(Clone)]
190pub struct ScopeInfo {
191    package_name: String,
192    module: Option<CodeUnit>,
193    class_unit: Option<CodeUnit>,
194    template_signature: Option<String>,
195    template_metadata: Option<CppTemplateMetadata>,
196    declarations_are_fields: bool,
197    recovered_specialization_member_scope: bool,
198    /// Namespace targets of every `using namespace X;` directive lexically
199    /// visible at this point in the file (declaration order), threaded
200    /// forward sibling-by-sibling by the sequential container walk (see
201    /// `CppWork::Siblings`). An out-of-line member definition written as a
202    /// bare `Class::method` at file/namespace scope with no enclosing
203    /// `namespace {}` block (issue #1093, e.g. log4cxx's
204    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
205    /// const { ... }`) has no other structural signal for which namespace
206    /// actually owns `Class`; this is the best-effort candidate list used to
207    /// recover it so the definition's indexed identity matches its header
208    /// declaration's.
209    visible_using_namespaces: Vec<String>,
210}
211
212struct CppContainer<'tree> {
213    node: Node<'tree>,
214    scope: ScopeInfo,
215}
216
217struct CppNodeWork<'tree> {
218    node: Node<'tree>,
219    scope: ScopeInfo,
220}
221
222/// Cursor over one container's remaining named children, processed one at a
223/// time (rather than all at once) so a `using namespace X;` sibling can
224/// update `scope.visible_using_namespaces` for the siblings that follow it,
225/// matching real C++ using-directive semantics. Nested container work is
226/// still pushed and fully drained before the cursor resumes (stack LIFO
227/// order), preserving the original left-to-right visitation order.
228struct CppSiblingsWork<'tree> {
229    children: std::vec::IntoIter<Node<'tree>>,
230    scope: ScopeInfo,
231}
232
233enum CppWork<'tree> {
234    Container(CppContainer<'tree>),
235    Node(CppNodeWork<'tree>),
236    Siblings(CppSiblingsWork<'tree>),
237}
238
239fn class_like_name(node: Node<'_>, source: &str) -> Option<String> {
240    let best = class_like_name_from_children(node, source);
241    if let Some(parent) = node.parent()
242        && matches!(
243            parent.kind(),
244            "declaration" | "field_declaration" | "function_definition"
245        )
246        && node
247            .child_by_field_name("name")
248            .map(|name_node| {
249                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
250            })
251            .unwrap_or(false)
252        && let Some(recovered) = exported_class_name_from_node(parent, source)
253        && best.as_deref() != Some(recovered.as_str())
254    {
255        return Some(recovered);
256    }
257    best.or_else(|| {
258        node.child_by_field_name("name")
259            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
260            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
261    })
262}
263
264fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
265    let mut grammar_name = None;
266    if let Some(name_node) = node.child_by_field_name("name") {
267        let name = normalize_cpp_whitespace(node_text(name_node, source));
268        if name.is_empty() {
269            return None;
270        }
271        if !cpp_export_macro_token(&name) {
272            return Some(name);
273        }
274        grammar_name = Some(name);
275    }
276
277    let mut best = None;
278    let mut cursor = node.walk();
279    let mut stack = Vec::new();
280    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
281        if matches!(
282            child.kind(),
283            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
284        ) {
285            break;
286        }
287        stack.push(child);
288    }
289
290    while let Some(current) = stack.pop() {
291        if matches!(current.kind(), "type_identifier" | "identifier") {
292            let name = normalize_cpp_whitespace(node_text(current, source));
293            if !name.is_empty() && !cpp_export_macro_token(&name) {
294                best = Some(name);
295            }
296            continue;
297        }
298
299        for index in (0..current.named_child_count()).rev() {
300            if let Some(child) = current.named_child(index) {
301                stack.push(child);
302            }
303        }
304    }
305    best.or(grammar_name)
306}
307
308pub fn cpp_export_macro_token(token: &str) -> bool {
309    token
310        .chars()
311        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
312}
313
314struct RecoveredExportedClass<'tree> {
315    declaration_node: Node<'tree>,
316    name: String,
317    body: Option<Node<'tree>>,
318    raw_supertypes: Option<Vec<String>>,
319    uses_initializer_body: bool,
320    /// Present only for the fragmented multiple-base export shape (issue #938).
321    /// Carries the true class-body byte region -- the members tree-sitter scattered
322    /// out of the recovered node -- so they can be reparsed and re-owned as members
323    /// rather than lost inside the truncated `initializer_list` stand-in.
324    fragmented_body: Option<FragmentedExportBody>,
325}
326
327/// The recovered class-body geometry for a fragmented multiple-base export class.
328/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
329/// verbatim for a region reparse (issue #941 machinery) so every recovered member
330/// keeps its exact original byte/line position. `class_range` is the full class
331/// navigation range spanning to the displaced closing brace.
332struct FragmentedExportBody {
333    reparse_start: usize,
334    reparse_end: usize,
335    class_range: Range,
336}
337
338struct DisplacedFragmentNamespaceBoundary<'tree> {
339    class_close: Node<'tree>,
340    class_semicolon: Node<'tree>,
341    namespace_items: Vec<Node<'tree>>,
342}
343
344/// Result of validating a reparsed fragmented class body.  A complete tree can
345/// safely consume the whole region.  A partial tree may contain only the exact
346/// class-named constructor that tree-sitter merged into an access label; its
347/// remaining siblings must stay on the ordinary outer walk.
348enum FragmentedExportMembers {
349    Complete(Tree),
350    ConditionalConstructor(Tree),
351}
352
353#[derive(Clone, Copy)]
354struct DisplacedMacroClassTail {
355    split_index: usize,
356    class_range: Range,
357}
358
359fn recover_exported_class_declaration<'tree>(
360    node: Node<'tree>,
361    source: &str,
362) -> Option<RecoveredExportedClass<'tree>> {
363    if let Some(recovered) = recover_malformed_exported_multiple_base_class(node, source) {
364        return Some(recovered);
365    }
366
367    let class_node = first_class_like_child(node)?;
368    if let Some(name_node) = class_node.child_by_field_name("name") {
369        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
370        if cpp_export_macro_token(&class_name) {
371            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
372            // Name declarator. Only a bare declarator can be the displaced class name;
373            // wrappers describe an object whose type merely happens to look macro-like.
374            let mut cursor = node.walk();
375            if node
376                .children_by_field_name("declarator", &mut cursor)
377                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
378            {
379                return None;
380            }
381        } else if has_direct_cpp_declarator(node) {
382            return None;
383        }
384    }
385    let name = exported_class_name_from_node(class_node, source)?;
386    Some(RecoveredExportedClass {
387        declaration_node: class_node,
388        name,
389        body: cpp_body_node(class_node),
390        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
391            .then(|| extract_cpp_supertypes(class_node, source)),
392        uses_initializer_body: false,
393        fragmented_body: None,
394    })
395}
396
397fn recover_malformed_exported_multiple_base_class<'tree>(
398    node: Node<'tree>,
399    source: &str,
400) -> Option<RecoveredExportedClass<'tree>> {
401    if node.kind() != "declaration" {
402        return None;
403    }
404    let class_node = node.child_by_field_name("type")?;
405    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
406        return None;
407    }
408    let macro_name = class_node
409        .child_by_field_name("name")
410        .and_then(|name| direct_identifier_name(name, source))?;
411    if !cpp_export_macro_token(&macro_name) {
412        return None;
413    }
414
415    let mut named_cursor = node.walk();
416    let mut named = node.named_children(&mut named_cursor);
417    if named
418        .next()
419        .is_none_or(|child| !same_node(child, class_node))
420    {
421        return None;
422    }
423    let displaced = named.next()?;
424    if displaced.kind() != "ERROR" {
425        return None;
426    }
427    let name = displaced_exported_class_name(displaced, source)?;
428
429    let remaining = named.collect::<Vec<_>>();
430    let init = *remaining.last()?;
431    if init.kind() != "init_declarator" {
432        return None;
433    }
434    let final_base = init
435        .child_by_field_name("declarator")
436        .and_then(|base| recovered_malformed_base_name(base, source))?;
437    let body = init.child_by_field_name("value")?;
438    // A complete reduction has a real closing brace here. In Chromium's Widget
439    // declaration, tree-sitter instead emits the same direct `}` slot as a
440    // zero-width missing node where the first body macro truncates the prefix.
441    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
442        return None;
443    }
444
445    let mut declarator_cursor = node.walk();
446    let direct_declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
447    if direct_declarators.count() < 2 {
448        return None;
449    }
450    if remaining[..remaining.len() - 1]
451        .iter()
452        .any(|child| match child.kind() {
453            "qualified_identifier"
454            | "scoped_type_identifier"
455            | "type_identifier"
456            | "identifier" => false,
457            "ERROR" => !is_malformed_inheritance_access(*child, source),
458            _ => true,
459        })
460    {
461        return None;
462    }
463
464    let mut raw_supertypes = Vec::new();
465    for base in &remaining[..remaining.len() - 1] {
466        if base.kind() == "ERROR" {
467            continue;
468        }
469        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
470    }
471    raw_supertypes.push(final_base);
472
473    Some(RecoveredExportedClass {
474        declaration_node: node,
475        name,
476        body: Some(body),
477        raw_supertypes: Some(raw_supertypes),
478        uses_initializer_body: true,
479        fragmented_body: fragmented_export_body_region(node, body, source),
480    })
481}
482
483/// Locate the true class-body region for a fragmented multiple-base export class.
484///
485/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
486/// emits in place of the real class body. Tree-sitter reduces that body in one of
487/// two shapes, both of which lose the members from the recovered node:
488///
489/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
490///   a real closing brace and holds the whole body text inline. The interior between
491///   the braces reparses to the members directly.
492/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
493///   first member with a zero-width MISSING `}`; every later member -- and the real
494///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
495///   siblings. The interior runs from the opening brace to that displaced `}`.
496///
497/// Returns the interior byte range to reparse plus the full class navigation range.
498fn fragmented_export_body_region(
499    node: Node<'_>,
500    body: Node<'_>,
501    source: &str,
502) -> Option<FragmentedExportBody> {
503    let reparse_start = body.start_byte() + 1;
504    let close = direct_close_brace(body)?;
505    if close.end_byte() > close.start_byte() {
506        return Some(FragmentedExportBody {
507            reparse_start,
508            reparse_end: close.start_byte(),
509            class_range: cpp_declaration_range(node),
510        });
511    }
512    // The closing brace was displaced past the recovered node. A balanced nested
513    // class keeps its own braces, so the first lone-`}` sibling is this class's.
514    let mut sibling = node.next_named_sibling();
515    let displaced_close = loop {
516        let Some(current) = sibling else {
517            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
518        };
519        if cpp_is_stray_close_brace(current, source) {
520            break current;
521        }
522        sibling = current.next_named_sibling();
523    };
524    Some(FragmentedExportBody {
525        reparse_start,
526        reparse_end: displaced_close.start_byte(),
527        class_range: Range {
528            start_byte: node.start_byte(),
529            end_byte: displaced_close.end_byte(),
530            start_line: node.start_position().row + 1,
531            end_line: displaced_close.end_position().row + 1,
532        },
533    })
534}
535
536/// Locate the true class-body region for the export-macro class shape that
537/// tree-sitter promotes to a `function_definition`.
538///
539/// In this shape the synthetic function body closes at the first inline
540/// method, while the class's real members continue as root-level siblings until
541/// a stray `}` followed by the displaced class `;`. Reparse the complete
542/// interior so those siblings are visited with the recovered class scope.
543fn fragmented_export_function_body_region(
544    node: Node<'_>,
545    body: Node<'_>,
546    source: &str,
547    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
548) -> Option<FragmentedExportBody> {
549    let reparse_start = body.start_byte().checked_add(1)?;
550    if let Some(boundary) = displaced_namespace {
551        return Some(FragmentedExportBody {
552            reparse_start,
553            reparse_end: boundary.class_close.start_byte(),
554            class_range: Range {
555                start_byte: node.start_byte(),
556                end_byte: boundary.class_semicolon.end_byte(),
557                start_line: node.start_position().row + 1,
558                end_line: boundary.class_semicolon.end_position().row + 1,
559            },
560        });
561    }
562    let siblings = cpp_following_named_siblings(node, source);
563    let boundary = fragmented_export_sibling_class_boundary(node, source);
564    let boundary_index = boundary.and_then(|boundary| {
565        siblings
566            .iter()
567            .position(|candidate| same_node(*candidate, boundary))
568    });
569    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
570    let mut sibling_index = 0;
571    // A complete recovered class's synthetic wrapper is immediately followed
572    // by its displaced semicolon (comments may sit between the body and that
573    // semicolon). Only scan for a later stray close when real member siblings
574    // intervene; otherwise every earlier complete class would borrow the next
575    // malformed class's close and claim its members.
576    while let Some(current) = siblings.get(sibling_index).copied() {
577        if current.kind() == "comment" {
578            sibling_index += 1;
579            continue;
580        }
581        if cpp_is_stray_semicolon(current, source) {
582            return None;
583        }
584        break;
585    }
586    while let Some(current) = siblings.get(sibling_index).copied() {
587        let next = siblings.get(sibling_index + 1).copied();
588        if cpp_is_stray_close_brace(current, source)
589            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
590        {
591            let semicolon = next.expect("checked above");
592            return Some(FragmentedExportBody {
593                reparse_start,
594                reparse_end: current.start_byte(),
595                class_range: Range {
596                    start_byte: node.start_byte(),
597                    end_byte: semicolon.end_byte(),
598                    start_line: node.start_position().row + 1,
599                    end_line: semicolon.end_position().row + 1,
600                },
601            });
602        }
603        // When the final access label keeps the class close in its malformed
604        // declaration body, tree-sitter nests the lone `}` ERROR below the
605        // label instead of exposing it as a direct sibling. Search only the
606        // scattered siblings after the synthetic wrapper. The first such
607        // close is the class terminator because nested class bodies retain
608        // their own balanced class_specifier nodes.
609        if current.start_byte() >= body.end_byte()
610            && let Some(close) = cpp_nested_stray_close_brace(current, source)
611        {
612            return Some(FragmentedExportBody {
613                reparse_start,
614                reparse_end: close.start_byte(),
615                class_range: Range {
616                    start_byte: node.start_byte(),
617                    end_byte: current.end_byte(),
618                    start_line: node.start_position().row + 1,
619                    end_line: current.end_position().row + 1,
620                },
621            });
622        }
623        sibling_index += 1;
624    }
625    boundary.map(|boundary| FragmentedExportBody {
626        reparse_start,
627        reparse_end: boundary.start_byte(),
628        class_range: Range {
629            start_byte: node.start_byte(),
630            end_byte: boundary.start_byte(),
631            start_line: node.start_position().row + 1,
632            end_line: boundary.start_position().row + 1,
633        },
634    })
635}
636
637/// Find a later macro-export class that tree-sitter lifted through an enclosing
638/// preprocessor container. A class that is still a direct sibling can be a
639/// nested member of the current fragmented class, so only a changed parent is
640/// a proven boundary between the two recovered class envelopes.
641fn fragmented_export_sibling_class_boundary<'tree>(
642    node: Node<'tree>,
643    source: &str,
644) -> Option<Node<'tree>> {
645    let node_parent = node.parent()?;
646    cpp_following_named_siblings(node, source)
647        .into_iter()
648        .find(|candidate| {
649            recover_exported_class_function_definition(*candidate, source).is_some()
650                && candidate
651                    .parent()
652                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
653        })
654}
655
656/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
657/// export-class wrapper can place the class close inside an access-label node,
658/// so direct-sibling checks alone miss the boundary.  Walk named CST children
659/// only; the helper does not inspect source text beyond the existing structured
660/// stray-brace predicate.
661fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
662    let mut stack = vec![node];
663    while let Some(current) = stack.pop() {
664        if cpp_is_stray_close_brace(current, source) {
665            return Some(current);
666        }
667        let mut cursor = current.walk();
668        stack.extend(current.named_children(&mut cursor));
669    }
670    None
671}
672
673/// Return named siblings that follow `node`, including siblings that tree-sitter
674/// attached to an enclosing container after malformed recovery split the local
675/// declaration list. Stop at the first structurally visible class close so a
676/// later namespace or exported class cannot supply the recovery boundary.
677fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
678    let mut siblings = Vec::new();
679    let mut anchor = node;
680    while let Some(parent) = anchor.parent() {
681        let at_translation_unit = parent.kind() == "translation_unit";
682        let mut sibling = anchor.next_named_sibling();
683        while let Some(current) = sibling {
684            if at_translation_unit
685                && (current.kind() == "namespace_definition"
686                    || (current.kind() == "function_definition"
687                        && first_class_like_child(current).is_some()))
688            {
689                return siblings;
690            }
691            siblings.push(current);
692            if cpp_is_stray_close_brace(current, source) {
693                if let Some(semicolon) = current
694                    .next_named_sibling()
695                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
696                {
697                    siblings.push(semicolon);
698                }
699                return siblings;
700            }
701            if current.start_byte() >= node.end_byte()
702                && matches!(current.kind(), "ERROR" | "labeled_statement")
703                && cpp_nested_stray_close_brace(current, source).is_some()
704            {
705                return siblings;
706            }
707            sibling = current.next_named_sibling();
708        }
709        anchor = parent;
710    }
711    siblings
712}
713
714fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
715    if node.start_byte() >= class_end {
716        return false;
717    }
718    node.end_byte() <= class_end
719        || cpp_nested_stray_close_brace(node, source)
720            .is_some_and(|close| close.start_byte() == class_end)
721}
722
723/// Recover a plain class whose opening prefix is retained in one ERROR node
724/// while one or more nested class closes and the outer close are displaced to
725/// sibling `}`/`;` nodes. This is the non-export counterpart to the fragmented
726/// export-class recovery above. All boundaries come from tree-sitter nodes: the
727/// direct class tokens establish nesting depth and the displaced close nodes
728/// terminate it.
729fn fragmented_plain_class_body<'tree>(
730    node: Node<'tree>,
731    source: &str,
732) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
733    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
734        return Some(recovered);
735    }
736    if node.kind() != "ERROR" {
737        return None;
738    }
739    let mut cursor = node.walk();
740    let children = node.children(&mut cursor).collect::<Vec<_>>();
741    let keyword = children.first()?;
742    if !matches!(keyword.kind(), "class" | "struct" | "union") {
743        return None;
744    }
745    let name_node = children
746        .iter()
747        .copied()
748        .skip(1)
749        .find(|child| child.is_named())?;
750    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
751        return None;
752    }
753    let name = normalize_cpp_whitespace(node_text(name_node, source));
754    if name.is_empty() || cpp_export_macro_token(&name) {
755        return None;
756    }
757    let open_index = children.iter().position(|child| child.kind() == "{")?;
758    let open = children[open_index];
759    let nested_class_opens = children[open_index + 1..]
760        .iter()
761        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
762        .count();
763    let mut closes_remaining = 1 + nested_class_opens;
764    let mut sibling = node.next_named_sibling();
765    while let Some(candidate) = sibling {
766        let next = candidate.next_named_sibling();
767        if cpp_is_stray_close_brace(candidate, source) {
768            closes_remaining -= 1;
769            if closes_remaining == 0 {
770                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
771                if open.end_byte() >= candidate.start_byte() {
772                    return None;
773                }
774                return Some((
775                    node,
776                    name,
777                    FragmentedExportBody {
778                        reparse_start: open.end_byte(),
779                        reparse_end: candidate.start_byte(),
780                        class_range: Range {
781                            start_byte: node.start_byte(),
782                            end_byte: semicolon.end_byte(),
783                            start_line: node.start_position().row + 1,
784                            end_line: semicolon.end_position().row + 1,
785                        },
786                    },
787                ));
788            }
789        }
790        sibling = next;
791    }
792    None
793}
794
795pub(crate) fn recovered_fragmented_plain_class_has_body(
796    node: Node<'_>,
797    source: &str,
798    expected_name: &str,
799    expected_range: &Range,
800) -> bool {
801    fragmented_plain_class_body(node, source).is_some_and(|(_, name, fragmented)| {
802        name == expected_name
803            && fragmented.class_range.start_byte == expected_range.start_byte
804            && fragmented.class_range.end_byte == expected_range.end_byte
805    })
806}
807
808/// Recover a plain class whose parser-visible body ends inside a malformed
809/// inline member. Tree-sitter then attaches either the next real member
810/// declarator or the unfinished `else` branch directly to the outer function
811/// definition and leaves the class's actual `};` among later siblings. Those
812/// structured continuations and the close/semicolon siblings establish the
813/// complete body envelope without interpreting source text.
814fn fragmented_plain_class_declaration_body<'tree>(
815    node: Node<'tree>,
816    source: &str,
817) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
818    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
819        return None;
820    }
821    let class_node = node.child_by_field_name("type")?;
822    if !matches!(
823        class_node.kind(),
824        "class_specifier" | "struct_specifier" | "union_specifier"
825    ) {
826        return None;
827    }
828    let name_node = class_node.child_by_field_name("name")?;
829    let name = normalize_cpp_whitespace(node_text(name_node, source));
830    if name.is_empty() || cpp_export_macro_token(&name) {
831        return None;
832    }
833    let body = cpp_body_node(class_node)?;
834    if body.kind() != "field_declaration_list" {
835        return None;
836    }
837    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
838        if declarator.start_byte() < class_node.end_byte() {
839            return None;
840        }
841        let mut cursor = node.walk();
842        node.named_children(&mut cursor).any(|child| {
843            if child.kind() != "ERROR"
844                || child.start_byte() < class_node.end_byte()
845                || child.end_byte() > declarator.start_byte()
846            {
847                return false;
848            }
849            let mut cursor = child.walk();
850            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
851            let Some((return_type, attributes)) = components.split_last() else {
852                return false;
853            };
854            matches!(
855                return_type.kind(),
856                "identifier"
857                    | "type_identifier"
858                    | "primitive_type"
859                    | "decltype"
860                    | "placeholder_type_specifier"
861            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
862                && attributes.iter().all(|attribute| {
863                    matches!(attribute.kind(), "identifier" | "type_identifier")
864                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
865                            *attribute, source,
866                        )))
867                })
868        })
869    } else {
870        let mut cursor = node.walk();
871        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
872        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
873            if same_node(*candidate_class, class_node)
874                && continuation.kind() == "identifier"
875                && node_text(*continuation, source) == "else"
876                && continuation_body.kind() == "compound_statement"
877                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
878                && continuation_body
879                    .child(continuation_body.child_count().saturating_sub(1))
880                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
881    };
882    if !displaced_member {
883        return None;
884    }
885    let open = body
886        .children(&mut body.walk())
887        .find(|child| child.kind() == "{")?;
888    let siblings = cpp_following_named_siblings(node, source);
889    let ordinary_boundary =
890        siblings
891            .iter()
892            .copied()
893            .enumerate()
894            .find_map(|(close_index, close)| {
895                cpp_is_stray_close_brace(close, source)
896                    .then(|| {
897                        siblings
898                            .get(close_index + 1)
899                            .copied()
900                            .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
901                            .map(|semicolon| (close, semicolon))
902                    })
903                    .flatten()
904            });
905    let (close, semicolon) =
906        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
907            (boundary.class_close, boundary.class_semicolon)
908        } else {
909            ordinary_boundary?
910        };
911    if open.end_byte() >= close.start_byte() {
912        return None;
913    }
914    Some((
915        class_node,
916        name,
917        FragmentedExportBody {
918            reparse_start: open.end_byte(),
919            reparse_end: close.start_byte(),
920            class_range: Range {
921                start_byte: class_node.start_byte(),
922                end_byte: semicolon.end_byte(),
923                start_line: class_node.start_position().row + 1,
924                end_line: semicolon.end_position().row + 1,
925            },
926        },
927    ))
928}
929
930fn displaced_export_function_namespace_shape<'tree>(
931    declaration: Node<'tree>,
932    source: &str,
933) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
934    let mut nested = Vec::new();
935    for index in (0..declaration.named_child_count()).rev() {
936        nested.push(declaration.named_child(index)?);
937    }
938    while let Some(current) = nested.pop() {
939        // A recovered export class nested in this class can consume the first
940        // parser-visible namespace close itself. In that shape the existing
941        // later-class boundary logic already distinguishes the nested and
942        // namespace-sibling owners; do not mistake the nested close for this
943        // class's terminator.
944        if recover_exported_class_function_definition(current, source).is_some() {
945            return None;
946        }
947        for index in (0..current.named_child_count()).rev() {
948            nested.push(current.named_child(index)?);
949        }
950    }
951    let mut same_envelope_sibling = declaration.next_named_sibling();
952    while let Some(current) = same_envelope_sibling {
953        if recover_exported_class_function_definition(current, source).is_some() {
954            return None;
955        }
956        same_envelope_sibling = current.next_named_sibling();
957    }
958    let declaration_list = declaration.parent()?;
959    if declaration_list.kind() != "declaration_list" {
960        return None;
961    }
962    let namespace = declaration_list.parent()?;
963    if namespace.kind() != "namespace_definition"
964        || namespace.child_by_field_name("body") != Some(declaration_list)
965    {
966        return None;
967    }
968    let class_close = direct_close_brace(declaration_list)?;
969    let trailing_semicolon = namespace.next_named_sibling()?;
970    if trailing_semicolon.kind() != "expression_statement"
971        || trailing_semicolon.named_child_count() != 0
972    {
973        return None;
974    }
975    // A chain of malformed export classes can consume one parser-visible
976    // namespace close per class. Walk through the enclosing sibling levels so
977    // the later real namespace close remains the structural boundary; a
978    // direct next-sibling walk stops at the first collapsed namespace and
979    // incorrectly makes its intervening items members of this class.
980    let siblings = cpp_following_named_siblings(namespace, source);
981    let trailing_index = siblings
982        .iter()
983        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
984    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
985        recover_exported_class_function_definition(*candidate, source).is_some()
986    }) {
987        // Consecutive recovered classes already have an exact sibling-class
988        // boundary. Preserve that established path, including nested export
989        // classes, instead of interpreting the first class close as a
990        // collapsed namespace boundary.
991        return None;
992    }
993    let mut namespace_items = Vec::new();
994    let mut nested_fragment_end = 0;
995    for current in siblings.into_iter().skip(trailing_index + 1) {
996        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
997        {
998            return Some(DisplacedFragmentNamespaceBoundary {
999                class_close,
1000                class_semicolon: trailing_semicolon,
1001                namespace_items,
1002            });
1003        }
1004        if current.start_byte() >= nested_fragment_end
1005            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1006        {
1007            nested_fragment_end = fragmented.class_range.end_byte;
1008        } else if current.start_byte() >= nested_fragment_end
1009            && recover_exported_class_function_definition(current, source).is_some()
1010            && let Some(body) = cpp_body_node(current)
1011            && let Some(fragmented) =
1012                fragmented_export_function_body_region(current, body, source, None)
1013        {
1014            nested_fragment_end = fragmented.class_range.end_byte;
1015        }
1016        namespace_items.push(current);
1017    }
1018    None
1019}
1020
1021fn displaced_fragment_namespace_boundary<'tree>(
1022    declaration: Node<'tree>,
1023    body: Node<'tree>,
1024    source: &str,
1025) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1026    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1027    let reparse_start = body.start_byte() + 1;
1028    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1029    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1030}
1031
1032/// Recover the class/namespace brace geometry for a declaration whose class
1033/// close tree-sitter consumed as the enclosing namespace close. This proof is
1034/// independent of whether every member in the class body can be reparsed: the
1035/// ordinary-tree fallback can still re-own bounded sibling declarations when
1036/// an unknown macro makes the complete body reparse unsafe.
1037fn displaced_fragment_namespace_geometry<'tree>(
1038    declaration: Node<'tree>,
1039    source: &str,
1040) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1041    // A templated class's malformed function wrapper remains beneath the
1042    // template node even though its later members have escaped to the
1043    // enclosing declaration list. Lift only that exact declaration child.
1044    let envelope = declaration
1045        .parent()
1046        .filter(|parent| {
1047            parent.kind() == "template_declaration"
1048                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1049        })
1050        .unwrap_or(declaration);
1051    let declaration_list = envelope.parent()?;
1052    if declaration_list.kind() != "declaration_list" {
1053        return None;
1054    }
1055    let namespace = declaration_list.parent()?;
1056    if namespace.kind() != "namespace_definition"
1057        || namespace.child_by_field_name("body") != Some(declaration_list)
1058    {
1059        return None;
1060    }
1061    let class_close = direct_close_brace(declaration_list)?;
1062    let trailing_semicolon = namespace.next_named_sibling()?;
1063    if trailing_semicolon.kind() != "expression_statement"
1064        || trailing_semicolon.named_child_count() != 0
1065    {
1066        return None;
1067    }
1068    let mut namespace_items = Vec::new();
1069    let mut sibling = trailing_semicolon.next_named_sibling();
1070    let mut nested_fragment_end = 0;
1071    loop {
1072        let current = sibling?;
1073        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1074        {
1075            break;
1076        }
1077        if current.start_byte() >= nested_fragment_end
1078            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1079        {
1080            nested_fragment_end = fragmented.class_range.end_byte;
1081        }
1082        namespace_items.push(current);
1083        sibling = current.next_named_sibling();
1084    }
1085    Some(DisplacedFragmentNamespaceBoundary {
1086        class_close,
1087        class_semicolon: trailing_semicolon,
1088        namespace_items,
1089    })
1090}
1091
1092/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1093fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1094    (0..node.child_count())
1095        .filter_map(|index| node.child(index))
1096        .find(|child| !child.is_named() && child.kind() == "}")
1097}
1098
1099/// A displaced lone closing brace: the class close that the fragmented multiple-base
1100/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1101fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1102    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1103}
1104
1105/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1106/// text while skipping line/block comments and string/char literals. The
1107/// exported-class recovery needs this when tree-sitter's bogus
1108/// `function_definition` body runs past the class's true closing brace and
1109/// swallows following siblings (issue #1524): the grammar tree carries no
1110/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1111/// close is located textually. Returns `None` when the text is unbalanced or
1112/// contains a construct the scanner deliberately does not interpret (raw
1113/// strings) -- callers treat that as "cannot partition" and keep the
1114/// un-split recovery.
1115fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1116    let bytes = source.as_bytes();
1117    if bytes.get(open_byte) != Some(&b'{') {
1118        return None;
1119    }
1120    let mut depth = 0usize;
1121    let mut i = open_byte;
1122    while i < bytes.len() {
1123        match bytes[i] {
1124            b'{' => depth += 1,
1125            b'}' => {
1126                depth = depth.checked_sub(1)?;
1127                if depth == 0 {
1128                    return Some(i);
1129                }
1130            }
1131            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1132                while i < bytes.len() && bytes[i] != b'\n' {
1133                    i += 1;
1134                }
1135                continue;
1136            }
1137            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1138                i += 2;
1139                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1140                    i += 1;
1141                }
1142                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1143                continue;
1144            }
1145            quote @ (b'"' | b'\'') => {
1146                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1147                // bail out rather than mis-count.
1148                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1149                    return None;
1150                }
1151                i += 1;
1152                while i < bytes.len() && bytes[i] != quote {
1153                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1154                }
1155                if i >= bytes.len() {
1156                    return None;
1157                }
1158            }
1159            _ => {}
1160        }
1161        i += 1;
1162    }
1163    None
1164}
1165
1166fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1167    let mut name = None;
1168    let mut colon_count = 0;
1169    let mut access_count = 0;
1170    for index in 0..node.child_count() {
1171        let child = node.child(index)?;
1172        match child.kind() {
1173            "identifier" | "type_identifier" if child.is_named() => {
1174                if name.is_some() {
1175                    return None;
1176                }
1177                let candidate = normalize_cpp_whitespace(node_text(child, source));
1178                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1179                    return None;
1180                }
1181                name = Some(candidate);
1182            }
1183            ":" if !child.is_named() => colon_count += 1,
1184            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1185            _ => return None,
1186        }
1187    }
1188    (colon_count == 1 && access_count == 1)
1189        .then_some(name)
1190        .flatten()
1191}
1192
1193fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1194    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1195        return false;
1196    }
1197    node.named_child(0)
1198        .and_then(|child| direct_identifier_name(child, source))
1199        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1200}
1201
1202fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1203    (0..node.child_count()).any(|index| {
1204        node.child(index)
1205            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1206    })
1207}
1208
1209fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1210    match node.kind() {
1211        "type_identifier" | "identifier" | "namespace_identifier" => {
1212            recovered_base_atom(node, source)
1213        }
1214        "template_type" | "template_function" => node
1215            .child_by_field_name("name")
1216            .and_then(|name| recovered_malformed_base_name(name, source)),
1217        "ERROR" => None,
1218        "qualified_identifier" | "scoped_type_identifier" => {
1219            let suffix = node
1220                .child_by_field_name("name")
1221                .and_then(|name| recovered_malformed_base_name(name, source))?;
1222            let scope = node
1223                .child_by_field_name("scope")
1224                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1225            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1226                malformed_qualified_prefix(node, source)?
1227            } else {
1228                if malformed_qualified_prefix(node, source).is_some() {
1229                    return None;
1230                }
1231                scope
1232            };
1233            Some(format!("{prefix}::{suffix}"))
1234        }
1235        _ => None,
1236    }
1237}
1238
1239fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1240    if !matches!(
1241        node.kind(),
1242        "identifier" | "type_identifier" | "namespace_identifier"
1243    ) {
1244        return None;
1245    }
1246    let name = normalize_cpp_whitespace(node_text(node, source));
1247    (!name.is_empty()).then_some(name)
1248}
1249
1250fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1251    let mut prefix = None;
1252    let mut cursor = node.walk();
1253    for error in node
1254        .named_children(&mut cursor)
1255        .filter(|child| child.kind() == "ERROR")
1256    {
1257        if error.named_child_count() != 1 || prefix.is_some() {
1258            return None;
1259        }
1260        prefix = error
1261            .named_child(0)
1262            .and_then(|child| recovered_base_atom(child, source));
1263        prefix.as_ref()?;
1264    }
1265    prefix
1266}
1267
1268fn recover_exported_class_function_definition<'tree>(
1269    node: Node<'tree>,
1270    source: &str,
1271) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1272    if node.kind() != "function_definition" {
1273        return None;
1274    }
1275    let type_node = node.child_by_field_name("type")?;
1276    let declarator = node.child_by_field_name("declarator")?;
1277
1278    if matches!(
1279        type_node.kind(),
1280        "class_specifier" | "struct_specifier" | "union_specifier"
1281    ) {
1282        let type_name = type_node
1283            .child_by_field_name("name")
1284            .and_then(|name| direct_identifier_name(name, source));
1285        let exported_macro_type = type_name
1286            .as_ref()
1287            .is_some_and(|name| cpp_export_macro_token(name));
1288        if exported_macro_type {
1289            let mut cursor = node.walk();
1290            let errors_before_declarator = node
1291                .named_children(&mut cursor)
1292                .filter(|child| {
1293                    child.kind() == "ERROR"
1294                        && child.start_byte() >= type_node.end_byte()
1295                        && child.end_byte() <= declarator.start_byte()
1296                })
1297                .collect::<Vec<_>>();
1298            if let Some(name) = errors_before_declarator
1299                .iter()
1300                .find_map(|error| displaced_exported_class_name(*error, source))
1301            {
1302                let raw_supertypes = errors_before_declarator
1303                    .iter()
1304                    .any(|error| malformed_inheritance_syntax(*error))
1305                    .then(|| recovered_malformed_base_name(declarator, source))
1306                    .flatten()
1307                    .map(|base| vec![base]);
1308                return Some((node, name, raw_supertypes));
1309            }
1310            if errors_before_declarator
1311                .iter()
1312                .any(|error| malformed_inheritance_syntax(*error))
1313            {
1314                return None;
1315            }
1316        }
1317        if !exported_macro_type
1318            && let Some(name) = type_name
1319            && !cpp_export_macro_token(&name)
1320            && let Some(base) =
1321                recovered_postfix_export_macro_base(node, type_node, declarator, source)
1322        {
1323            return Some((node, name, Some(vec![base])));
1324        }
1325        if let Some(name) = direct_identifier_name(declarator, source)
1326            && exported_macro_type
1327            && !cpp_export_macro_token(&name)
1328        {
1329            let raw_supertypes = exported_macro_type
1330                .then(|| recovered_single_base_after_declarator(node, declarator, source))
1331                .flatten()
1332                .map(|base| vec![base]);
1333            return Some((node, name, raw_supertypes));
1334        }
1335        if declarator.kind() == "parenthesized_declarator"
1336            && type_node
1337                .child_by_field_name("name")
1338                .and_then(|name| direct_identifier_name(name, source))
1339                .is_some_and(|name| cpp_export_macro_token(&name))
1340        {
1341            let body_start = node
1342                .child_by_field_name("body")
1343                .map(|body| body.start_byte())
1344                .unwrap_or(node.end_byte());
1345            let mut cursor = node.walk();
1346            if let Some(name) = node
1347                .named_children(&mut cursor)
1348                .filter(|child| {
1349                    child.kind() == "ERROR"
1350                        && child.start_byte() >= declarator.end_byte()
1351                        && child.end_byte() <= body_start
1352                })
1353                .find_map(|error| declarator_name_from_node(error, source))
1354            {
1355                return Some((node, name, None));
1356            }
1357        }
1358    }
1359
1360    let declarator_text = direct_identifier_name(declarator, source)?;
1361    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1362        return None;
1363    }
1364    class_identifier_before_body(node, source).map(|name| (node, name, None))
1365}
1366
1367/// Recover the class item from a region reparse that still carries the
1368/// sentinel's synthetic function envelope.  An unknown class attribute can
1369/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
1370/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
1371/// then nested below that function, so direct class-child lookup is not enough.
1372struct CppSentinelReparsedClass<'tree> {
1373    declaration_node: Node<'tree>,
1374    name: String,
1375    body: Node<'tree>,
1376    raw_supertypes: Option<Vec<String>>,
1377}
1378
1379fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1380    let mut cursor = root.walk();
1381    root.named_children(&mut cursor)
1382        .find(|child| child.kind() != "comment")
1383        .filter(|child| child.kind() == "template_declaration")
1384}
1385
1386fn cpp_sentinel_reparsed_class<'tree>(
1387    root: Node<'tree>,
1388    template_node: Option<Node<'tree>>,
1389    source: &str,
1390) -> Option<CppSentinelReparsedClass<'tree>> {
1391    let container = template_node.unwrap_or(root);
1392    let mut cursor = container.walk();
1393    for child in container.named_children(&mut cursor) {
1394        if matches!(
1395            child.kind(),
1396            "class_specifier" | "struct_specifier" | "union_specifier"
1397        ) {
1398            let name = class_like_name(child, source)?;
1399            let body = cpp_body_node(child)?;
1400            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1401                .then(|| extract_cpp_supertypes(child, source));
1402            return Some(CppSentinelReparsedClass {
1403                declaration_node: child,
1404                name,
1405                body,
1406                raw_supertypes,
1407            });
1408        }
1409        if child.kind() == "declaration"
1410            && let Some(class_node) = first_class_like_child(child)
1411        {
1412            let name = class_like_name(class_node, source)?;
1413            let body = cpp_body_node(class_node)?;
1414            let raw_supertypes =
1415                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1416                    .then(|| extract_cpp_supertypes(class_node, source));
1417            return Some(CppSentinelReparsedClass {
1418                declaration_node: class_node,
1419                name,
1420                body,
1421                raw_supertypes,
1422            });
1423        }
1424        // Only when the nested class item carries its own body. A bodyless
1425        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
1426        // a function definition -- is the export-macro shape recovered by the
1427        // next arm, and must fall through to it rather than abort the search.
1428        if child.kind() == "function_definition"
1429            && let Some(class_node) = first_class_like_child(child)
1430            && let Some(body) = cpp_body_node(class_node)
1431            && let Some(name) = class_like_name(class_node, source)
1432        {
1433            let raw_supertypes =
1434                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1435                    .then(|| extract_cpp_supertypes(class_node, source));
1436            return Some(CppSentinelReparsedClass {
1437                declaration_node: class_node,
1438                name,
1439                body,
1440                raw_supertypes,
1441            });
1442        }
1443        if child.kind() == "function_definition"
1444            && let Some((_, name, raw_supertypes)) =
1445                recover_exported_class_function_definition(child, source)
1446        {
1447            let body = cpp_body_node(child)?;
1448            return Some(CppSentinelReparsedClass {
1449                declaration_node: child,
1450                name,
1451                body,
1452                raw_supertypes,
1453            });
1454        }
1455    }
1456    None
1457}
1458
1459fn recovered_postfix_export_macro_base(
1460    node: Node<'_>,
1461    type_node: Node<'_>,
1462    declarator: Node<'_>,
1463    source: &str,
1464) -> Option<String> {
1465    let mut cursor = node.walk();
1466    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
1467        child.kind() == "ERROR"
1468            && child.start_byte() >= type_node.end_byte()
1469            && child.end_byte() <= declarator.start_byte()
1470            && postfix_export_macro_inheritance(*child, source)
1471    });
1472    malformed_clauses.next()?;
1473    if malformed_clauses.next().is_some() {
1474        return None;
1475    }
1476    recovered_malformed_base_name(declarator, source)
1477}
1478
1479fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
1480    let mut macro_count = 0;
1481    let mut colon_count = 0;
1482    let mut access_count = 0;
1483    for index in 0..node.child_count() {
1484        let Some(child) = node.child(index) else {
1485            return false;
1486        };
1487        match child.kind() {
1488            "identifier" | "type_identifier" if child.is_named() => {
1489                let candidate = normalize_cpp_whitespace(node_text(child, source));
1490                if !cpp_export_macro_token(&candidate) {
1491                    return false;
1492                }
1493                macro_count += 1;
1494            }
1495            ":" if !child.is_named() => colon_count += 1,
1496            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1497            _ => return false,
1498        }
1499    }
1500    macro_count == 1 && colon_count == 1 && access_count == 1
1501}
1502
1503fn recovered_single_base_after_declarator(
1504    node: Node<'_>,
1505    declarator: Node<'_>,
1506    source: &str,
1507) -> Option<String> {
1508    let body_start = node
1509        .child_by_field_name("body")
1510        .map(|body| body.start_byte())
1511        .unwrap_or(node.end_byte());
1512    let mut cursor = node.walk();
1513    let mut bases = node
1514        .named_children(&mut cursor)
1515        .filter(|child| {
1516            child.kind() == "ERROR"
1517                && child.start_byte() >= declarator.end_byte()
1518                && child.end_byte() <= body_start
1519        })
1520        .filter_map(|error| displaced_exported_class_name(error, source));
1521    let base = bases.next()?;
1522    bases.next().is_none().then_some(base)
1523}
1524
1525fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
1526    (0..node.child_count()).any(|index| {
1527        node.child(index)
1528            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
1529    })
1530}
1531
1532pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
1533    recover_exported_class_function_definition(node, source).is_some()
1534}
1535
1536fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
1537    matches!(
1538        kind,
1539        "ERROR"
1540            | "preproc_if"
1541            | "preproc_ifdef"
1542            | "preproc_ifndef"
1543            | "preproc_else"
1544            | "preproc_elif"
1545    ) || (kind == "labeled_statement" && in_class_scope)
1546}
1547
1548pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
1549    if node.kind() != "declaration" {
1550        return false;
1551    }
1552    let mut ancestor = node.parent();
1553    while let Some(container) = ancestor {
1554        match container.kind() {
1555            "compound_statement" => {
1556                return container.parent().is_some_and(|class_container| {
1557                    is_recovered_exported_class_container(class_container, source)
1558                });
1559            }
1560            // These containers preserve ScopeInfo in visit_node. declaration_list is
1561            // the body container selected for a linkage specification.
1562            "template_declaration" | "linkage_specification" | "declaration_list" => {}
1563            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
1564            _ => return false,
1565        }
1566        ancestor = container.parent();
1567    }
1568    false
1569}
1570
1571pub fn recovered_exported_class_has_body(
1572    node: Node<'_>,
1573    source: &str,
1574    expected_name: &str,
1575) -> Option<bool> {
1576    match node.kind() {
1577        "function_definition" => {
1578            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
1579            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
1580        }
1581        "declaration" | "field_declaration" => {
1582            let recovered = recover_exported_class_declaration(node, source)?;
1583            (recovered.name == expected_name).then(|| recovered.body.is_some())
1584        }
1585        _ => None,
1586    }
1587}
1588
1589fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
1590    let body_start = node
1591        .child_by_field_name("body")
1592        .map(|body| body.start_byte())
1593        .unwrap_or(node.end_byte());
1594    let mut stack = Vec::new();
1595    for index in (0..node.named_child_count()).rev() {
1596        let Some(child) = node.named_child(index) else {
1597            continue;
1598        };
1599        if child.start_byte() >= body_start {
1600            continue;
1601        }
1602        stack.push(child);
1603    }
1604
1605    let mut best = None;
1606    while let Some(current) = stack.pop() {
1607        if matches!(current.kind(), "identifier" | "type_identifier") {
1608            let name = normalize_cpp_whitespace(node_text(current, source));
1609            if !name.is_empty()
1610                && !cpp_export_macro_token(&name)
1611                && !matches!(name.as_str(), "class" | "struct" | "union")
1612            {
1613                best = Some(name);
1614            }
1615            continue;
1616        }
1617
1618        for index in (0..current.named_child_count()).rev() {
1619            if let Some(child) = current.named_child(index)
1620                && child.start_byte() < body_start
1621            {
1622                stack.push(child);
1623            }
1624        }
1625    }
1626    best
1627}
1628
1629fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1630    if node.kind() == "declaration"
1631        && node
1632            .child_by_field_name("type")
1633            .or_else(|| first_class_like_child(node))
1634            .is_some_and(|type_node| {
1635                matches!(
1636                    type_node.kind(),
1637                    "class_specifier" | "struct_specifier" | "union_specifier"
1638                )
1639            })
1640        && let Some(name) = node
1641            .child_by_field_name("declarator")
1642            .and_then(|declarator| declarator_name_from_node(declarator, source))
1643        && !cpp_export_macro_token(&name)
1644    {
1645        return Some(name);
1646    }
1647
1648    if node.kind() == "function_definition"
1649        && node.child_by_field_name("type").is_some_and(|type_node| {
1650            matches!(
1651                type_node.kind(),
1652                "class_specifier" | "struct_specifier" | "union_specifier"
1653            )
1654        })
1655        && let Some(name) = node
1656            .child_by_field_name("declarator")
1657            .and_then(|declarator| direct_identifier_name(declarator, source))
1658        && !cpp_export_macro_token(&name)
1659    {
1660        return Some(name);
1661    }
1662
1663    let class_node = if matches!(
1664        node.kind(),
1665        "class_specifier" | "struct_specifier" | "union_specifier"
1666    ) {
1667        node
1668    } else {
1669        first_class_like_child(node)?
1670    };
1671    class_like_name_from_children(class_node, source)
1672}
1673
1674fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
1675    if !matches!(
1676        node.kind(),
1677        "identifier" | "field_identifier" | "type_identifier"
1678    ) {
1679        return None;
1680    }
1681    let name = normalize_cpp_whitespace(node_text(node, source));
1682    (!name.is_empty()).then_some(name)
1683}
1684
1685fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1686    match node.kind() {
1687        "identifier" | "field_identifier" | "type_identifier" => {
1688            let name = normalize_cpp_whitespace(node_text(node, source));
1689            (!name.is_empty()).then_some(name)
1690        }
1691        _ => {
1692            let mut cursor = node.walk();
1693            node.named_children(&mut cursor)
1694                .find_map(|child| declarator_name_from_node(child, source))
1695        }
1696    }
1697}
1698
1699fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
1700    let mut cursor = node.walk();
1701    node.named_children(&mut cursor).find(|child| {
1702        matches!(
1703            child.kind(),
1704            "class_specifier" | "struct_specifier" | "union_specifier"
1705        )
1706    })
1707}
1708
1709/// Push a container's children as a `Siblings` cursor rather than snapshotting
1710/// them all with one shared scope: children are visited one at a time so a
1711/// `using namespace X;` sibling can affect the scope threaded to the siblings
1712/// that textually follow it (issue #1093).
1713fn push_cpp_container_work<'tree>(
1714    node: Node<'tree>,
1715    scope: ScopeInfo,
1716    stack: &mut Vec<CppWork<'tree>>,
1717) {
1718    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
1719}
1720
1721/// Materialize one selected named-child range with a tree-sitter cursor. The
1722/// cursor advances linearly across the parent's concrete children; repeatedly
1723/// asking for `named_child(index)` is quadratic on very wide generated nodes.
1724fn push_cpp_sibling_range<'tree>(
1725    parent: Node<'tree>,
1726    start_index: usize,
1727    end_index: usize,
1728    scope: ScopeInfo,
1729    stack: &mut Vec<CppWork<'tree>>,
1730) {
1731    let mut cursor = parent.walk();
1732    let children = parent
1733        .named_children(&mut cursor)
1734        .skip(start_index)
1735        .take(end_index.saturating_sub(start_index))
1736        .collect::<Vec<_>>()
1737        .into_iter();
1738    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
1739}
1740
1741/// Advance a `Siblings` cursor by one child: dispatch the current child under
1742/// the scope accumulated from its *earlier* siblings, then push a
1743/// continuation for the remaining siblings carrying the scope updated for
1744/// *this* child (only `using namespace X;` directives change it). Pushing the
1745/// continuation before the current child's own node work means the current
1746/// child's subtree fully drains (LIFO) before the next sibling is visited,
1747/// preserving left-to-right order.
1748fn advance_cpp_siblings<'tree>(
1749    mut siblings: CppSiblingsWork<'tree>,
1750    source: &str,
1751    stack: &mut Vec<CppWork<'tree>>,
1752) {
1753    let Some(child) = siblings.children.next() else {
1754        return;
1755    };
1756    let current_scope = siblings.scope.clone();
1757    if let Some(namespace) = cpp_using_namespace_target(child, source) {
1758        siblings.scope.visible_using_namespaces.push(namespace);
1759    }
1760    if !siblings.children.as_slice().is_empty() {
1761        stack.push(CppWork::Siblings(siblings));
1762    }
1763    stack.push(CppWork::Node(CppNodeWork {
1764        node: child,
1765        scope: current_scope,
1766    }));
1767}
1768
1769/// The namespace target of a `using namespace X;` directive, or `None` for
1770/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
1771/// kind. Distinguished structurally by the presence of the grammar's literal
1772/// `namespace` keyword token among the node's children -- not by inspecting
1773/// source text -- so it never misreads a member-importing using-declaration
1774/// as a namespace directive.
1775fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
1776    if node.kind() != "using_declaration" {
1777        return None;
1778    }
1779    let mut cursor = node.walk();
1780    let is_namespace_directive = node
1781        .children(&mut cursor)
1782        .any(|child| child.kind() == "namespace");
1783    if !is_namespace_directive {
1784        return None;
1785    }
1786    let target = node.named_child(0)?;
1787    let text = normalize_cpp_whitespace(node_text(target, source));
1788    (!text.is_empty()).then_some(text)
1789}
1790
1791/// Every `using namespace X;` directive target in a file, in source order, for
1792/// resolution-time consumers that need the file's using-directives without the
1793/// per-position scope threading extraction does. Parses `source` fresh and
1794/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
1795/// keys on the grammar's `namespace` keyword token, not source text), so it
1796/// never misreads a member-importing `using X::Y;` as a namespace directive.
1797///
1798/// This is a whole-file over-approximation of what is in scope at any one point
1799/// (a directive nested inside a `namespace {}` block or a function body is still
1800/// reported), which is exactly what the #1134 identity reconciler wants: extra
1801/// candidate namespaces that no visible class confirms are harmless, and two
1802/// that both confirm are treated as a genuine ambiguity by the reconciler.
1803pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
1804    let mut parser = Parser::new();
1805    if parser
1806        .set_language(&tree_sitter_cpp::LANGUAGE.into())
1807        .is_err()
1808    {
1809        return Vec::new();
1810    }
1811    let Some(tree) = parser.parse(source, None) else {
1812        return Vec::new();
1813    };
1814    let mut namespaces = Vec::new();
1815    let mut seen = std::collections::HashSet::new();
1816    let mut stack = vec![tree.root_node()];
1817    while let Some(node) = stack.pop() {
1818        if let Some(namespace) = cpp_using_namespace_target(node, source)
1819            && seen.insert(namespace.clone())
1820        {
1821            namespaces.push(namespace);
1822        }
1823        let mut cursor = node.walk();
1824        stack.extend(node.named_children(&mut cursor));
1825    }
1826    namespaces
1827}
1828
1829pub struct CppVisitor<'a> {
1830    pub file: &'a ProjectFile,
1831    pub source: &'a str,
1832    pub parsed: &'a mut ParsedFile,
1833    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
1834    /// Byte regions whose contents were re-owned by a fragmented export-class
1835    /// recovery (#938): the scattered members between the fragmented
1836    /// declaration and its displaced closing brace are indexed as members of
1837    /// the recovered class by the region reparse, so the ordinary sibling walk
1838    /// must not ALSO index them as top-level declarations (that double-indexing
1839    /// made a scattered nested class ambiguous between `Inner` and
1840    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
1841    /// linear scan at visit time is fine.
1842    pub consumed_fragment_regions: Vec<(usize, usize)>,
1843}
1844
1845impl<'a> CppVisitor<'a> {
1846    #[allow(clippy::too_many_arguments)]
1847    pub fn visit_container(
1848        &mut self,
1849        node: Node<'_>,
1850        package_name: &str,
1851        module: Option<CodeUnit>,
1852        class_unit: Option<CodeUnit>,
1853        template_signature: Option<String>,
1854        visible_using_namespaces: Vec<String>,
1855    ) {
1856        let scope = ScopeInfo {
1857            package_name: package_name.to_string(),
1858            module,
1859            class_unit,
1860            template_signature,
1861            template_metadata: None,
1862            declarations_are_fields: false,
1863            recovered_specialization_member_scope: false,
1864            visible_using_namespaces,
1865        };
1866        self.run_container_work(node, scope);
1867    }
1868
1869    /// Whether a work node lies entirely inside a byte region consumed by a
1870    /// fragmented export-class recovery (#938); such nodes were already indexed
1871    /// as members of the recovered class by the region reparse.
1872    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
1873        self.consumed_fragment_regions
1874            .iter()
1875            .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
1876    }
1877
1878    /// Drive the container work loop from an explicit seed scope to completion. The
1879    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
1880    /// stays alive for the whole traversal.
1881    fn run_container_work<'tree>(&mut self, node: Node<'tree>, scope: ScopeInfo) {
1882        let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
1883        while let Some(work) = stack.pop() {
1884            match work {
1885                CppWork::Container(container) => {
1886                    push_cpp_container_work(container.node, container.scope, &mut stack);
1887                }
1888                CppWork::Siblings(siblings) => {
1889                    advance_cpp_siblings(siblings, self.source, &mut stack);
1890                }
1891                CppWork::Node(work) => {
1892                    if self.node_is_inside_consumed_fragment(work.node) {
1893                        continue;
1894                    }
1895                    self.visit_node(work.node, &work.scope, &mut stack);
1896                }
1897            }
1898        }
1899    }
1900
1901    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
1902    /// it only when the entire region is member-shaped. This validation must happen
1903    /// before registering the recovered class because a rejected speculative range
1904    /// must not leak into the ordinary recovery path.
1905    fn reparse_fragmented_export_class_members(
1906        &self,
1907        fragmented: &FragmentedExportBody,
1908        class_name: &str,
1909    ) -> Option<FragmentedExportMembers> {
1910        if fragmented.reparse_start >= fragmented.reparse_end {
1911            return None;
1912        }
1913        let tree = cpp_reparse_fragmented_class_body(
1914            self.source,
1915            fragmented.reparse_start,
1916            fragmented.reparse_end,
1917        )?;
1918        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
1919            return Some(FragmentedExportMembers::Complete(tree));
1920        }
1921        let has_conditional_constructor = {
1922            let root = tree.root_node();
1923            let mut cursor = root.walk();
1924            root.named_children(&mut cursor).any(|child| {
1925                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
1926            })
1927        };
1928        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
1929    }
1930
1931    /// Index an already validated fragmented body as members of `class_unit`. The
1932    /// region reparse keeps each member's exact original byte and line positions.
1933    fn visit_fragmented_export_class_members(
1934        &mut self,
1935        outcome: FragmentedExportMembers,
1936        class_unit: CodeUnit,
1937        scope: &ScopeInfo,
1938    ) -> bool {
1939        let (tree, complete) = match outcome {
1940            FragmentedExportMembers::Complete(tree) => (tree, true),
1941            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
1942        };
1943        let root = tree.root_node();
1944        let class_name = class_unit.identifier().to_string();
1945        let member_scope = ScopeInfo {
1946            // A recovered export-macro class may borrow its namespace from an
1947            // earlier forward declaration even when the malformed node itself
1948            // sits at file scope. Use the recovered class identity as the
1949            // authoritative package for reparsed members as well.
1950            package_name: class_unit.package_name().to_string(),
1951            module: scope.module.clone(),
1952            class_unit: Some(class_unit),
1953            template_signature: scope.template_signature.clone(),
1954            template_metadata: None,
1955            declarations_are_fields: true,
1956            recovered_specialization_member_scope: false,
1957            visible_using_namespaces: scope.visible_using_namespaces.clone(),
1958        };
1959        if !complete {
1960            // A conditional beginning immediately after an access label can
1961            // fragment one constructor declaration while leaving the rest of
1962            // the class body as unsafe statement soup. Recover only that
1963            // structurally proven constructor and leave the outer-tree
1964            // siblings unconsumed for their ordinary walk.
1965            let mut cursor = root.walk();
1966            let constructors = root
1967                .named_children(&mut cursor)
1968                .filter_map(|child| {
1969                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
1970                })
1971                .collect::<Vec<_>>();
1972            for constructor in constructors {
1973                let mut stack = Vec::new();
1974                self.visit_node(constructor, &member_scope, &mut stack);
1975                while let Some(work) = stack.pop() {
1976                    match work {
1977                        CppWork::Container(container) => {
1978                            push_cpp_container_work(container.node, container.scope, &mut stack);
1979                        }
1980                        CppWork::Siblings(siblings) => {
1981                            advance_cpp_siblings(siblings, self.source, &mut stack);
1982                        }
1983                        CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
1984                    }
1985                }
1986            }
1987            return false;
1988        }
1989        self.run_container_work(root, member_scope);
1990        true
1991    }
1992
1993    fn visit_recovered_fragment_constructor(
1994        &mut self,
1995        range: std::ops::Range<usize>,
1996        constructor_body: Node<'_>,
1997        class_declaration: Node<'_>,
1998        class_unit: &CodeUnit,
1999        scope: &ScopeInfo,
2000    ) {
2001        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
2002            return;
2003        };
2004        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
2005            tree.root_node(),
2006            range.start,
2007            class_unit.identifier(),
2008            self.source,
2009        ) else {
2010            return;
2011        };
2012        let member_scope = ScopeInfo {
2013            package_name: class_unit.package_name().to_string(),
2014            module: scope.module.clone(),
2015            class_unit: Some(class_unit.clone()),
2016            template_signature: scope.template_signature.clone(),
2017            template_metadata: None,
2018            declarations_are_fields: true,
2019            recovered_specialization_member_scope: false,
2020            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2021        };
2022        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
2023        else {
2024            return;
2025        };
2026        debug_assert_eq!(function.name, class_unit.identifier());
2027        let code_unit = function.code_unit(self.file.clone());
2028        self.parsed.add_code_unit_with_range(
2029            code_unit.clone(),
2030            Range {
2031                start_byte: function_declarator.start_byte(),
2032                end_byte: constructor_body.end_byte(),
2033                start_line: function_declarator.start_position().row + 1,
2034                end_line: constructor_body.end_position().row + 1,
2035            },
2036            None,
2037            None,
2038        );
2039        self.parsed.add_signature_with_metadata(
2040            code_unit.clone(),
2041            cpp_signature_metadata(
2042                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
2043                function_declarator,
2044                self.source,
2045            )
2046            .with_declaration_only(false)
2047            .with_callable_linkage(cpp_callable_linkage(class_declaration, self.source)),
2048        );
2049        self.parsed.add_child(class_unit.clone(), code_unit);
2050    }
2051
2052    fn visit_recovered_fragment_prefix_members(
2053        &mut self,
2054        root: Node<'_>,
2055        constructor_start: usize,
2056        class_unit: &CodeUnit,
2057        scope: &ScopeInfo,
2058    ) {
2059        let member_scope = ScopeInfo {
2060            package_name: class_unit.package_name().to_string(),
2061            module: scope.module.clone(),
2062            class_unit: Some(class_unit.clone()),
2063            template_signature: scope.template_signature.clone(),
2064            template_metadata: None,
2065            declarations_are_fields: true,
2066            recovered_specialization_member_scope: false,
2067            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2068        };
2069        let mut stack = vec![root];
2070        while let Some(current) = stack.pop() {
2071            if current.kind() == "comment" || current.start_byte() >= constructor_start {
2072                continue;
2073            }
2074            if current.end_byte() <= constructor_start
2075                && current.kind() != "translation_unit"
2076                && current.kind() != "labeled_statement"
2077                && current.kind() != "ERROR"
2078            {
2079                let mut work_stack = Vec::new();
2080                self.visit_node(current, &member_scope, &mut work_stack);
2081                while let Some(work) = work_stack.pop() {
2082                    match work {
2083                        CppWork::Container(container) => {
2084                            push_cpp_container_work(
2085                                container.node,
2086                                container.scope,
2087                                &mut work_stack,
2088                            );
2089                        }
2090                        CppWork::Siblings(siblings) => {
2091                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
2092                        }
2093                        CppWork::Node(work) => {
2094                            self.visit_node(work.node, &work.scope, &mut work_stack)
2095                        }
2096                    }
2097                }
2098                continue;
2099            }
2100            if matches!(
2101                current.kind(),
2102                "translation_unit" | "labeled_statement" | "ERROR"
2103            ) {
2104                let mut cursor = current.walk();
2105                stack.extend(current.named_children(&mut cursor));
2106            }
2107        }
2108    }
2109
2110    fn visit_node<'tree>(
2111        &mut self,
2112        node: Node<'tree>,
2113        scope: &ScopeInfo,
2114        stack: &mut Vec<CppWork<'tree>>,
2115    ) {
2116        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
2117            self.visit_node(node, &recovered_scope, stack);
2118            return;
2119        }
2120        if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
2121        {
2122            let displaced_namespace_items =
2123                displaced_fragment_namespace_geometry(node, self.source)
2124                    .map(|boundary| boundary.namespace_items)
2125                    .unwrap_or_default();
2126            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
2127            let mut class_stack = Vec::new();
2128            // When the full body cannot be safely reparsed, the original class
2129            // node still proves ownership for its parser-visible prefix.
2130            let parser_visible_body =
2131                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
2132                    .then(|| cpp_body_node(class_node))
2133                    .flatten();
2134            let class_unit = self.visit_named_class_like_shape(
2135                class_node,
2136                name,
2137                parser_visible_body,
2138                true,
2139                Some(fragmented.class_range),
2140                Some(extract_cpp_supertypes(class_node, self.source)),
2141                scope,
2142                &mut class_stack,
2143            );
2144            let member_scope = ScopeInfo {
2145                package_name: class_unit.package_name().to_string(),
2146                module: scope.module.clone(),
2147                class_unit: Some(class_unit.clone()),
2148                template_signature: scope.template_signature.clone(),
2149                template_metadata: None,
2150                declarations_are_fields: true,
2151                recovered_specialization_member_scope: false,
2152                visible_using_namespaces: scope.visible_using_namespaces.clone(),
2153            };
2154            let complete = outcome.is_some_and(|outcome| {
2155                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
2156            });
2157            if complete {
2158                self.consumed_fragment_regions
2159                    .push((node.start_byte(), fragmented.class_range.end_byte));
2160            } else {
2161                // A macro-constrained member can make the full body reparse
2162                // unsafe while tree-sitter still exposes later class members
2163                // as bounded siblings up to the displaced `}`/`;`. Keep the
2164                // structurally proven class/base declaration and re-own those
2165                // sibling nodes under it. They retain their original parser
2166                // nodes and exact ranges; the close boundary comes solely from
2167                // `fragmented_plain_class_body`.
2168                // Template wrappers put the escaped members beside the
2169                // template rather than beside its malformed declaration.
2170                for candidate in cpp_following_named_siblings(node, self.source) {
2171                    if candidate.start_byte() >= fragmented.reparse_end {
2172                        break;
2173                    }
2174                    if cpp_fragment_sibling_is_class_member(
2175                        candidate,
2176                        fragmented.reparse_end,
2177                        self.source,
2178                    ) {
2179                        self.recovered_class_sibling_scopes
2180                            .insert(candidate.id(), member_scope.clone());
2181                    }
2182                }
2183            }
2184            for item in displaced_namespace_items {
2185                self.recovered_class_sibling_scopes
2186                    .insert(item.id(), scope.clone());
2187            }
2188            stack.extend(class_stack);
2189            return;
2190        }
2191        match node.kind() {
2192            "template_declaration" => {
2193                if let Some(recovered) = recover_fragmented_preprocessor_class(node, self.source) {
2194                    let mut template_scope = scope.clone();
2195                    template_scope.template_signature =
2196                        cpp_template_signature(node, recovered.declaration_node, self.source);
2197                    template_scope.template_metadata =
2198                        cpp_template_metadata(node, recovered.class_node, self.source);
2199                    let raw_supertypes =
2200                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
2201                    let mut class_stack = Vec::new();
2202                    let class_unit = self.visit_named_class_like_shape(
2203                        recovered.class_node,
2204                        recovered.name,
2205                        Some(recovered.body),
2206                        true,
2207                        Some(recovered.range),
2208                        raw_supertypes,
2209                        &template_scope,
2210                        &mut class_stack,
2211                    );
2212                    self.parsed.record_materialization(
2213                        MaterializationRecord::RecoveredDeclaration {
2214                            recovery: recovered.range,
2215                            unit: class_unit.clone(),
2216                        },
2217                    );
2218                    let member_scope = ScopeInfo {
2219                        package_name: template_scope.package_name.clone(),
2220                        module: template_scope.module.clone(),
2221                        class_unit: Some(class_unit.clone()),
2222                        template_signature: template_scope.template_signature.clone(),
2223                        template_metadata: None,
2224                        declarations_are_fields: true,
2225                        recovered_specialization_member_scope: recovered
2226                            .class_node
2227                            .child_by_field_name("name")
2228                            .is_some_and(|name| name.kind() == "template_type"),
2229                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
2230                    };
2231                    for tail_member in recovered.tail_members.into_iter().rev() {
2232                        stack.push(CppWork::Node(CppNodeWork {
2233                            node: tail_member,
2234                            scope: member_scope.clone(),
2235                        }));
2236                    }
2237                    stack.extend(class_stack);
2238                    for sibling in recovered.member_siblings {
2239                        self.recovered_class_sibling_scopes
2240                            .insert(sibling.id(), member_scope.clone());
2241                    }
2242                    return;
2243                }
2244                for index in (0..node.named_child_count()).rev() {
2245                    let Some(child) = node.named_child(index) else {
2246                        continue;
2247                    };
2248                    if matches!(
2249                        child.kind(),
2250                        "class_specifier"
2251                            | "struct_specifier"
2252                            | "union_specifier"
2253                            | "enum_specifier"
2254                            | "function_definition"
2255                            | "declaration"
2256                            | "field_declaration"
2257                            | "alias_declaration"
2258                            | "namespace_definition"
2259                    ) {
2260                        let mut template_scope = scope.clone();
2261                        template_scope.template_signature =
2262                            cpp_template_signature(node, child, self.source);
2263                        template_scope.template_metadata =
2264                            cpp_template_metadata(node, child, self.source);
2265                        if let Some(recovered) =
2266                            recover_fragmented_partial_specialization(node, child, self.source)
2267                        {
2268                            let code_unit = self.visit_named_class_like_shape(
2269                                recovered.declaration_node,
2270                                recovered.name,
2271                                None,
2272                                true,
2273                                Some(recovered.range),
2274                                None,
2275                                &template_scope,
2276                                stack,
2277                            );
2278                            self.parsed.record_materialization(
2279                                MaterializationRecord::RecoveredDeclaration {
2280                                    recovery: recovered.range,
2281                                    unit: code_unit.clone(),
2282                                },
2283                            );
2284                            let mut member_scope = template_scope.clone();
2285                            member_scope.class_unit = Some(code_unit);
2286                            member_scope.declarations_are_fields = true;
2287                            member_scope.recovered_specialization_member_scope = true;
2288                            for prefix_member in recovered.prefix_members.into_iter().rev() {
2289                                stack.push(CppWork::Node(CppNodeWork {
2290                                    node: prefix_member,
2291                                    scope: member_scope.clone(),
2292                                }));
2293                            }
2294                            for sibling in recovered.member_siblings {
2295                                self.recovered_class_sibling_scopes
2296                                    .insert(sibling.id(), member_scope.clone());
2297                            }
2298                            for following in recovered.following_declarations.into_iter().rev() {
2299                                stack.push(CppWork::Node(CppNodeWork {
2300                                    node: following,
2301                                    scope: scope.clone(),
2302                                }));
2303                            }
2304                            return;
2305                        }
2306                        stack.push(CppWork::Node(CppNodeWork {
2307                            node: child,
2308                            scope: template_scope,
2309                        }));
2310                    }
2311                }
2312            }
2313            "namespace_definition" => self.visit_namespace(node, scope, stack),
2314            "linkage_specification" => {
2315                if let Some(body) = cpp_body_node(node) {
2316                    stack.push(CppWork::Container(CppContainer {
2317                        node: body,
2318                        scope: scope.clone(),
2319                    }));
2320                } else {
2321                    stack.push(CppWork::Container(CppContainer {
2322                        node,
2323                        scope: scope.clone(),
2324                    }));
2325                }
2326            }
2327            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
2328                self.visit_class_like(node, scope, stack)
2329            }
2330            "function_definition" => self.visit_function_definition(node, scope, stack),
2331            // A bare namespace-begin sentinel can make tree-sitter promote the
2332            // wrapped declaration to an ERROR node instead of the usual bogus
2333            // function_definition envelope. Keep the recovery entry point on
2334            // the same structured path for both shapes; ordinary ERROR nodes
2335            // retain their declaration-preserving wrapper traversal when the
2336            // sentinel predicate does not match.
2337            "ERROR" => {
2338                if !self.visit_sentinel_macro_region(node, scope, stack) {
2339                    self.visit_macro_swallowed_function_declarations(node, scope);
2340                    stack.push(CppWork::Container(CppContainer {
2341                        node,
2342                        scope: scope.clone(),
2343                    }));
2344                }
2345            }
2346            "declaration" => {
2347                if scope.class_unit.is_some()
2348                    && scope.declarations_are_fields
2349                    && scope.recovered_specialization_member_scope
2350                    && let Some(alias_name) =
2351                        recovered_using_declaration_alias_name(node, self.source)
2352                {
2353                    self.add_type_aliases(node, scope, vec![alias_name]);
2354                } else {
2355                    self.visit_declaration(node, scope, scope.declarations_are_fields, stack)
2356                }
2357            }
2358            "field_declaration" => self.visit_declaration(node, scope, true, stack),
2359            "type_definition" | "alias_declaration" => {
2360                self.visit_type_declaration(node, scope, stack)
2361            }
2362            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
2363            "preproc_include" => self.visit_include(node),
2364            kind if preserves_declaration_scope_through_wrapper(
2365                kind,
2366                scope.class_unit.is_some(),
2367            ) =>
2368            {
2369                // A preprocessor conditional gates every declaration inside it
2370                // on a configuration this analyzer never evaluates; record the
2371                // interval so declaration state can say so (issue #1476). The
2372                // else/elif branches are children of the `preproc_if` node, so
2373                // recording the openers covers every branch.
2374                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
2375                    let mut range = cpp_declaration_range(node);
2376                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
2377                        range.end_byte = boundary.end_byte;
2378                        range.end_line = boundary.end_line;
2379                    }
2380                    self.parsed.record_materialization(
2381                        MaterializationRecord::ConfigurationConditional { range },
2382                    );
2383                }
2384                stack.push(CppWork::Container(CppContainer {
2385                    node,
2386                    scope: scope.clone(),
2387                }))
2388            }
2389            _ => {}
2390        }
2391    }
2392
2393    fn visit_macro_swallowed_function_declarations(
2394        &mut self,
2395        envelope: Node<'_>,
2396        scope: &ScopeInfo,
2397    ) {
2398        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
2399            || envelope.kind() == "ERROR"
2400                && envelope
2401                    .parent()
2402                    .is_some_and(|parent| parent.kind() == "ERROR")
2403        {
2404            return;
2405        }
2406        let mut stack = (0..envelope.named_child_count())
2407            .filter_map(|index| envelope.named_child(index))
2408            .collect::<Vec<_>>();
2409        while let Some(node) = stack.pop() {
2410            if node.kind() == "function_declarator" {
2411                self.visit_error_swallowed_function_declaration(node, scope);
2412            }
2413            for index in 0..node.named_child_count() {
2414                if let Some(child) = node.named_child(index) {
2415                    stack.push(child);
2416                }
2417            }
2418        }
2419    }
2420
2421    fn visit_error_swallowed_function_declaration(
2422        &mut self,
2423        node: Node<'_>,
2424        scope: &ScopeInfo,
2425    ) -> bool {
2426        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
2427            return false;
2428        };
2429        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
2430            return false;
2431        };
2432        let root = tree.root_node();
2433        let mut cursor = root.walk();
2434        let declarations = root
2435            .named_children(&mut cursor)
2436            .filter(|child| child.kind() != "comment")
2437            .collect::<Vec<_>>();
2438        let [declaration] = declarations.as_slice() else {
2439            return false;
2440        };
2441        if declaration.kind() != "declaration"
2442            || declaration.has_error()
2443            || declaration.start_byte() != start
2444            || declaration.end_byte() != end
2445        {
2446            return false;
2447        }
2448        let recovery = cpp_recovery_window(self.source, start, end);
2449        self.record_recovered_declarations(recovery, |visitor| {
2450            visitor.run_container_work(root, scope.clone());
2451        });
2452        true
2453    }
2454
2455    fn visit_namespace<'tree>(
2456        &mut self,
2457        node: Node<'tree>,
2458        scope: &ScopeInfo,
2459        stack: &mut Vec<CppWork<'tree>>,
2460    ) {
2461        let name_node = node.child_by_field_name("name");
2462        let Some(name_node) = name_node else {
2463            if let Some(body) = cpp_body_node(node) {
2464                stack.push(CppWork::Container(CppContainer {
2465                    node: body,
2466                    scope: scope.clone(),
2467                }));
2468            }
2469            return;
2470        };
2471        // Diagnostic corpora contain deliberately ill-formed global namespace
2472        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
2473        // the leading global `::` as the first anonymous child. Honor that AST
2474        // boundary instead of appending the name to the lexical namespace;
2475        // appending produced legacy names such as `outer::::outer::inner`, which
2476        // could not round-trip through the structured FqName boundary.
2477        let explicitly_global = name_node
2478            .child(0)
2479            .is_some_and(|child| !child.is_named() && child.kind() == "::");
2480        let components = cpp_namespace_name_components(name_node, self.source);
2481        if components.is_empty() {
2482            return;
2483        }
2484        // One Module per namespace level. C++17's `namespace a::b { ... }` is
2485        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
2486        // shorthand must declare `a` as well as `a::b` -- extracting only the
2487        // innermost level left the enclosing namespace undeclared and made the
2488        // two spellings of one construct disagree (issue #1878).
2489        let mut package_name = if explicitly_global {
2490            String::new()
2491        } else {
2492            scope.package_name.clone()
2493        };
2494        let mut module = None;
2495        for component in components {
2496            let full_name = if package_name.is_empty() {
2497                component
2498            } else {
2499                format!("{package_name}::{component}")
2500            };
2501            let level = CodeUnit::new_fq(
2502                self.file.clone(),
2503                CodeUnitType::Module,
2504                "",
2505                full_name.clone(),
2506                cpp_namespace_fq(&full_name),
2507            );
2508            if !self.parsed.contains_declaration(&level) {
2509                self.parsed
2510                    .add_code_unit(level.clone(), node, self.source, None, None);
2511            }
2512            package_name = full_name;
2513            module = Some(level);
2514        }
2515
2516        let namespace_scope = ScopeInfo {
2517            package_name,
2518            module,
2519            class_unit: scope.class_unit.clone(),
2520            template_signature: scope.template_signature.clone(),
2521            template_metadata: scope.template_metadata.clone(),
2522            declarations_are_fields: false,
2523            recovered_specialization_member_scope: false,
2524            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2525        };
2526        let container = cpp_body_node(node).unwrap_or(node);
2527        stack.push(CppWork::Container(CppContainer {
2528            node: container,
2529            scope: namespace_scope,
2530        }));
2531    }
2532
2533    fn visit_class_like<'tree>(
2534        &mut self,
2535        node: Node<'tree>,
2536        scope: &ScopeInfo,
2537        stack: &mut Vec<CppWork<'tree>>,
2538    ) {
2539        let Some(name) = class_like_name(node, self.source) else {
2540            return;
2541        };
2542        self.visit_named_class_like(node, name, scope, stack);
2543    }
2544
2545    fn visit_named_class_like<'tree>(
2546        &mut self,
2547        node: Node<'tree>,
2548        name: String,
2549        scope: &ScopeInfo,
2550        stack: &mut Vec<CppWork<'tree>>,
2551    ) {
2552        let body = cpp_body_node(node);
2553        let definition_body_present = body.is_some();
2554        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
2555            .then(|| extract_cpp_supertypes(node, self.source));
2556        self.visit_named_class_like_shape(
2557            node,
2558            name,
2559            body,
2560            definition_body_present,
2561            None,
2562            raw_supertypes,
2563            scope,
2564            stack,
2565        );
2566    }
2567
2568    #[allow(clippy::too_many_arguments)]
2569    fn visit_named_class_like_shape<'tree>(
2570        &mut self,
2571        declaration_node: Node<'tree>,
2572        name: String,
2573        body: Option<Node<'tree>>,
2574        definition_body_present: bool,
2575        explicit_range: Option<Range>,
2576        raw_supertypes: Option<Vec<String>>,
2577        scope: &ScopeInfo,
2578        stack: &mut Vec<CppWork<'tree>>,
2579    ) -> CodeUnit {
2580        let displaced_macro_tail = if explicit_range.is_none() {
2581            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
2582        } else {
2583            None
2584        };
2585        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
2586        let recovered_scope = self.scope_for_recovered_exported_class(
2587            declaration_node,
2588            &name,
2589            definition_body_present,
2590            scope,
2591        );
2592        let scope = &recovered_scope;
2593        let short_name = if let Some(parent) = &scope.class_unit {
2594            format!("{}${name}", parent.short_name())
2595        } else {
2596            name
2597        };
2598        let fq = cpp_class_fq(&scope.package_name, &short_name);
2599        let code_unit = CodeUnit::with_signature_and_fq(
2600            self.file.clone(),
2601            CodeUnitType::Class,
2602            scope.package_name.clone(),
2603            short_name,
2604            scope.template_signature.clone(),
2605            false,
2606            fq,
2607        );
2608        let has_body = definition_body_present;
2609        if !has_body && self.parsed.contains_declaration(&code_unit) {
2610            self.parsed.record_navigation_range(
2611                code_unit.clone(),
2612                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
2613            );
2614            return code_unit;
2615        }
2616        if has_body {
2617            if let Some(range) = explicit_range {
2618                self.parsed
2619                    .replace_code_unit_with_range(code_unit.clone(), range, None, None);
2620            } else {
2621                self.parsed.replace_code_unit(
2622                    code_unit.clone(),
2623                    declaration_node,
2624                    self.source,
2625                    None,
2626                    None,
2627                );
2628            }
2629        } else {
2630            self.parsed
2631                .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
2632        }
2633        if let Some(raw_supertypes) = raw_supertypes {
2634            self.parsed
2635                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
2636        }
2637        self.parsed.add_signature(
2638            code_unit.clone(),
2639            render_cpp_type_signature(
2640                declaration_node,
2641                self.source,
2642                scope.template_signature.as_deref(),
2643            ),
2644        );
2645        if let Some(metadata) = &scope.template_metadata {
2646            let primary_short_name = if let Some(parent) = &scope.class_unit {
2647                format!("{}${}", parent.short_name(), metadata.primary_name)
2648            } else {
2649                metadata.primary_name.clone()
2650            };
2651            let primary_fq_name = CodeUnit::new(
2652                self.file.clone(),
2653                CodeUnitType::Class,
2654                scope.package_name.clone(),
2655                primary_short_name,
2656            )
2657            .fq_name();
2658            let mut metadata = metadata.clone();
2659            metadata.primary_fq_name = primary_fq_name;
2660            self.parsed
2661                .set_cpp_template_metadata(code_unit.clone(), metadata);
2662        }
2663        if let Some(parent) = &scope.class_unit {
2664            self.parsed.add_child(parent.clone(), code_unit.clone());
2665        } else if let Some(module) = &scope.module {
2666            self.parsed.add_child(module.clone(), code_unit.clone());
2667        }
2668
2669        if let Some(body) = body {
2670            let mut nested_scope = scope.clone();
2671            nested_scope.class_unit = Some(code_unit.clone());
2672            nested_scope.template_signature = scope.template_signature.clone();
2673            // Template metadata describes the class just created. It must not
2674            // leak into ordinary nested declarations in that class's body.
2675            // Recovered export-macro specializations carry a separate scope bit
2676            // for their declaration-shaped body members.
2677            nested_scope.template_metadata = None;
2678            // Export-macro class bodies recovered from a function_definition use
2679            // compound_statement children, whose direct fields are declarations.
2680            nested_scope.recovered_specialization_member_scope =
2681                scope.template_metadata.as_ref().is_some_and(|metadata| {
2682                    declaration_node.kind() == "function_definition"
2683                        && !metadata.specialization_arguments.is_empty()
2684                });
2685            nested_scope.declarations_are_fields =
2686                is_recovered_exported_class_container(declaration_node, self.source)
2687                    || nested_scope.recovered_specialization_member_scope;
2688            if let Some(displaced) = displaced_macro_tail {
2689                // A macro-shaped field without a source semicolon can make
2690                // tree-sitter consume the real class terminator as an ERROR
2691                // inside that field, then retain following namespace items as
2692                // later field-list children. Drain the proven class prefix
2693                // first and re-own only the structured tail with the outer
2694                // scope. The tail is pushed first because the work stack is
2695                // LIFO.
2696                push_cpp_sibling_range(
2697                    body,
2698                    displaced.split_index,
2699                    usize::MAX,
2700                    scope.clone(),
2701                    stack,
2702                );
2703                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
2704            } else {
2705                stack.push(CppWork::Container(CppContainer {
2706                    node: body,
2707                    scope: nested_scope,
2708                }));
2709            }
2710        }
2711        if declaration_node.kind() == "enum_specifier" {
2712            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
2713            if !self.has_enum_enumerator_units(&code_unit) {
2714                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
2715            }
2716        }
2717        code_unit
2718    }
2719
2720    fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
2721        let prefix = format!("{}.", parent.short_name());
2722        self.parsed.declarations().iter().any(|unit| {
2723            unit.kind() == CodeUnitType::Field
2724                && unit.source() == parent.source()
2725                && unit.package_name() == parent.package_name()
2726                && unit.short_name().starts_with(&prefix)
2727        })
2728    }
2729
2730    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
2731        walk_named_tree_preorder(node, false, |child| {
2732            if child.kind() != "enumerator" {
2733                return WalkControl::Continue;
2734            }
2735            let Some(name_node) = child.child_by_field_name("name") else {
2736                return WalkControl::Continue;
2737            };
2738            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
2739            if name.is_empty() {
2740                return WalkControl::Continue;
2741            }
2742            let code_unit = CodeUnit::new_fq(
2743                self.file.clone(),
2744                CodeUnitType::Field,
2745                scope.package_name.clone(),
2746                format!("{}.{}", parent.short_name(), name),
2747                parent
2748                    .fq()
2749                    .clone()
2750                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
2751            );
2752            if self.parsed.contains_declaration(&code_unit) {
2753                return WalkControl::Continue;
2754            }
2755            self.parsed.add_code_unit(
2756                code_unit.clone(),
2757                child,
2758                self.source,
2759                Some(parent.clone()),
2760                None,
2761            );
2762            self.parsed.add_signature(
2763                code_unit,
2764                normalize_cpp_whitespace(node_text(child, self.source)),
2765            );
2766            WalkControl::Continue
2767        });
2768    }
2769
2770    fn visit_enum_enumerators_from_text(
2771        &mut self,
2772        node: Node<'_>,
2773        scope: &ScopeInfo,
2774        parent: &CodeUnit,
2775    ) {
2776        let text = node_text(node, self.source);
2777        let Some((_, body)) = text.split_once('{') else {
2778            return;
2779        };
2780        let Some((body, _)) = body.rsplit_once('}') else {
2781            return;
2782        };
2783        for entry in body.split(',') {
2784            let trimmed = entry.trim();
2785            let name = trimmed
2786                .split('=')
2787                .next()
2788                .unwrap_or("")
2789                .split_whitespace()
2790                .next()
2791                .unwrap_or("");
2792            if name.is_empty() {
2793                continue;
2794            }
2795            let code_unit = CodeUnit::new_fq(
2796                self.file.clone(),
2797                CodeUnitType::Field,
2798                scope.package_name.clone(),
2799                format!("{}.{}", parent.short_name(), name),
2800                parent
2801                    .fq()
2802                    .clone()
2803                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
2804            );
2805            if self.parsed.contains_declaration(&code_unit) {
2806                continue;
2807            }
2808            self.parsed.add_code_unit(
2809                code_unit.clone(),
2810                node,
2811                self.source,
2812                Some(parent.clone()),
2813                None,
2814            );
2815            self.parsed.add_signature(code_unit, trimmed.to_string());
2816        }
2817    }
2818
2819    fn visit_function_definition<'tree>(
2820        &mut self,
2821        node: Node<'tree>,
2822        scope: &ScopeInfo,
2823        stack: &mut Vec<CppWork<'tree>>,
2824    ) {
2825        // A file-scope object-like macro sentinel the parser cannot see (issue
2826        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
2827        // prefixes as a bogus `function_definition` that swallows real namespaces,
2828        // classes, and members. Reparse the swallowed interior as C++ items so the
2829        // ordinary declaration visitors index it with byte/line-exact ownership.
2830        if self.visit_sentinel_macro_region(node, scope, stack) {
2831            return;
2832        }
2833        if node.has_error() {
2834            self.visit_macro_swallowed_function_declarations(node, scope);
2835        }
2836        if let Some((class_node, name, raw_supertypes)) =
2837            recover_exported_class_function_definition(node, self.source)
2838        {
2839            let body = cpp_body_node(class_node);
2840            let displaced_namespace = cpp_body_node(node)
2841                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
2842            let fragmented = cpp_body_node(node).and_then(|body| {
2843                fragmented_export_function_body_region(
2844                    node,
2845                    body,
2846                    self.source,
2847                    displaced_namespace.as_ref(),
2848                )
2849            });
2850            // The recovery tuple's first node is the class-like type when the
2851            // parser exposes one, but the synthetic wrapper owns the compound
2852            // statement that contains the truncated class body. Use the
2853            // wrapper body for fragmented-member detection; retain the
2854            // class-node body for the ordinary (non-fragmented) path below.
2855            if let Some(fragmented) = fragmented {
2856                // The lifted sibling no longer sits below the parser-visible
2857                // namespace node. Restore the current parent scope when the
2858                // ordinary work walk reaches that class.
2859                if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
2860                    .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
2861                {
2862                    let mut boundary_scope = scope.clone();
2863                    for sibling in cpp_following_named_siblings(node, self.source) {
2864                        if sibling.start_byte() >= boundary.start_byte() {
2865                            break;
2866                        }
2867                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
2868                            boundary_scope.visible_using_namespaces.push(namespace);
2869                        }
2870                    }
2871                    self.recovered_class_sibling_scopes
2872                        .insert(boundary.id(), boundary_scope);
2873                }
2874                let mut recovered_constructor = None;
2875                let mut recovered_prefix_tree = None;
2876                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
2877                {
2878                    Some(FragmentedExportMembers::Complete(tree)) => {
2879                        if let Some(body) = body
2880                            && let Some(range) =
2881                                cpp_reparsed_synthetic_initializer_constructor_range(
2882                                    tree.root_node(),
2883                                    &name,
2884                                    self.source,
2885                                    body.end_byte(),
2886                                )
2887                        {
2888                            recovered_constructor = Some(range);
2889                            recovered_prefix_tree = Some(tree);
2890                            None
2891                        } else {
2892                            Some(FragmentedExportMembers::Complete(tree))
2893                        }
2894                    }
2895                    outcome => outcome,
2896                };
2897                let mut class_stack = Vec::new();
2898                let class_unit = self.visit_named_class_like_shape(
2899                    class_node,
2900                    name,
2901                    None,
2902                    true,
2903                    Some(fragmented.class_range),
2904                    raw_supertypes,
2905                    scope,
2906                    &mut class_stack,
2907                );
2908                self.parsed
2909                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
2910                        recovery: fragmented.class_range,
2911                        unit: class_unit.clone(),
2912                    });
2913                let complete = outcome.is_some_and(|outcome| {
2914                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
2915                });
2916                if complete {
2917                    self.consumed_fragment_regions
2918                        .push((node.start_byte(), fragmented.class_range.end_byte));
2919                } else {
2920                    // The reparse can fail when the first constructor or a
2921                    // method body is split into statement-shaped siblings.
2922                    // Keep the recovered class envelope, but do not visit the
2923                    // synthetic wrapper body: its initializer expressions can
2924                    // look like same-named member functions (for example
2925                    // `Token.location(loc)`). Re-own only the original sibling
2926                    // nodes that fall inside the proven class range. Their CST
2927                    // shapes retain the real field/function kinds and ranges.
2928                    let member_scope = ScopeInfo {
2929                        package_name: class_unit.package_name().to_string(),
2930                        module: scope.module.clone(),
2931                        class_unit: Some(class_unit.clone()),
2932                        template_signature: scope.template_signature.clone(),
2933                        template_metadata: None,
2934                        declarations_are_fields: true,
2935                        recovered_specialization_member_scope: false,
2936                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
2937                    };
2938                    for candidate in cpp_following_named_siblings(node, self.source) {
2939                        if candidate.start_byte() >= fragmented.reparse_end {
2940                            break;
2941                        }
2942                        if cpp_fragment_sibling_is_class_member(
2943                            candidate,
2944                            fragmented.reparse_end,
2945                            self.source,
2946                        ) {
2947                            self.recovered_class_sibling_scopes
2948                                .insert(candidate.id(), member_scope.clone());
2949                        }
2950                    }
2951                    if let Some(range) = recovered_constructor
2952                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
2953                    {
2954                        self.visit_recovered_fragment_prefix_members(
2955                            prefix_tree.root_node(),
2956                            range.start,
2957                            &class_unit,
2958                            scope,
2959                        );
2960                        self.visit_recovered_fragment_constructor(
2961                            range,
2962                            body,
2963                            class_node,
2964                            &class_unit,
2965                            scope,
2966                        );
2967                    }
2968                }
2969                if let Some(boundary) = displaced_namespace {
2970                    for item in boundary.namespace_items {
2971                        self.recovered_class_sibling_scopes
2972                            .insert(item.id(), scope.clone());
2973                    }
2974                }
2975                stack.extend(class_stack);
2976                return;
2977            }
2978            let mut stack = Vec::new();
2979            let class_unit = self.visit_named_class_like_shape(
2980                class_node,
2981                name,
2982                body,
2983                body.is_some(),
2984                None,
2985                raw_supertypes,
2986                scope,
2987                &mut stack,
2988            );
2989            self.parsed
2990                .record_materialization(MaterializationRecord::RecoveredDeclaration {
2991                    recovery: cpp_declaration_range(node),
2992                    unit: class_unit,
2993                });
2994            // Issue #1524: the bogus `function_definition` body can run past
2995            // the class's true closing brace (the parse ends it with a
2996            // zero-width `MISSING "}"`), swallowing following namespace-scope
2997            // siblings -- they would index as members of the recovered class.
2998            // When the body's text-balanced close lands before the body's own
2999            // end, re-own the swallowed tail with the outer scope instead.
3000            if let Some(body) = body
3001                && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
3002                && class_close < body.end_byte()
3003            {
3004                let split = {
3005                    let mut cursor = body.walk();
3006                    body.named_children(&mut cursor)
3007                        .position(|child| child.start_byte() > class_close)
3008                };
3009                if let Some(split) = split {
3010                    // The seeded work is a single Container over the whole
3011                    // body with the class scope; replace it with the bounded
3012                    // head (class scope) plus the swallowed tail (outer
3013                    // scope). Push tail first so the head drains first.
3014                    let seeded = stack.pop();
3015                    match seeded {
3016                        Some(CppWork::Container(container)) => {
3017                            push_cpp_sibling_range(
3018                                body,
3019                                split,
3020                                usize::MAX,
3021                                scope.clone(),
3022                                &mut stack,
3023                            );
3024                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
3025                        }
3026                        // visit_named_class_like_shape always seeds exactly
3027                        // one Container when a body is present.
3028                        _ => unreachable!("exported-class seed is always one Container"),
3029                    }
3030                }
3031            }
3032            while let Some(work) = stack.pop() {
3033                match work {
3034                    CppWork::Container(container) => {
3035                        push_cpp_container_work(container.node, container.scope, &mut stack);
3036                    }
3037                    CppWork::Siblings(siblings) => {
3038                        advance_cpp_siblings(siblings, self.source, &mut stack);
3039                    }
3040                    CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
3041                }
3042            }
3043            return;
3044        }
3045        let recovered_constraint_constructor =
3046            cpp_recovered_template_macro_constructor(node, self.source);
3047        let declarator = recovered_constraint_constructor
3048            .map(|(declarator, _)| declarator)
3049            .or_else(|| node.child_by_field_name("declarator"));
3050        let Some(declarator) = declarator else {
3051            self.visit_malformed_function_definition_container(node, scope, stack);
3052            return;
3053        };
3054        let Some(function_declarator) = extract_function_declarator(declarator) else {
3055            self.visit_malformed_function_definition_container(node, scope, stack);
3056            return;
3057        };
3058        let function = if let Some((_, callable_name)) =
3059            cpp_macro_displaced_callable_parts(function_declarator, self.source)
3060        {
3061            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
3062        } else {
3063            extract_function_info(function_declarator, self.source, scope)
3064        };
3065        let Some(mut function) = function else {
3066            self.visit_malformed_function_definition_container(node, scope, stack);
3067            return;
3068        };
3069        if let Some((_, template_parameter)) = recovered_constraint_constructor {
3070            function.signature = format!(
3071                "template <{}>{}",
3072                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
3073                function.signature
3074            );
3075        }
3076        let code_unit = function.code_unit(self.file.clone());
3077        // Keep an earlier same-file prototype as another physical occurrence
3078        // of this callable. `CodeUnit` already identifies the role-neutral
3079        // overload, while ranges and signature metadata describe its
3080        // declaration/definition occurrences.
3081        self.parsed
3082            .add_code_unit(code_unit.clone(), node, self.source, None, None);
3083        let signature = if recovered_constraint_constructor.is_some() {
3084            normalize_cpp_whitespace(node_text(function_declarator, self.source))
3085        } else {
3086            render_cpp_function_display_signature_from_node(
3087                node,
3088                self.source,
3089                scope.template_signature.as_deref(),
3090                true,
3091            )
3092        };
3093        self.parsed.add_signature_with_metadata(
3094            code_unit.clone(),
3095            cpp_signature_metadata(signature, function_declarator, self.source)
3096                .with_declaration_only(false)
3097                .with_callable_linkage(cpp_callable_linkage(node, self.source)),
3098        );
3099        if let Some(parent) = &scope.class_unit {
3100            self.parsed.add_child(parent.clone(), code_unit);
3101        } else if let Some(module) = &scope.module {
3102            self.parsed.add_child(module.clone(), code_unit);
3103        }
3104    }
3105
3106    /// Recover the namespace lost when tree-sitter promotes an export-macro
3107    /// class definition to a root-level `function_definition`.  Only a
3108    /// body-bearing, top-level recovery may borrow a namespace, and only when
3109    /// one earlier namespace-scope forward declaration proves the identity.
3110    fn scope_for_recovered_exported_class(
3111        &self,
3112        node: Node<'_>,
3113        name: &str,
3114        definition_body_present: bool,
3115        scope: &ScopeInfo,
3116    ) -> ScopeInfo {
3117        if !definition_body_present
3118            || !scope.package_name.is_empty()
3119            || scope.class_unit.is_some()
3120            || !(is_recovered_exported_class_container(node, self.source)
3121                || matches!(node.kind(), "declaration" | "field_declaration")
3122                    && recover_exported_class_declaration(node, self.source).is_some()
3123                || matches!(
3124                    node.kind(),
3125                    "class_specifier" | "struct_specifier" | "union_specifier"
3126                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
3127                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
3128                        name_node,
3129                        self.source,
3130                    )))
3131                }) || node.parent().is_some_and(|parent| {
3132                    matches!(parent.kind(), "declaration" | "field_declaration")
3133                        && recover_exported_class_declaration(parent, self.source).is_some()
3134                        || is_recovered_exported_class_container(parent, self.source)
3135                })) && class_like_name(node, self.source).as_deref() == Some(name))
3136        {
3137            return scope.clone();
3138        }
3139        let Some(package_name) = unique_earlier_cpp_namespace_forward(node, name, self.source)
3140        else {
3141            return scope.clone();
3142        };
3143
3144        let module = CodeUnit::new_fq(
3145            self.file.clone(),
3146            CodeUnitType::Module,
3147            "",
3148            package_name.clone(),
3149            cpp_namespace_fq(&package_name),
3150        );
3151        let mut recovered = scope.clone();
3152        recovered.package_name = package_name;
3153        recovered.module = Some(module);
3154        recovered
3155    }
3156
3157    fn visit_malformed_function_definition_container<'tree>(
3158        &mut self,
3159        node: Node<'tree>,
3160        scope: &ScopeInfo,
3161        stack: &mut Vec<CppWork<'tree>>,
3162    ) {
3163        let Some(body) = cpp_body_node(node) else {
3164            return;
3165        };
3166        if !cpp_contains_namespace_definition(body) {
3167            return;
3168        }
3169        stack.push(CppWork::Container(CppContainer {
3170            node: body,
3171            scope: scope.clone(),
3172        }));
3173    }
3174
3175    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
3176    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
3177    /// emits for a sentinel-prefixed region, reparse the interior after the
3178    /// sentinel identifier as real C++ items -- confined to the region so
3179    /// every reparsed node keeps its original byte/line position -- and run the
3180    /// ordinary container visitation over the result. Returns `true` when it fired
3181    /// (the caller must then skip normal function processing). Nested sentinel
3182    /// regions recover recursively: the reparsed interior is walked through the
3183    /// same `visit_function_definition` path, so a sentinel inside the region hits
3184    /// this recovery again.
3185    /// Runs `reparse_walk` and records every declaration it mints as a
3186    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
3187    /// `recovery` (issue #1657). A reparsed sentinel region has no single
3188    /// recovered envelope unit: the ordinary visitors mint namespaces,
3189    /// classes, and members directly from the reparsed tree, so the walk's
3190    /// declaration delta is the recovered set. Records are ordered by
3191    /// declaration start byte so the parse product stays deterministic.
3192    fn record_recovered_declarations(
3193        &mut self,
3194        recovery: Range,
3195        reparse_walk: impl FnOnce(&mut Self),
3196    ) {
3197        let before = self.parsed.declarations().clone();
3198        reparse_walk(self);
3199        let mut minted: Vec<CodeUnit> = self
3200            .parsed
3201            .declarations()
3202            .iter()
3203            .filter(|unit| !before.contains(*unit))
3204            .cloned()
3205            .collect();
3206        minted.sort_by_cached_key(|unit| {
3207            let start = self
3208                .parsed
3209                .declaration_ranges(unit)
3210                .first()
3211                .map(|range| range.start_byte)
3212                .unwrap_or(usize::MAX);
3213            (start, unit.fq_name().to_string())
3214        });
3215        for unit in minted {
3216            self.parsed
3217                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3218                    recovery,
3219                    unit,
3220                });
3221        }
3222    }
3223
3224    fn visit_sentinel_macro_region<'tree>(
3225        &mut self,
3226        node: Node<'tree>,
3227        scope: &ScopeInfo,
3228        stack: &mut Vec<CppWork<'tree>>,
3229    ) -> bool {
3230        if self.visit_nested_namespace_sentinel(node, scope) {
3231            return true;
3232        }
3233        if let Some((
3234            reparse_start,
3235            class_start,
3236            body_start,
3237            class_close_start,
3238            class_close_end,
3239            class_close_line,
3240        )) = cpp_sentinel_macro_class_region(node, self.source)
3241        {
3242            let Some(class_tree) =
3243                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
3244            else {
3245                return false;
3246            };
3247            let class_root = class_tree.root_node();
3248            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
3249            let Some(reparsed_class) =
3250                cpp_sentinel_reparsed_class(class_root, template_node, self.source)
3251            else {
3252                return false;
3253            };
3254            let class_node = reparsed_class.declaration_node;
3255            let name = reparsed_class.name;
3256            let mut class_scope = scope.clone();
3257            if let Some(template_node) = template_node {
3258                class_scope.template_signature =
3259                    cpp_template_signature(template_node, class_node, self.source);
3260                class_scope.template_metadata =
3261                    cpp_template_metadata(template_node, class_node, self.source);
3262            }
3263            let Some(body_tree) =
3264                cpp_reparse_region_items(self.source, body_start, class_close_start)
3265            else {
3266                return false;
3267            };
3268            let raw_supertypes = reparsed_class.raw_supertypes;
3269            let class_range = Range {
3270                start_byte: class_start,
3271                end_byte: class_close_end,
3272                start_line: class_node.start_position().row + 1,
3273                end_line: class_close_line,
3274            };
3275            let class_scope =
3276                self.scope_for_recovered_exported_class(class_node, &name, true, &class_scope);
3277            let mut class_stack = Vec::new();
3278            let class_unit = self.visit_named_class_like_shape(
3279                class_node,
3280                name,
3281                None,
3282                true,
3283                Some(class_range),
3284                raw_supertypes,
3285                &class_scope,
3286                &mut class_stack,
3287            );
3288            self.parsed
3289                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3290                    recovery: class_range,
3291                    unit: class_unit.clone(),
3292                });
3293            let member_scope = ScopeInfo {
3294                package_name: class_scope.package_name.clone(),
3295                module: class_scope.module.clone(),
3296                class_unit: Some(class_unit),
3297                template_signature: class_scope.template_signature.clone(),
3298                template_metadata: None,
3299                declarations_are_fields: true,
3300                recovered_specialization_member_scope: false,
3301                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
3302            };
3303            self.run_container_work(body_tree.root_node(), member_scope);
3304            // Register only after the padded body reparse: its nodes deliberately
3305            // retain offsets inside the consumed region and must be visited first.
3306            self.consumed_fragment_regions
3307                .push((node.start_byte(), class_close_end));
3308            // An ERROR envelope can hold real sibling declarations after the
3309            // recovered class's close (the suffix-reparse boundary in
3310            // `cpp_sentinel_macro_class_region` partitions, it does not
3311            // consume). Walk the envelope's remaining children normally; the
3312            // consumed region above keeps the recovered class from being
3313            // indexed twice.
3314            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
3315                stack.push(CppWork::Container(CppContainer {
3316                    node,
3317                    scope: scope.clone(),
3318                }));
3319            }
3320            return true;
3321        }
3322        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
3323            return false;
3324        };
3325        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
3326            return false;
3327        };
3328        let root = tree.root_node();
3329        if !cpp_reparsed_items_are_indexable(root, self.source) {
3330            return false;
3331        }
3332        let recovery = cpp_recovery_window(self.source, start, end);
3333        self.record_recovered_declarations(recovery, |visitor| {
3334            visitor.visit_container(
3335                root,
3336                &scope.package_name,
3337                scope.module.clone(),
3338                scope.class_unit.clone(),
3339                scope.template_signature.clone(),
3340                scope.visible_using_namespaces.clone(),
3341            );
3342        });
3343        if end > node.end_byte() {
3344            self.consumed_fragment_regions
3345                .push((node.start_byte(), end));
3346        } else if node.kind() == "ERROR" && node.end_byte() > end {
3347            // The sentinel region ended at the first recovered class-like item
3348            // but the ERROR envelope keeps real sibling declarations after it
3349            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
3350            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
3351            // envelope's remaining children normally; the consumed region
3352            // keeps the reparsed prefix from being indexed twice.
3353            self.consumed_fragment_regions
3354                .push((node.start_byte(), end));
3355            stack.push(CppWork::Container(CppContainer {
3356                node,
3357                scope: scope.clone(),
3358            }));
3359        }
3360        true
3361    }
3362
3363    /// Re-own complete class declarations from the structured Abseil
3364    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
3365    /// its direct CST children already prove both namespace components and the
3366    /// class bodies, so the ordinary class/member visitor can retain ownership
3367    /// and exact source ranges without admitting unrelated callable bodies.
3368    fn visit_nested_namespace_sentinel(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
3369        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source) else {
3370            return false;
3371        };
3372
3373        let mut package_name = scope.package_name.clone();
3374        let mut module = scope.module.clone();
3375        for component in recovered.namespace_components {
3376            package_name = if package_name.is_empty() {
3377                component
3378            } else {
3379                format!("{package_name}::{component}")
3380            };
3381            let namespace_module = CodeUnit::new_fq(
3382                self.file.clone(),
3383                CodeUnitType::Module,
3384                "",
3385                package_name.clone(),
3386                cpp_namespace_fq(&package_name),
3387            );
3388            if !self.parsed.contains_declaration(&namespace_module) {
3389                self.parsed.add_code_unit(
3390                    namespace_module.clone(),
3391                    recovered.function,
3392                    self.source,
3393                    None,
3394                    None,
3395                );
3396            }
3397            module = Some(namespace_module);
3398        }
3399
3400        let recovered_scope = ScopeInfo {
3401            package_name,
3402            module,
3403            class_unit: scope.class_unit.clone(),
3404            template_signature: scope.template_signature.clone(),
3405            template_metadata: scope.template_metadata.clone(),
3406            declarations_are_fields: false,
3407            recovered_specialization_member_scope: false,
3408            visible_using_namespaces: scope.visible_using_namespaces.clone(),
3409        };
3410        if let Some(fragmented) =
3411            cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, self.source)
3412        {
3413            let mut class_scope = recovered_scope.clone();
3414            if let Some(template_node) = fragmented.template_node {
3415                class_scope.template_signature =
3416                    cpp_template_signature(template_node, fragmented.class_node, self.source);
3417                class_scope.template_metadata =
3418                    cpp_template_metadata(template_node, fragmented.class_node, self.source);
3419            }
3420            if let Some(outcome) = self
3421                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
3422            {
3423                let mut class_stack = Vec::new();
3424                let class_unit = self.visit_named_class_like_shape(
3425                    fragmented.class_node,
3426                    fragmented.name.clone(),
3427                    None,
3428                    true,
3429                    Some(fragmented.fragmented.class_range),
3430                    fragmented.raw_supertypes.clone(),
3431                    &class_scope,
3432                    &mut class_stack,
3433                );
3434                self.parsed
3435                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
3436                        recovery: fragmented.fragmented.class_range,
3437                        unit: class_unit.clone(),
3438                    });
3439                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
3440                    self.consumed_fragment_regions.push((
3441                        fragmented.consumed_start,
3442                        fragmented.fragmented.class_range.end_byte,
3443                    ));
3444                }
3445            }
3446        }
3447        // The class requirement above is the admission gate; once admitted,
3448        // traverse the whole proven inner namespace body so sibling aliases,
3449        // functions, and variables are not silently discarded.
3450        self.run_container_work(recovered.body, recovered_scope);
3451        true
3452    }
3453
3454    fn visit_declaration<'tree>(
3455        &mut self,
3456        node: Node<'tree>,
3457        scope: &ScopeInfo,
3458        in_class_body: bool,
3459        stack: &mut Vec<CppWork<'tree>>,
3460    ) {
3461        if self.visit_sentinel_macro_region(node, scope, stack) {
3462            return;
3463        }
3464        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
3465            !cpp_active_template_type_parameter(
3466                node,
3467                node_text(declarator, self.source),
3468                self.source,
3469            )
3470        }) {
3471            return;
3472        }
3473        if in_class_body
3474            && let Some(parent) = scope.class_unit.as_ref()
3475            && let Some(call) =
3476                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
3477        {
3478            self.visit_recovered_macro_qualified_constructor_definition(node, call, scope);
3479            return;
3480        }
3481        if in_class_body
3482            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
3483        {
3484            self.visit_recovered_macro_qualified_function_declaration(node, call, scope);
3485            return;
3486        }
3487        if in_class_body
3488            && let Some(declarators) =
3489                recovered_macro_qualified_field_declarators(node, self.source)
3490        {
3491            for declarator in declarators {
3492                self.visit_variable_declaration(node, declarator, scope, true);
3493            }
3494            return;
3495        }
3496        let recovered_alias_names = recovered_type_alias_names(node, self.source);
3497        if !recovered_alias_names.is_empty() {
3498            self.add_type_aliases(node, scope, recovered_alias_names);
3499            return;
3500        }
3501
3502        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
3503            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
3504                // Issue #938: the members tree-sitter scattered out of the fragmented
3505                // multiple-base export node are reparsed from their true body region
3506                // and re-owned as members of the recovered class, with an explicit
3507                // navigation range spanning to the displaced closing brace.
3508                if let Some(outcome) =
3509                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
3510                {
3511                    let consumed_region = (
3512                        recovered.declaration_node.end_byte(),
3513                        fragmented.class_range.end_byte,
3514                    );
3515                    let code_unit = self.visit_named_class_like_shape(
3516                        recovered.declaration_node,
3517                        recovered.name,
3518                        None,
3519                        true,
3520                        Some(fragmented.class_range),
3521                        recovered.raw_supertypes,
3522                        scope,
3523                        stack,
3524                    );
3525                    self.parsed.record_materialization(
3526                        MaterializationRecord::RecoveredDeclaration {
3527                            recovery: fragmented.class_range,
3528                            unit: code_unit.clone(),
3529                        },
3530                    );
3531                    let consume_fragment =
3532                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
3533                    // Everything between the fragmented declaration and its displaced
3534                    // closing brace now belongs to the recovered class; keep the
3535                    // ordinary walk from re-indexing those scattered siblings at top
3536                    // level. Register the consumed region only after indexing because
3537                    // the reparsed nodes retain byte offsets inside that same region.
3538                    if consume_fragment {
3539                        self.consumed_fragment_regions.push(consumed_region);
3540                    }
3541                    return;
3542                }
3543            }
3544            let uses_initializer_body = recovered.uses_initializer_body;
3545            let definition_body_present = recovered.body.is_some();
3546            let class_unit = self.visit_named_class_like_shape(
3547                recovered.declaration_node,
3548                recovered.name,
3549                recovered.body,
3550                definition_body_present,
3551                None,
3552                recovered.raw_supertypes,
3553                scope,
3554                stack,
3555            );
3556            self.parsed
3557                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3558                    recovery: cpp_declaration_range(node),
3559                    unit: class_unit,
3560                });
3561            if uses_initializer_body {
3562                return;
3563            }
3564        }
3565
3566        let mut handled_function = false;
3567        let mut handled_declarator = false;
3568        let mut cursor = node.walk();
3569        for child in node.named_children(&mut cursor) {
3570            if matches!(
3571                child.kind(),
3572                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3573            ) {
3574                // A named class-like definition remains a declaration even when
3575                // the same statement also declares an object, for example
3576                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
3577                // declaration's type and `kind` as its declarator.  Dropping the
3578                // type here loses both its nested owner and every later lexical
3579                // reference to it.  A body is the structured proof that this is
3580                // a definition rather than an elaborated type use such as
3581                // `class Kind value;`.
3582                if cpp_body_node(child).is_some() {
3583                    self.visit_class_like(child, scope, stack);
3584                }
3585                continue;
3586            }
3587        }
3588
3589        let mut cursor = node.walk();
3590        for child in node.children_by_field_name("declarator", &mut cursor) {
3591            if crate::structural::is_recovered_designator_init_declarator(child) {
3592                handled_declarator = true;
3593                continue;
3594            }
3595            if let Some(kind) = classify_declarator(child) {
3596                handled_declarator = true;
3597                match kind {
3598                    DeclaratorKind::Function(function_declarator) => {
3599                        handled_function = true;
3600                        self.visit_function_declaration(node, function_declarator, scope);
3601                    }
3602                    DeclaratorKind::Variable(variable_declarator) => {
3603                        self.visit_variable_declaration(
3604                            node,
3605                            variable_declarator,
3606                            scope,
3607                            in_class_body,
3608                        );
3609                    }
3610                }
3611            }
3612        }
3613
3614        if !handled_declarator {
3615            let mut cursor = node.walk();
3616            for child in node.named_children(&mut cursor) {
3617                if crate::structural::is_recovered_designator_init_declarator(child) {
3618                    handled_declarator = true;
3619                    continue;
3620                }
3621                if !is_unfielded_declarator_candidate(child) {
3622                    continue;
3623                }
3624                let Some(kind) = classify_declarator(child) else {
3625                    continue;
3626                };
3627                handled_declarator = true;
3628                match kind {
3629                    DeclaratorKind::Function(function_declarator) => {
3630                        handled_function = true;
3631                        self.visit_function_declaration(node, function_declarator, scope);
3632                    }
3633                    DeclaratorKind::Variable(variable_declarator) => {
3634                        self.visit_variable_declaration(
3635                            node,
3636                            variable_declarator,
3637                            scope,
3638                            in_class_body,
3639                        );
3640                    }
3641                }
3642            }
3643        }
3644
3645        if handled_function {
3646            return;
3647        }
3648
3649        if !handled_declarator {
3650            if in_class_body {
3651                self.visit_class_members_from_declaration(node, scope);
3652            } else {
3653                self.visit_global_variables_from_declaration(node, scope);
3654            }
3655        }
3656    }
3657
3658    fn visit_function_declaration(
3659        &mut self,
3660        declaration_node: Node<'_>,
3661        declarator: Node<'_>,
3662        scope: &ScopeInfo,
3663    ) {
3664        let Some(function) = extract_function_info(declarator, self.source, scope) else {
3665            return;
3666        };
3667        let code_unit =
3668            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
3669        if self.parsed.contains_declaration(&code_unit) {
3670            self.parsed
3671                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3672            return;
3673        }
3674        self.parsed
3675            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3676        let signature = render_cpp_function_display_signature_from_node(
3677            declaration_node,
3678            self.source,
3679            scope.template_signature.as_deref(),
3680            false,
3681        );
3682        self.parsed.add_signature_with_metadata(
3683            code_unit.clone(),
3684            cpp_signature_metadata(signature, declarator, self.source)
3685                .with_declaration_only(true)
3686                .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source)),
3687        );
3688        if let Some(parent) = &scope.class_unit {
3689            self.parsed.add_child(parent.clone(), code_unit);
3690        } else if let Some(module) = &scope.module {
3691            self.parsed.add_child(module.clone(), code_unit);
3692        }
3693    }
3694
3695    fn visit_recovered_macro_qualified_function_declaration(
3696        &mut self,
3697        declaration_node: Node<'_>,
3698        call: Node<'_>,
3699        scope: &ScopeInfo,
3700    ) {
3701        let Some(parent) = &scope.class_unit else {
3702            return;
3703        };
3704        let Some(name_node) = call.child_by_field_name("function") else {
3705            return;
3706        };
3707        let Some(arguments) = call.child_by_field_name("arguments") else {
3708            return;
3709        };
3710        let Some((signature, parameter_labels)) =
3711            recovered_macro_qualified_function_parameters(arguments, self.source)
3712        else {
3713            return;
3714        };
3715        let arity = parameter_labels.len();
3716        let function = FunctionInfo {
3717            package_name: scope.package_name.clone(),
3718            owner_path: Some(parent.short_name().to_string()),
3719            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
3720            signature,
3721        };
3722        if function.name.is_empty() {
3723            return;
3724        }
3725        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3726        if self.parsed.contains_declaration(&code_unit) {
3727            self.parsed
3728                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3729            return;
3730        }
3731        self.parsed
3732            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3733        let signature_label = render_cpp_function_display_signature_from_node(
3734            declaration_node,
3735            self.source,
3736            scope.template_signature.as_deref(),
3737            false,
3738        );
3739        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3740            .with_declaration_only(true)
3741            .with_callable_arity(CallableArity::exact(arity))
3742            .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3743        self.parsed
3744            .add_signature_with_metadata(code_unit.clone(), metadata);
3745        self.parsed.add_child(parent.clone(), code_unit);
3746    }
3747
3748    fn visit_recovered_macro_qualified_constructor_definition(
3749        &mut self,
3750        declaration_node: Node<'_>,
3751        call: Node<'_>,
3752        scope: &ScopeInfo,
3753    ) {
3754        let Some(parent) = &scope.class_unit else {
3755            return;
3756        };
3757        let Some(arguments) = call.child_by_field_name("arguments") else {
3758            return;
3759        };
3760        let Some((mut signature, parameter_labels)) =
3761            recovered_macro_qualified_function_parameters(arguments, self.source)
3762        else {
3763            return;
3764        };
3765        if let Some(template_signature) = &scope.template_signature {
3766            signature = format!("{template_signature}{signature}");
3767        }
3768        let arity = parameter_labels.len();
3769        let function = FunctionInfo {
3770            package_name: scope.package_name.clone(),
3771            owner_path: Some(parent.short_name().to_string()),
3772            name: parent.identifier().to_string(),
3773            signature,
3774        };
3775        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3776        self.parsed
3777            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3778        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
3779        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3780            .with_declaration_only(false)
3781            .with_callable_arity(CallableArity::exact(arity))
3782            .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3783        self.parsed
3784            .add_signature_with_metadata(code_unit.clone(), metadata);
3785        self.parsed.add_child(parent.clone(), code_unit);
3786    }
3787
3788    fn visit_variable_declaration(
3789        &mut self,
3790        declaration_node: Node<'_>,
3791        declarator: Node<'_>,
3792        scope: &ScopeInfo,
3793        in_class_body: bool,
3794    ) {
3795        let Some(name) = extract_variable_name(declarator, self.source) else {
3796            return;
3797        };
3798        let short_name = if in_class_body {
3799            let Some(parent) = &scope.class_unit else {
3800                return;
3801            };
3802            format!("{}.{}", parent.short_name(), name)
3803        } else {
3804            name
3805        };
3806        let fq = cpp_member_fq(&scope.package_name, &short_name);
3807        let code_unit = CodeUnit::new_fq(
3808            self.file.clone(),
3809            CodeUnitType::Field,
3810            scope.package_name.clone(),
3811            short_name,
3812            fq,
3813        );
3814        if self.parsed.contains_declaration(&code_unit) {
3815            return;
3816        }
3817        self.parsed
3818            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3819        self.parsed.add_signature_with_metadata(
3820            code_unit.clone(),
3821            SignatureMetadata::new(
3822                render_cpp_field_signature(declaration_node, declarator, self.source),
3823                Vec::new(),
3824            )
3825            .with_cpp_field_linkage(cpp_field_declaration_linkage(declaration_node, self.source)),
3826        );
3827        if let Some(parent) = &scope.class_unit {
3828            self.parsed.add_child(parent.clone(), code_unit);
3829        } else if let Some(module) = &scope.module {
3830            self.parsed.add_child(module.clone(), code_unit);
3831        }
3832    }
3833
3834    fn visit_class_members_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
3835        let mut cursor = node.walk();
3836        for child in node.named_children(&mut cursor) {
3837            if child.kind() == "init_declarator"
3838                && let Some(inner) = child.child_by_field_name("declarator")
3839            {
3840                self.visit_variable_declaration(node, inner, scope, true);
3841            } else if matches!(
3842                child.kind(),
3843                "identifier"
3844                    | "field_identifier"
3845                    | "pointer_declarator"
3846                    | "reference_declarator"
3847                    | "array_declarator"
3848                    | "parenthesized_declarator"
3849            ) {
3850                self.visit_variable_declaration(node, child, scope, true);
3851            }
3852        }
3853    }
3854
3855    fn visit_global_variables_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
3856        let mut cursor = node.walk();
3857        for child in node.named_children(&mut cursor) {
3858            if child.kind() == "init_declarator"
3859                && let Some(inner) = child.child_by_field_name("declarator")
3860            {
3861                self.visit_variable_declaration(node, inner, scope, false);
3862            } else if matches!(
3863                child.kind(),
3864                "identifier"
3865                    | "field_identifier"
3866                    | "pointer_declarator"
3867                    | "reference_declarator"
3868                    | "array_declarator"
3869                    | "parenthesized_declarator"
3870            ) {
3871                self.visit_variable_declaration(node, child, scope, false);
3872            }
3873        }
3874    }
3875
3876    fn visit_include(&mut self, node: Node<'_>) {
3877        let raw = normalize_cpp_whitespace(node_text(node, self.source));
3878        self.parsed.imports.push(ImportInfo {
3879            raw_snippet: raw,
3880            is_wildcard: false,
3881            is_global: false,
3882            identifier: None,
3883            alias: None,
3884            path: None,
3885            binder_span: None,
3886        });
3887    }
3888
3889    fn visit_type_declaration<'tree>(
3890        &mut self,
3891        node: Node<'tree>,
3892        scope: &ScopeInfo,
3893        stack: &mut Vec<CppWork<'tree>>,
3894    ) {
3895        if let Some(type_node) = node.child_by_field_name("type")
3896            && matches!(
3897                type_node.kind(),
3898                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3899            )
3900        {
3901            self.visit_class_like(type_node, scope, stack);
3902        }
3903
3904        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
3905            let range = Range {
3906                start_byte: node.start_byte(),
3907                end_byte: recovered.end_node.end_byte(),
3908                start_line: node.start_position().row + 1,
3909                end_line: recovered.end_node.end_position().row + 1,
3910            };
3911            let signature = self
3912                .source
3913                .get(range.start_byte..range.end_byte)
3914                .map(normalize_cpp_whitespace)
3915                .unwrap_or_default();
3916            self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
3917            return;
3918        }
3919
3920        let alias_names = match node.kind() {
3921            "alias_declaration" => extract_alias_declaration_name(node, self.source)
3922                .into_iter()
3923                .collect::<Vec<_>>(),
3924            "type_definition" => extract_typedef_alias_names(node, self.source),
3925            _ => Vec::new(),
3926        };
3927        self.add_type_aliases(node, scope, alias_names);
3928    }
3929
3930    fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
3931        let signature = normalize_cpp_whitespace(node_text(node, self.source));
3932        self.record_type_aliases(
3933            node,
3934            scope,
3935            alias_names,
3936            signature,
3937            cpp_declaration_range(node),
3938        );
3939    }
3940
3941    fn record_type_aliases(
3942        &mut self,
3943        node: Node<'_>,
3944        scope: &ScopeInfo,
3945        alias_names: Vec<String>,
3946        signature: String,
3947        range: Range,
3948    ) {
3949        if signature.is_empty() {
3950            return;
3951        }
3952        let type_name = node
3953            .child_by_field_name("type")
3954            .and_then(|type_node| type_node.child_by_field_name("name"))
3955            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
3956        for alias_name in alias_names {
3957            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
3958                continue;
3959            }
3960            let short_name = if let Some(parent) = &scope.class_unit {
3961                format!("{}${alias_name}", parent.short_name())
3962            } else {
3963                alias_name
3964            };
3965            let fq = cpp_class_fq(&scope.package_name, &short_name);
3966            let code_unit = CodeUnit::with_signature_and_fq(
3967                self.file.clone(),
3968                CodeUnitType::Class,
3969                scope.package_name.clone(),
3970                short_name,
3971                Some(signature.clone()),
3972                false,
3973                fq,
3974            );
3975            // Declaration identity does not include the alias signature. Keep
3976            // each physical range so conditional aliases retain their guards.
3977            self.parsed
3978                .add_code_unit_with_range(code_unit.clone(), range, None, None);
3979            self.parsed
3980                .add_signature(code_unit.clone(), signature.clone());
3981            if let Some(metadata) = &scope.template_metadata {
3982                let mut metadata = metadata.clone();
3983                metadata.primary_fq_name = code_unit.fq_name();
3984                self.parsed
3985                    .set_cpp_template_metadata(code_unit.clone(), metadata);
3986            }
3987            if let Some(parent) = &scope.class_unit {
3988                self.parsed.add_child(parent.clone(), code_unit.clone());
3989            } else if let Some(module) = &scope.module {
3990                self.parsed.add_child(module.clone(), code_unit.clone());
3991            }
3992            self.parsed.mark_type_alias(code_unit);
3993        }
3994    }
3995
3996    fn visit_macro(&mut self, node: Node<'_>) {
3997        let Some(name) = extract_macro_name(node, self.source) else {
3998            return;
3999        };
4000        let signature = node_text(node, self.source).trim_end().to_string();
4001        if signature.is_empty() {
4002            return;
4003        }
4004        let fq = cpp_member_fq("", &name);
4005        let code_unit = CodeUnit::new_fq(self.file.clone(), CodeUnitType::Macro, "", name, fq);
4006        if self.parsed.contains_declaration_identity(&code_unit) {
4007            return;
4008        }
4009        self.parsed
4010            .add_code_unit(code_unit.clone(), node, self.source, None, None);
4011        let name_range = node
4012            .child_by_field_name("name")
4013            .map(cpp_declaration_range)
4014            .unwrap_or_else(|| cpp_declaration_range(node));
4015        self.parsed
4016            .record_materialization(MaterializationRecord::GeneratedDeclaration {
4017                site: cpp_declaration_range(node),
4018                argument: name_range,
4019                kind: GenerationKind::PreprocessorDefinition,
4020                unit: code_unit.clone(),
4021            });
4022        self.parsed.add_signature(code_unit, signature);
4023    }
4024}
4025
4026/// Classify a C++ field while its declaration syntax is already available.
4027///
4028/// The persisted result lets later visibility queries avoid reparsing the
4029/// complete source file only to recover linkage.
4030pub fn cpp_field_declaration_linkage(declaration: Node<'_>, source: &str) -> CppFieldLinkage {
4031    let mut current = declaration.parent();
4032    let mut enclosed_by_class = false;
4033    while let Some(node) = current {
4034        if node.kind() == "namespace_definition"
4035            && node
4036                .child_by_field_name("name")
4037                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4038        {
4039            return CppFieldLinkage::Internal;
4040        }
4041        if matches!(
4042            node.kind(),
4043            "class_specifier" | "struct_specifier" | "union_specifier"
4044        ) && node
4045            .child_by_field_name("name")
4046            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4047        {
4048            return CppFieldLinkage::Internal;
4049        }
4050        if matches!(
4051            node.kind(),
4052            "class_specifier" | "struct_specifier" | "union_specifier"
4053        ) {
4054            enclosed_by_class = true;
4055        }
4056        if matches!(node.kind(), "function_definition" | "lambda_expression") {
4057            return CppFieldLinkage::Internal;
4058        }
4059        current = node.parent();
4060    }
4061    if enclosed_by_class {
4062        return CppFieldLinkage::External;
4063    }
4064    let mut cursor = declaration.walk();
4065    let mut has_static = false;
4066    let mut has_extern = false;
4067    let mut has_inline = false;
4068    let mut has_const = false;
4069    let mut has_constexpr = false;
4070    for child in declaration.named_children(&mut cursor) {
4071        let text = normalize_cpp_whitespace(node_text(child, source));
4072        match (child.kind(), text.as_str()) {
4073            ("storage_class_specifier", "static") => has_static = true,
4074            ("storage_class_specifier", "extern") => has_extern = true,
4075            ("storage_class_specifier", "inline") => has_inline = true,
4076            ("storage_class_specifier", "constexpr") => has_constexpr = true,
4077            ("type_qualifier", "const") => has_const = true,
4078            ("type_qualifier", "constexpr") => has_constexpr = true,
4079            _ => {}
4080        }
4081    }
4082    if has_static {
4083        CppFieldLinkage::Internal
4084    } else if has_extern || has_inline {
4085        CppFieldLinkage::External
4086    } else if has_const || has_constexpr {
4087        CppFieldLinkage::InternalUnlessExternalPeer
4088    } else {
4089        CppFieldLinkage::External
4090    }
4091}
4092
4093fn cpp_declaration_range(node: Node<'_>) -> Range {
4094    Range {
4095        start_byte: node.start_byte(),
4096        end_byte: node.end_byte(),
4097        start_line: node.start_position().row + 1,
4098        end_line: node.end_position().row + 1,
4099    }
4100}
4101
4102/// A recovery interval as a [`Range`], for materialization records whose
4103/// window is a byte region rather than one parser node (the sentinel-macro
4104/// region reparses, issue #941/#1657).
4105fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
4106    let line_at = |byte: usize| {
4107        source.as_bytes()[..byte]
4108            .iter()
4109            .filter(|&&b| b == b'\n')
4110            .count()
4111            + 1
4112    };
4113    Range {
4114        start_byte,
4115        end_byte,
4116        start_line: line_at(start_byte),
4117        end_line: line_at(end_byte),
4118    }
4119}
4120
4121pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
4122    let mut in_block_comment = false;
4123    for line in source.lines() {
4124        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
4125        let trimmed = stripped.trim();
4126        if !looks_like_quoted_include_line(trimmed) {
4127            continue;
4128        }
4129
4130        let raw = normalize_cpp_whitespace(trimmed);
4131        // The tree-sitter walk already recorded every `#include` it could see;
4132        // this line scan only recovers the ones a parse error hid, so skip a
4133        // snippet that is already an import binding.
4134        if parsed
4135            .imports
4136            .iter()
4137            .any(|import| import.raw_snippet == raw)
4138        {
4139            continue;
4140        }
4141
4142        parsed.imports.push(ImportInfo {
4143            raw_snippet: raw,
4144            is_wildcard: false,
4145            is_global: false,
4146            identifier: None,
4147            alias: None,
4148            path: None,
4149            binder_span: None,
4150        });
4151    }
4152}
4153
4154fn looks_like_quoted_include_line(line: &str) -> bool {
4155    let Some(rest) = line.trim_start().strip_prefix('#') else {
4156        return false;
4157    };
4158    let Some(rest) = rest.trim_start().strip_prefix("include") else {
4159        return false;
4160    };
4161    rest.trim_start().starts_with('"')
4162}
4163
4164fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
4165    let mut raw = Vec::new();
4166    let mut cursor = node.walk();
4167    for child in node.named_children(&mut cursor) {
4168        if child.kind() == "base_class_clause" {
4169            collect_cpp_base_nodes(child, source, &mut raw);
4170        }
4171    }
4172    raw
4173}
4174
4175fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
4176    walk_named_tree_preorder(node, false, |child| match child.kind() {
4177        "type_identifier" | "qualified_identifier" | "template_type" => {
4178            let text = normalize_cpp_whitespace(node_text(child, source));
4179            if !text.is_empty() {
4180                raw.push(text);
4181            }
4182            WalkControl::SkipChildren
4183        }
4184        _ => WalkControl::Continue,
4185    });
4186}
4187
4188fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
4189    let mut out = String::new();
4190    let chars: Vec<char> = line.chars().collect();
4191    let mut index = 0;
4192    let mut in_string = false;
4193    let mut in_char = false;
4194    let mut escape = false;
4195
4196    while index < chars.len() {
4197        let ch = chars[index];
4198        let next = chars.get(index + 1).copied();
4199
4200        if *in_block_comment {
4201            if ch == '*' && next == Some('/') {
4202                *in_block_comment = false;
4203                index += 2;
4204            } else {
4205                index += 1;
4206            }
4207            continue;
4208        }
4209
4210        if in_string {
4211            out.push(ch);
4212            if escape {
4213                escape = false;
4214            } else if ch == '\\' {
4215                escape = true;
4216            } else if ch == '"' {
4217                in_string = false;
4218            }
4219            index += 1;
4220            continue;
4221        }
4222
4223        if in_char {
4224            out.push(ch);
4225            if escape {
4226                escape = false;
4227            } else if ch == '\\' {
4228                escape = true;
4229            } else if ch == '\'' {
4230                in_char = false;
4231            }
4232            index += 1;
4233            continue;
4234        }
4235
4236        if ch == '/' && next == Some('/') {
4237            break;
4238        }
4239        if ch == '/' && next == Some('*') {
4240            *in_block_comment = true;
4241            index += 2;
4242            continue;
4243        }
4244        if ch == '"' {
4245            in_string = true;
4246            out.push(ch);
4247            index += 1;
4248            continue;
4249        }
4250        if ch == '\'' {
4251            in_char = true;
4252            out.push(ch);
4253            index += 1;
4254            continue;
4255        }
4256
4257        out.push(ch);
4258        index += 1;
4259    }
4260
4261    out
4262}
4263
4264#[derive(Clone)]
4265struct FunctionInfo {
4266    package_name: String,
4267    owner_path: Option<String>,
4268    name: String,
4269    signature: String,
4270}
4271
4272enum DeclaratorKind<'a> {
4273    Function(Node<'a>),
4274    Variable(Node<'a>),
4275}
4276
4277impl FunctionInfo {
4278    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
4279        self.code_unit_with_synthetic(file, false)
4280    }
4281
4282    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
4283        let short_name = if let Some(owner) = &self.owner_path {
4284            format!("{owner}.{}", self.name)
4285        } else {
4286            self.name.clone()
4287        };
4288        let fq = cpp_member_fq(&self.package_name, &short_name);
4289        CodeUnit::with_signature_and_fq(
4290            file,
4291            CodeUnitType::Function,
4292            self.package_name.clone(),
4293            short_name,
4294            Some(self.signature.clone()),
4295            synthetic,
4296            fq,
4297        )
4298    }
4299}
4300
4301fn extract_function_info(
4302    declarator: Node<'_>,
4303    source: &str,
4304    scope: &ScopeInfo,
4305) -> Option<FunctionInfo> {
4306    let parameters_node = declarator.child_by_field_name("parameters")?;
4307    let declarator_name_node = declarator
4308        .child_by_field_name("declarator")
4309        .or_else(|| parameters_node.prev_named_sibling())?;
4310    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
4311}
4312
4313fn extract_function_info_from_name(
4314    declarator: Node<'_>,
4315    declarator_name_node: Node<'_>,
4316    source: &str,
4317    scope: &ScopeInfo,
4318) -> Option<FunctionInfo> {
4319    let parameters_node = declarator.child_by_field_name("parameters")?;
4320    let parameters_text = cpp_parameter_signature(parameters_node, source);
4321    let recovered_specialization_member = scope
4322        .recovered_specialization_member_scope
4323        .then(|| {
4324            let terminal = declarator_name_node
4325                .child_by_field_name("name")
4326                .unwrap_or(declarator_name_node);
4327            let name = canonical_cpp_qualified_component(terminal, source)?.name;
4328            let owner = scope.class_unit.as_ref()?;
4329            Some((
4330                Some(owner.short_name().to_string()),
4331                name,
4332                scope.package_name.clone(),
4333            ))
4334        })
4335        .flatten();
4336    let (owner_path, name, package_name) = if let Some(parts) = recovered_specialization_member {
4337        parts
4338    } else if let Some(parts) =
4339        split_structured_templated_cpp_name(declarator_name_node, source, scope)
4340    {
4341        parts
4342    } else {
4343        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
4344            declarator_name_node,
4345            source,
4346        )?);
4347        if raw_name.is_empty() {
4348            return None;
4349        }
4350        split_cpp_name(&raw_name, scope)
4351    };
4352    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
4353    let mut signature = if suffix.is_empty() {
4354        parameters_text
4355    } else {
4356        format!("{parameters_text} {suffix}")
4357    };
4358    if let Some(template_signature) = &scope.template_signature {
4359        signature = format!("{template_signature}{signature}");
4360    }
4361
4362    Some(FunctionInfo {
4363        package_name,
4364        owner_path,
4365        name,
4366        signature,
4367    })
4368}
4369
4370/// Recover the semantic return type and callable name when a declaration macro
4371/// occupies a function definition's `type` field. In this exact tree-sitter
4372/// recovery shape the true return type is exposed as the declarator's apparent
4373/// name and the true callable name is the sole identifier in an `ERROR`
4374/// immediately before the complete parameter list.
4375fn cpp_macro_displaced_callable_parts<'tree>(
4376    function_declarator: Node<'tree>,
4377    source: &str,
4378) -> Option<(Node<'tree>, Node<'tree>)> {
4379    let definition = function_declarator.parent()?;
4380    if definition.kind() != "function_definition"
4381        || definition.child_by_field_name("declarator") != Some(function_declarator)
4382        || definition
4383            .child_by_field_name("body")
4384            .is_none_or(|body| body.kind() != "compound_statement")
4385    {
4386        return None;
4387    }
4388    let macro_type = definition.child_by_field_name("type")?;
4389    if macro_type.kind() != "type_identifier"
4390        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
4391    {
4392        return None;
4393    }
4394
4395    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
4396    if !matches!(
4397        apparent_return_type.kind(),
4398        "identifier" | "field_identifier" | "type_identifier"
4399    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
4400    {
4401        return None;
4402    }
4403    let parameters = function_declarator.child_by_field_name("parameters")?;
4404    let mut cursor = function_declarator.walk();
4405    let between = function_declarator
4406        .named_children(&mut cursor)
4407        .filter(|child| child.kind() != "comment")
4408        .filter(|child| {
4409            child.start_byte() >= apparent_return_type.end_byte()
4410                && child.end_byte() <= parameters.start_byte()
4411                && !same_node(*child, apparent_return_type)
4412                && !same_node(*child, parameters)
4413        })
4414        .collect::<Vec<_>>();
4415    let [name_error] = between.as_slice() else {
4416        return None;
4417    };
4418    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
4419        return None;
4420    }
4421    let callable_name = name_error.named_child(0)?;
4422    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
4423        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
4424    {
4425        return None;
4426    }
4427    Some((apparent_return_type, callable_name))
4428}
4429
4430/// The part of a `function_declarator` after its parameter list that belongs to
4431/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
4432/// specification, a trailing return type and a trailing requires-clause.
4433///
4434/// The grammar makes each of these a distinct sibling of the `parameters`
4435/// field, so they are read from the tree. Splitting the declarator's text on
4436/// the parameter list instead silently dropped every qualifier whenever the
4437/// parameter list was spelled with whitespace that normalization rewrote - a
4438/// line break or a double space was enough to make a `const` member definition
4439/// a different logical symbol from its declaration (#1827).
4440///
4441/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
4442/// are deliberately excluded. C++ does not make them part of the signature and
4443/// an out-of-line definition never repeats them, so including them would split
4444/// a declaration from its own definition.
4445fn cpp_declarator_identity_suffix(
4446    declarator: Node<'_>,
4447    parameters_node: Node<'_>,
4448    source: &str,
4449) -> String {
4450    let mut cursor = declarator.walk();
4451    let parts = declarator
4452        .named_children(&mut cursor)
4453        .filter(|child| child.start_byte() >= parameters_node.end_byte())
4454        .filter(|child| {
4455            matches!(
4456                child.kind(),
4457                "type_qualifier"
4458                    | "ref_qualifier"
4459                    | "noexcept"
4460                    | "throw_specifier"
4461                    | "trailing_return_type"
4462                    | "requires_clause"
4463            )
4464        })
4465        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
4466        .filter(|text| !text.is_empty())
4467        .collect::<Vec<_>>();
4468    normalize_cpp_qualifier_suffix(&parts.join(" "))
4469}
4470
4471fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
4472    match classify_declarator(node)? {
4473        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
4474        DeclaratorKind::Variable(_) => None,
4475    }
4476}
4477
4478fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
4479    match node.kind() {
4480        "function_declarator" => {
4481            let inner = node
4482                .child_by_field_name("declarator")
4483                .or_else(|| node.child_by_field_name("name"))
4484                .or_else(|| last_named_child(node));
4485            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
4486                Some(DeclaratorKind::Variable(node))
4487            } else {
4488                Some(DeclaratorKind::Function(node))
4489            }
4490        }
4491        "init_declarator"
4492        | "pointer_declarator"
4493        | "reference_declarator"
4494        | "parenthesized_declarator"
4495        | "array_declarator"
4496        | "attributed_declarator"
4497        | "template_function" => node
4498            .child_by_field_name("declarator")
4499            .or_else(|| node.child_by_field_name("name"))
4500            .or_else(|| last_named_child(node))
4501            .and_then(classify_declarator),
4502        "identifier" | "field_identifier" | "qualified_identifier" => {
4503            Some(DeclaratorKind::Variable(node))
4504        }
4505        _ => node
4506            .child_by_field_name("declarator")
4507            .or_else(|| node.child_by_field_name("name"))
4508            .or_else(|| last_named_child(node))
4509            .and_then(classify_declarator),
4510    }
4511}
4512
4513fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
4514    matches!(
4515        node.kind(),
4516        "function_declarator"
4517            | "init_declarator"
4518            | "pointer_declarator"
4519            | "reference_declarator"
4520            | "parenthesized_declarator"
4521            | "array_declarator"
4522            | "attributed_declarator"
4523            | "template_function"
4524            | "identifier"
4525            | "field_identifier"
4526            | "qualified_identifier"
4527    )
4528}
4529
4530fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
4531    let class_like = first_class_like_child(node);
4532    let mut cursor = node.walk();
4533    node.named_children(&mut cursor).any(|child| {
4534        matches!(
4535            child.kind(),
4536            "init_declarator"
4537                | "pointer_declarator"
4538                | "reference_declarator"
4539                | "array_declarator"
4540                | "function_declarator"
4541                | "parenthesized_declarator"
4542                | "attributed_declarator"
4543        ) || matches!(
4544            child.kind(),
4545            "identifier" | "field_identifier" | "qualified_identifier"
4546        ) && class_like.is_none_or(|class_node| {
4547            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
4548        })
4549    })
4550}
4551
4552/// Find the unique namespace-scope forward declaration that precedes a
4553/// recovered export-macro class definition.  Tree-sitter can close a malformed
4554/// class at the enclosing namespace's closing brace, leaving the later class
4555/// definitions as root-level recovered `function_definition` nodes.  A
4556/// preceding `class Name;` in the same namespace is the only structured identity
4557/// signal available in that shape.
4558///
4559/// The search is deliberately conservative: it only accepts a body-less class
4560/// specifier whose declaration has no declarator and is not nested in a function
4561/// or class body.  More than one matching namespace forward declaration is
4562/// ambiguous and returns `None` rather than guessing.
4563fn unique_earlier_cpp_namespace_forward(
4564    recovered_node: Node<'_>,
4565    name: &str,
4566    source: &str,
4567) -> Option<String> {
4568    let mut root = recovered_node;
4569    while let Some(parent) = root.parent() {
4570        root = parent;
4571    }
4572
4573    let mut candidates = Vec::new();
4574    let mut stack = vec![root];
4575    while let Some(current) = stack.pop() {
4576        if current.start_byte() < recovered_node.start_byte()
4577            && matches!(
4578                current.kind(),
4579                "class_specifier" | "struct_specifier" | "union_specifier"
4580            )
4581            && cpp_body_node(current).is_none()
4582            && current.parent().is_some_and(|parent| {
4583                parent.kind() == "declaration_list"
4584                    || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
4585            })
4586            && class_like_name(current, source).as_deref() == Some(name)
4587            && cpp_namespace_definition_for_forward(current).is_some_and(|namespace| {
4588                // Borrowing is only justified by the parser-recovery shape we
4589                // are repairing: the namespace that held the forward must
4590                // itself contain a syntax error and must have closed before
4591                // the root-level recovered class. A clean, unrelated
4592                // namespace forward is not an identity proof.
4593                namespace.has_error()
4594                    && namespace.end_byte() < recovered_node.start_byte()
4595                    && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
4596            })
4597            && let Some(package_name) = cpp_namespace_name_for_forward(current, source)
4598        {
4599            candidates.push(package_name);
4600        }
4601
4602        let mut cursor = current.walk();
4603        for child in current.named_children(&mut cursor) {
4604            if child.start_byte() < recovered_node.start_byte() {
4605                stack.push(child);
4606            }
4607        }
4608    }
4609
4610    if candidates.len() == 1 {
4611        candidates.pop()
4612    } else {
4613        None
4614    }
4615}
4616
4617fn malformed_namespace_is_nearest_recovery_region(
4618    namespace: Node<'_>,
4619    recovered_node: Node<'_>,
4620) -> bool {
4621    let mut root = recovered_node;
4622    while let Some(parent) = root.parent() {
4623        root = parent;
4624    }
4625    let mut cursor = root.walk();
4626    root.named_children(&mut cursor)
4627        .filter(|sibling| {
4628            namespace.end_byte() <= sibling.start_byte()
4629                && sibling.end_byte() <= recovered_node.start_byte()
4630        })
4631        .all(is_malformed_namespace_recovery_trivia)
4632}
4633
4634fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
4635    matches!(node.kind(), "ERROR" | "comment")
4636        || node.kind().starts_with("preproc_")
4637        || node.kind() == "expression_statement" && node.named_child_count() == 0
4638}
4639
4640/// Return the namespace path for a forward class only when the declaration is
4641/// at namespace scope.  A declaration nested in a function/class body may share
4642/// the same namespace ancestor but cannot identify a top-level class definition.
4643fn cpp_namespace_name_for_forward(node: Node<'_>, source: &str) -> Option<String> {
4644    cpp_namespace_definition_for_forward(node)?;
4645    cpp_lexical_namespace_name(node, source)
4646}
4647
4648fn cpp_namespace_definition_for_forward(node: Node<'_>) -> Option<Node<'_>> {
4649    let declaration = node.parent()?;
4650    let mut ancestor = declaration.parent();
4651    while let Some(current) = ancestor {
4652        if matches!(
4653            current.kind(),
4654            "compound_statement"
4655                | "field_declaration_list"
4656                | "class_specifier"
4657                | "struct_specifier"
4658                | "union_specifier"
4659                | "function_definition"
4660                | "lambda_expression"
4661        ) {
4662            return None;
4663        }
4664        if current.kind() == "namespace_definition" {
4665            return Some(current);
4666        }
4667        ancestor = current.parent();
4668    }
4669    None
4670}
4671
4672fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
4673    match node.kind() {
4674        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4675        "parenthesized_declarator" => node
4676            .child_by_field_name("declarator")
4677            .or_else(|| last_named_child(node))
4678            .is_some_and(is_pointer_wrapper_declarator),
4679        "template_function" => node
4680            .child_by_field_name("name")
4681            .is_some_and(is_function_pointer_like_inner_declarator),
4682        _ => false,
4683    }
4684}
4685
4686fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
4687    match node.kind() {
4688        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4689        "parenthesized_declarator" => node
4690            .child_by_field_name("declarator")
4691            .or_else(|| last_named_child(node))
4692            .is_some_and(is_pointer_wrapper_declarator),
4693        _ => false,
4694    }
4695}
4696
4697fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<String>, String, String) {
4698    let cleaned = raw_name.trim_start_matches("template ").trim();
4699    // A leading `::` is the explicit-global marker, not an empty owner segment.
4700    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
4701    // erroneous macro envelope swallowing the first identifier of an
4702    // out-of-line `X::X` constructor, chromium #1573); without this strip the
4703    // split below yields owner_parts `[""]`, constructing a unit with an empty
4704    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
4705    let cleaned = cleaned.trim_start_matches("::");
4706    // Parser recovery can preserve two adjacent scope operators around a
4707    // missing component (for example `X::/**/::method` in compiler diagnostic
4708    // fixtures). Empty components are syntax-recovery artifacts, never C++
4709    // owners. Keeping one as the final owner constructed `short_name=".method"`
4710    // and violated the structured package/short boundary during a large LLVM
4711    // workspace build. This is the same legacy-string-to-FqName bridge as the
4712    // ordinary split above; discard only components that the delimiter itself
4713    // proves empty.
4714    let parts: Vec<_> = cleaned
4715        .split("::")
4716        .filter(|component| !component.is_empty())
4717        .collect();
4718    if parts.is_empty() {
4719        return (None, cleaned.to_string(), scope.package_name.clone());
4720    }
4721    if parts.len() > 1 {
4722        let name = parts.last().unwrap_or(&cleaned).to_string();
4723        let owner_parts = &parts[..parts.len() - 1];
4724        if let Some(class_unit) = &scope.class_unit {
4725            // Lexically inside a class body: the owner is that class, whatever
4726            // the declarator re-qualifies it as.
4727            return (
4728                Some(class_unit.short_name().to_string()),
4729                name,
4730                scope.package_name.clone(),
4731            );
4732        }
4733        if !scope.package_name.is_empty() {
4734            // Out-of-line member definition written *inside* an enclosing
4735            // `namespace {}` block (scope package is that namespace). Every
4736            // owner segment before the terminal member is a class-nesting step
4737            // -- an out-of-line nested-class member `Outer::Inner::method` in
4738            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
4739            // namespace path: `using namespace` never brings nested-class
4740            // access into unqualified scope, so C++ always writes the full
4741            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
4742            // that redundantly re-states the enclosing namespace it already
4743            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
4744            // strip that re-qualifying prefix (which duplicates a suffix of the
4745            // enclosing package path) before treating what remains as the
4746            // nested-class chain, so the redundant spelling still lands on the
4747            // same `log4cxx.Foo.method` identity as its header declaration.
4748            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
4749            let owner_path = (!nested.is_empty()).then(|| nested.join("$"));
4750            return (owner_path, name, scope.package_name.clone());
4751        }
4752        // File scope (no enclosing `namespace {}` block, scope package empty).
4753        let (owner_path, package_name) = if owner_parts.len() > 1 {
4754            // A multi-segment qualifier at file scope with no enclosing
4755            // namespace: treat all but the last owner segment as the namespace
4756            // path and the last as the owning class (`ns1::ns2::Class::method`
4757            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
4758            // is really a namespace or an outer class cannot be told from the
4759            // declarator text alone here, and no enclosing namespace or
4760            // in-index owner is available at per-file extraction to confirm the
4761            // class reading, so the far-more-common namespace interpretation is
4762            // kept rather than guessed away (the nested-class-at-file-scope and
4763            // using-directive-qualified nested-class shapes remain on this
4764            // behavior; see #1121).
4765            (
4766                Some(owner_parts.last().unwrap_or(&"").to_string()),
4767                owner_parts[..owner_parts.len() - 1].join("::"),
4768            )
4769        } else {
4770            // A bare `Class::member` qualifier at file scope carries no
4771            // namespace segment of its own. The declarator alone cannot say
4772            // which namespace owns `Class` -- but a `using namespace X;`
4773            // directive already in effect at this point in the file (#1093,
4774            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
4775            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
4776            // is the remaining structural signal for it, so fall back to it
4777            // rather than leaving the definition's package empty while its
4778            // header declaration (parsed inside the `namespace {}` block) keeps
4779            // the real one -- an identity split that made the same member
4780            // unresolvable under its own displayed spelling.
4781            (
4782                Some(owner_parts[0].to_string()),
4783                cpp_using_directive_namespace_for_bare_owner(scope),
4784            )
4785        };
4786        return (owner_path, name, package_name);
4787    }
4788
4789    let package_name = scope.package_name.clone();
4790    let owner_path = scope
4791        .class_unit
4792        .as_ref()
4793        .map(|parent| parent.short_name().to_string());
4794    (owner_path, cleaned.to_string(), package_name)
4795}
4796
4797/// Drop the leading owner segments of an out-of-line member qualifier that
4798/// merely re-state the enclosing namespace the definition already sits in, so
4799/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
4800/// definition may redundantly write `a::b::Outer::Inner::method` (or the
4801/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
4802/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
4803/// noise, not class-nesting steps. Returns the owner segments with the longest
4804/// such re-qualifying prefix removed (possibly all of them, when the qualifier
4805/// names only the enclosing namespace before the terminal member -- a
4806/// re-qualified free function). `package_name` is the enclosing namespace path
4807/// in its stored `::`-joined form; both sides are split on the same delimiter
4808/// the namespace walker joined them with, so this compares namespace *segments*
4809/// rather than scanning text.
4810fn strip_redundant_namespace_prefix<'a>(
4811    owner_parts: &'a [&'a str],
4812    package_name: &str,
4813) -> &'a [&'a str] {
4814    if package_name.is_empty() {
4815        return owner_parts;
4816    }
4817    let package_segments: Vec<&str> = package_name.split("::").collect();
4818    let max_prefix = owner_parts.len().min(package_segments.len());
4819    for prefix_len in (1..=max_prefix).rev() {
4820        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
4821        if &owner_parts[..prefix_len] == package_suffix {
4822            return &owner_parts[prefix_len..];
4823        }
4824    }
4825    owner_parts
4826}
4827
4828/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
4829/// class name at file/namespace scope, from the `using namespace` directives
4830/// visible at this point in the file. Several may be in scope at once (a
4831/// primary `using namespace NS;` alongside deeper conveniences like `using
4832/// namespace NS::helpers;`); since the declarator gives no way to tell which
4833/// one actually declares the owner class, prefer the shallowest (fewest
4834/// `::`-separated segments) as the file's most likely "home" namespace,
4835/// breaking ties by declaration order. Returns an empty string (leaving the
4836/// caller's package unqualified, as before) when no using-namespace directive
4837/// is in scope.
4838fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
4839    scope
4840        .visible_using_namespaces
4841        .iter()
4842        .min_by_key(|namespace| namespace.split("::").count())
4843        .cloned()
4844        .unwrap_or_default()
4845}
4846
4847struct CppQualifiedNameComponent {
4848    name: String,
4849    is_template_id: bool,
4850}
4851
4852fn split_structured_templated_cpp_name(
4853    declarator_name: Node<'_>,
4854    source: &str,
4855    scope: &ScopeInfo,
4856) -> Option<(Option<String>, String, String)> {
4857    if declarator_name.kind() != "qualified_identifier" {
4858        return None;
4859    }
4860
4861    let mut components = Vec::new();
4862    let mut current = declarator_name;
4863    let mut explicitly_global = false;
4864    loop {
4865        if current.kind() == "qualified_identifier" {
4866            if let Some(component) = current.child_by_field_name("scope") {
4867                components.push(canonical_cpp_qualified_component(component, source)?);
4868            } else if components.is_empty() {
4869                explicitly_global = true;
4870            } else {
4871                return None;
4872            }
4873            current = current.child_by_field_name("name")?;
4874        } else {
4875            components.push(canonical_cpp_qualified_component(current, source)?);
4876            break;
4877        }
4878    }
4879
4880    let terminal = components.pop()?;
4881    let owner_start = components
4882        .iter()
4883        .position(|component| component.is_template_id)?;
4884    let explicit_package = components[..owner_start]
4885        .iter()
4886        .map(|component| component.name.as_str())
4887        .collect::<Vec<_>>()
4888        .join("::");
4889    let explicit_package_is_empty = explicit_package.is_empty();
4890    let package_name = match (
4891        explicitly_global,
4892        scope.package_name.is_empty(),
4893        explicit_package_is_empty,
4894    ) {
4895        (true, _, _) => explicit_package,
4896        (false, _, true) => scope.package_name.clone(),
4897        (false, true, false) => explicit_package,
4898        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
4899    };
4900    // Same identity-split fallback as `split_cpp_name` (#1093): a template
4901    // specialization's owner class named with no namespace segment of its own
4902    // (`explicit_package` empty) at file scope (`explicitly_global` false)
4903    // with nothing enclosing (`package_name` still empty) has no structural
4904    // signal for its namespace besides an in-scope `using namespace X;`.
4905    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
4906    {
4907        cpp_using_directive_namespace_for_bare_owner(scope)
4908    } else {
4909        package_name
4910    };
4911    let owner_path = components[owner_start..]
4912        .iter()
4913        .map(|component| component.name.as_str())
4914        .collect::<Vec<_>>()
4915        .join("$");
4916    if owner_path.is_empty() || terminal.name.is_empty() {
4917        return None;
4918    }
4919
4920    Some((Some(owner_path), terminal.name, package_name))
4921}
4922
4923fn canonical_cpp_qualified_component(
4924    mut component: Node<'_>,
4925    source: &str,
4926) -> Option<CppQualifiedNameComponent> {
4927    let mut is_template_id = false;
4928    loop {
4929        match component.kind() {
4930            "template_type" => {
4931                is_template_id = true;
4932                component = component.child_by_field_name("name")?;
4933            }
4934            "dependent_name" => component = component.named_child(0)?,
4935            "identifier"
4936            | "field_identifier"
4937            | "namespace_identifier"
4938            | "type_identifier"
4939            | "operator_name"
4940            | "destructor_name" => {
4941                let name = normalize_cpp_whitespace(node_text(component, source));
4942                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
4943                    name,
4944                    is_template_id,
4945                });
4946            }
4947            _ => component = component.child_by_field_name("name")?,
4948        }
4949    }
4950}
4951
4952fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
4953    match node.kind() {
4954        "identifier"
4955        | "field_identifier"
4956        | "type_identifier"
4957        | "operator_name"
4958        | "destructor_name"
4959        | "qualified_identifier" => node_text(node, source).to_string(),
4960        "function_declarator"
4961        | "pointer_declarator"
4962        | "reference_declarator"
4963        | "parenthesized_declarator"
4964        | "array_declarator"
4965        | "template_function" => node
4966            .child_by_field_name("declarator")
4967            .or_else(|| node.child_by_field_name("name"))
4968            .or_else(|| last_named_child(node))
4969            .map(|child| extract_declarator_name(child, source))
4970            .unwrap_or_else(|| node_text(node, source).to_string()),
4971        _ => node
4972            .child_by_field_name("name")
4973            .map(|child| extract_declarator_name(child, source))
4974            .unwrap_or_else(|| node_text(node, source).to_string()),
4975    }
4976}
4977
4978/// Extract a callable identity only through declaration-shaped AST nodes.
4979/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
4980/// expose the call's parameter list as a false function declarator; accepting
4981/// arbitrary node text there emitted bogus names such as `.*f`.
4982fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
4983    match node.kind() {
4984        "identifier"
4985        | "field_identifier"
4986        | "type_identifier"
4987        | "operator_name"
4988        | "destructor_name"
4989        | "qualified_identifier" => Some(node_text(node, source).to_string()),
4990        "function_declarator"
4991        | "pointer_declarator"
4992        | "reference_declarator"
4993        | "parenthesized_declarator"
4994        | "array_declarator"
4995        | "template_function" => node
4996            .child_by_field_name("declarator")
4997            .or_else(|| node.child_by_field_name("name"))
4998            .and_then(|child| extract_callable_declarator_name(child, source)),
4999        _ => None,
5000    }
5001}
5002
5003fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
5004    match node.kind() {
5005        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
5006            let name = node_text(node, source).trim().to_string();
5007            (!name.is_empty()).then_some(name)
5008        }
5009        _ => node
5010            .child_by_field_name("declarator")
5011            .or_else(|| node.child_by_field_name("name"))
5012            .or_else(|| last_named_child(node))
5013            .and_then(|child| extract_variable_name(child, source)),
5014    }
5015}
5016
5017fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
5018    let count = node.named_child_count();
5019    if count == 0 {
5020        None
5021    } else {
5022        node.named_child(count - 1)
5023    }
5024}
5025
5026fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
5027    let name_node = node.child_by_field_name("name")?;
5028    let name = normalize_cpp_whitespace(node_text(name_node, source));
5029    (!name.is_empty()).then_some(name)
5030}
5031
5032fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5033    if node.kind() != "declaration" {
5034        return Vec::new();
5035    }
5036    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
5037        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
5038    }) else {
5039        return Vec::new();
5040    };
5041    let Some(declarator) = node.child_by_field_name("declarator") else {
5042        return Vec::new();
5043    };
5044    if node_text(keyword, source) == "using"
5045        && (declarator.kind() != "init_declarator"
5046            || declarator.child_by_field_name("value").is_none())
5047    {
5048        return Vec::new();
5049    }
5050    if node_text(keyword, source) == "typedef"
5051        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
5052    {
5053        return vec![alias_name];
5054    }
5055    extract_typedef_declarator_name(declarator, source)
5056        .into_iter()
5057        .collect()
5058}
5059
5060fn recovered_typedef_error_alias_name(
5061    declaration: Node<'_>,
5062    declarator: Node<'_>,
5063    source: &str,
5064) -> Option<String> {
5065    // An export macro between `class` and its name can make tree-sitter parse
5066    // the recovered class body as a function body. In that shape,
5067    //
5068    //     typedef spi::Filter BASE_CLASS;
5069    //
5070    // becomes a declaration whose `declarator` is the underlying qualified
5071    // type (`spi::Filter`) and whose actual alias name is displaced into the
5072    // following ERROR node. Do not publish the terminal underlying type
5073    // (`Filter`) as a false class-owned alias.
5074    if declarator.kind() != "qualified_identifier" {
5075        return None;
5076    }
5077    let mut cursor = declaration.walk();
5078    let mut errors = declaration
5079        .named_children(&mut cursor)
5080        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
5081    let error = errors.next()?;
5082    if errors.next().is_some() || error.named_child_count() != 1 {
5083        return None;
5084    }
5085    let name = error.named_child(0)?;
5086    if !matches!(
5087        name.kind(),
5088        "identifier" | "field_identifier" | "type_identifier"
5089    ) {
5090        return None;
5091    }
5092    let name = normalize_cpp_whitespace(node_text(name, source));
5093    (!name.is_empty()).then_some(name)
5094}
5095
5096fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5097    // A function-like token in the type position can make tree-sitter expose
5098    // its argument as a parenthesized declarator. Do not publish that argument
5099    // as an alias. The macro-specific recovery below handles the proven shape.
5100    if fragmented_parenthesized_typedef_type(node).is_some() {
5101        return Vec::new();
5102    }
5103    let has_function_like_macro_type = node
5104        .child_by_field_name("type")
5105        .filter(|type_node| type_node.kind() == "type_identifier")
5106        .is_some_and(|type_node| {
5107            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5108        });
5109    let mut names = Vec::new();
5110    let mut cursor = node.walk();
5111    for declarator in node.children_by_field_name("declarator", &mut cursor) {
5112        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
5113            continue;
5114        }
5115        if let Some(name) = extract_typedef_declarator_name(declarator, source)
5116            && !names.contains(&name)
5117        {
5118            names.push(name);
5119        }
5120    }
5121    names
5122}
5123
5124struct RecoveredMacroTypedefAlias<'tree> {
5125    name: String,
5126    end_node: Node<'tree>,
5127}
5128
5129/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
5130/// into an identifier expression statement. The uppercase macro token, missing
5131/// typedef terminator, and complete sibling terminator prove this exact shape.
5132fn recovered_macro_typedef_alias<'tree>(
5133    node: Node<'tree>,
5134    source: &str,
5135) -> Option<RecoveredMacroTypedefAlias<'tree>> {
5136    let type_node = fragmented_parenthesized_typedef_type(node)?;
5137    if type_node.kind() != "type_identifier"
5138        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5139    {
5140        return None;
5141    }
5142
5143    let end_node = node.next_named_sibling()?;
5144    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
5145        return None;
5146    }
5147    let name_node = end_node.named_child(0)?;
5148    if name_node.kind() != "identifier" {
5149        return None;
5150    }
5151    let has_terminator = (0..end_node.child_count()).any(|index| {
5152        end_node
5153            .child(index)
5154            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
5155    });
5156    if !has_terminator {
5157        return None;
5158    }
5159    let name = normalize_cpp_whitespace(node_text(name_node, source));
5160    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
5161}
5162
5163fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
5164    if node.kind() != "type_definition" {
5165        return None;
5166    }
5167    let mut declarator_cursor = node.walk();
5168    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
5169    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
5170        return None;
5171    }
5172    let has_missing_terminator = (0..node.child_count()).any(|index| {
5173        node.child(index)
5174            .is_some_and(|child| child.kind() == ";" && child.is_missing())
5175    });
5176    if !has_missing_terminator {
5177        return None;
5178    }
5179    node.child_by_field_name("type")
5180}
5181
5182fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5183    match node.kind() {
5184        "identifier" | "field_identifier" | "type_identifier" => {
5185            let name = normalize_cpp_whitespace(node_text(node, source));
5186            (!name.is_empty()).then_some(name)
5187        }
5188        "qualified_identifier" => node
5189            .child_by_field_name("name")
5190            .and_then(|name| extract_typedef_declarator_name(name, source)),
5191        _ => node
5192            .child_by_field_name("declarator")
5193            .or_else(|| node.child_by_field_name("name"))
5194            .or_else(|| last_named_child(node))
5195            .and_then(|child| extract_typedef_declarator_name(child, source)),
5196    }
5197}
5198
5199fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
5200    let name = node
5201        .child_by_field_name("name")
5202        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5203        .or_else(|| {
5204            let mut cursor = node.walk();
5205            node.named_children(&mut cursor)
5206                .find(|child| {
5207                    matches!(
5208                        child.kind(),
5209                        "identifier" | "field_identifier" | "type_identifier"
5210                    )
5211                })
5212                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5213        })?;
5214    (!name.is_empty()).then_some(name)
5215}
5216
5217fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
5218    left.id() == right.id()
5219}
5220
5221fn render_cpp_type_signature(
5222    node: Node<'_>,
5223    source: &str,
5224    template_signature: Option<&str>,
5225) -> String {
5226    let text = normalize_cpp_whitespace(node_text(node, source));
5227    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
5228    let rendered = if head.ends_with(';') {
5229        head.to_string()
5230    } else {
5231        format!("{head} {{")
5232    };
5233    if let Some(template_signature) = template_signature {
5234        format!("template {template_signature} {rendered}")
5235    } else {
5236        rendered
5237    }
5238}
5239
5240fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
5241    if let Some(signature) =
5242        render_recovered_macro_qualified_field_signature(node, declarator, source)
5243    {
5244        return signature;
5245    }
5246    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
5247    let prefix = cpp_declaration_prefix(node, source);
5248    let name = extract_variable_name(declarator, source).unwrap_or_default();
5249    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
5250    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
5251        || (prefix.ends_with('&') && raw_suffix == "&")
5252    {
5253        String::new()
5254    } else {
5255        raw_suffix
5256    };
5257
5258    let mut rendered = if suffix.is_empty() {
5259        format!("{prefix} {name}")
5260    } else if suffix.starts_with('*') || suffix.starts_with('&') {
5261        format!("{prefix}{suffix} {name}")
5262    } else if suffix.starts_with('[') || suffix.starts_with('(') {
5263        format!("{prefix} {name}{suffix}")
5264    } else {
5265        format!("{prefix} {suffix}{name}")
5266    };
5267    rendered = collapse_cpp_whitespace(&rendered);
5268
5269    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5270        format!("{rendered} = {initializer};")
5271    } else if declaration_text.ends_with(';') {
5272        format!("{rendered};")
5273    } else {
5274        rendered
5275    }
5276}
5277
5278fn render_recovered_macro_qualified_field_signature(
5279    node: Node<'_>,
5280    declarator: Node<'_>,
5281    source: &str,
5282) -> Option<String> {
5283    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
5284    if !recovered
5285        .iter()
5286        .any(|candidate| same_node(*candidate, declarator))
5287    {
5288        return None;
5289    }
5290    let pseudo_declarator = node.child_by_field_name("declarator")?;
5291    let mut cursor = node.walk();
5292    let clause = node
5293        .named_children(&mut cursor)
5294        .find(|child| child.kind() == "bitfield_clause")?;
5295    let mut cursor = clause.walk();
5296    let error = clause
5297        .named_children(&mut cursor)
5298        .find(|child| child.kind() == "ERROR")?;
5299    let qualified_type =
5300        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
5301    let prefix = cpp_declaration_prefix(node, source);
5302    let name = extract_variable_name(declarator, source)?;
5303    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
5304    let mut rendered = if suffix.is_empty() {
5305        format!("{prefix} {qualified_type} {name}")
5306    } else {
5307        format!("{prefix} {qualified_type} {suffix} {name}")
5308    };
5309    rendered = collapse_cpp_whitespace(&rendered);
5310
5311    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
5312        Some(format!(
5313            "{rendered} = {};",
5314            normalize_cpp_whitespace(node_text(initializer, source))
5315        ))
5316    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5317        Some(format!("{rendered} = {initializer};"))
5318    } else {
5319        Some(format!("{rendered};"))
5320    }
5321}
5322
5323fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
5324    match node.kind() {
5325        "pointer_expression" => {
5326            let operator = node
5327                .child_by_field_name("operator")
5328                .or_else(|| node.child(0))
5329                .map(|operator| node_text(operator, source))
5330                .unwrap_or("*");
5331            let argument = node
5332                .child_by_field_name("argument")
5333                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
5334                .unwrap_or_default();
5335            format!("{operator}{argument}")
5336        }
5337        "unary_expression" => {
5338            let operator = node
5339                .child_by_field_name("operator")
5340                .or_else(|| node.child(0))
5341                .map(|operator| node_text(operator, source))
5342                .unwrap_or_default();
5343            let argument = node
5344                .child_by_field_name("argument")
5345                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
5346                .unwrap_or_default();
5347            format!("{operator}{argument}")
5348        }
5349        "identifier" | "field_identifier" => String::new(),
5350        _ => cpp_declarator_suffix_without_name(node, source),
5351    }
5352}
5353
5354fn recovered_macro_qualified_field_initializer<'tree>(
5355    clause: Node<'tree>,
5356    declarator: Node<'tree>,
5357) -> Option<Node<'tree>> {
5358    let mut stack = vec![clause];
5359    while let Some(current) = stack.pop() {
5360        if current.kind() == "assignment_expression"
5361            && current
5362                .child_by_field_name("left")
5363                .is_some_and(|left| same_node(left, declarator))
5364        {
5365            return current.child_by_field_name("right");
5366        }
5367        let mut cursor = current.walk();
5368        stack.extend(current.named_children(&mut cursor));
5369    }
5370    None
5371}
5372
5373fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
5374    let text = node_text(node, source);
5375    let mut cursor = node.walk();
5376    let first_declarator = node.named_children(&mut cursor).find(|child| {
5377        matches!(
5378            child.kind(),
5379            "init_declarator"
5380                | "identifier"
5381                | "field_identifier"
5382                | "pointer_declarator"
5383                | "reference_declarator"
5384                | "array_declarator"
5385                | "function_declarator"
5386        )
5387    });
5388    let prefix = if let Some(first_declarator) = first_declarator {
5389        let end = first_declarator
5390            .start_byte()
5391            .saturating_sub(node.start_byte());
5392        let mut prefix = text.get(..end).unwrap_or(text).to_string();
5393        let declarator_suffix = match first_declarator.kind() {
5394            "init_declarator" => first_declarator
5395                .child_by_field_name("declarator")
5396                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
5397                .unwrap_or_default(),
5398            _ => cpp_declarator_suffix_without_name(first_declarator, source),
5399        };
5400        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
5401            prefix.push_str(&declarator_suffix);
5402        }
5403        return collapse_cpp_whitespace(&prefix)
5404            .trim_end_matches(',')
5405            .trim_end_matches(';')
5406            .trim()
5407            .to_string();
5408    } else {
5409        text
5410    };
5411    collapse_cpp_whitespace(prefix)
5412        .trim_end_matches(',')
5413        .trim_end_matches(';')
5414        .trim()
5415        .to_string()
5416}
5417
5418fn cpp_preserved_initializer(
5419    declaration_node: Node<'_>,
5420    declarator: Node<'_>,
5421    source: &str,
5422) -> Option<String> {
5423    let name = extract_variable_name(declarator, source)?;
5424    let mut cursor = declaration_node.walk();
5425    for child in declaration_node.named_children(&mut cursor) {
5426        if child.kind() != "init_declarator" {
5427            continue;
5428        }
5429        let Some(inner) = child.child_by_field_name("declarator") else {
5430            continue;
5431        };
5432        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
5433            continue;
5434        }
5435        let value = child.child_by_field_name("value")?;
5436        let kind = value.kind();
5437        if matches!(
5438            kind,
5439            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
5440        ) {
5441            return Some(normalize_cpp_whitespace(node_text(value, source)));
5442        }
5443        break;
5444    }
5445    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
5446    let pattern = format!(
5447        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
5448        regex::escape(&name)
5449    );
5450    Regex::new(&pattern)
5451        .ok()
5452        .and_then(|regex| regex.captures(&declaration_text))
5453        .and_then(|captures| captures.get(1))
5454        .map(|value| value.as_str().to_string())
5455}
5456
5457fn render_cpp_function_display_signature_from_node(
5458    node: Node<'_>,
5459    source: &str,
5460    template_signature: Option<&str>,
5461    has_body: bool,
5462) -> String {
5463    let root = enclosing_cpp_declaration_node(node).unwrap_or(node);
5464    let parent_text = node_text(root, source);
5465    let body_local_start = root
5466        .child_by_field_name("body")
5467        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
5468        .unwrap_or(parent_text.len());
5469    let display = parent_text
5470        .get(..body_local_start)
5471        .unwrap_or(parent_text)
5472        .trim()
5473        .trim();
5474    let display = if let Some(template_signature) = template_signature {
5475        if display.starts_with("template ") {
5476            display.to_string()
5477        } else {
5478            format!("template {template_signature} {display}")
5479        }
5480    } else {
5481        display.to_string()
5482    };
5483    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
5484    if has_body {
5485        format!("{display} {{...}}")
5486    } else {
5487        format!("{display};")
5488    }
5489}
5490
5491fn cpp_template_signature(
5492    template_node: Node<'_>,
5493    declaration_child: Node<'_>,
5494    source: &str,
5495) -> Option<String> {
5496    let text = source
5497        .get(template_node.start_byte()..declaration_child.start_byte())
5498        .unwrap_or("");
5499    let text = normalize_cpp_whitespace(text);
5500    let start = text.find('<')?;
5501    let end = text.rfind('>')?;
5502    if end < start {
5503        return None;
5504    }
5505    Some(text[start..=end].to_string())
5506}
5507
5508struct RecoveredFragmentedPartialSpecialization<'tree> {
5509    declaration_node: Node<'tree>,
5510    name: String,
5511    range: Range,
5512    prefix_members: Vec<Node<'tree>>,
5513    member_siblings: Vec<Node<'tree>>,
5514    following_declarations: Vec<Node<'tree>>,
5515}
5516
5517struct RecoveredFragmentedPreprocessorClass<'tree> {
5518    declaration_node: Node<'tree>,
5519    class_node: Node<'tree>,
5520    body: Node<'tree>,
5521    name: String,
5522    range: Range,
5523    tail_members: Vec<Node<'tree>>,
5524    member_siblings: Vec<Node<'tree>>,
5525}
5526
5527/// Recover a class whose preprocessor-fragmented parse closes at an early
5528/// member body and publishes the remaining in-class declarations as siblings
5529/// of the surrounding alternative. Primary classes are admitted only when an
5530/// earlier branch contains the matching bodyless declaration and the class
5531/// node retains the displaced `#endif`. Partial specializations instead carry
5532/// their identity structurally in the `template_type` name and template
5533/// metadata. Retain the original AST nodes and re-own only the siblings through
5534/// the displaced structural `};` terminator.
5535fn recover_fragmented_preprocessor_class<'tree>(
5536    template_node: Node<'tree>,
5537    source: &str,
5538) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
5539    let alternative = template_node.parent()?;
5540    if alternative.kind() != "preproc_else" {
5541        return None;
5542    }
5543    let conditional = alternative.parent()?;
5544    if conditional.kind() != "preproc_if" {
5545        return None;
5546    }
5547    let declaration_node = template_node
5548        .named_children(&mut template_node.walk())
5549        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
5550    let class_node = declaration_node
5551        .named_children(&mut declaration_node.walk())
5552        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
5553    let body = cpp_body_node(class_node)?;
5554    if class_node.end_byte() >= declaration_node.end_byte() {
5555        return None;
5556    }
5557    let name = class_like_name(class_node, source)?;
5558    let is_partial_specialization = class_node
5559        .child_by_field_name("name")
5560        .is_some_and(|class_name| class_name.kind() == "template_type");
5561    if is_partial_specialization {
5562        let metadata = cpp_template_metadata(template_node, class_node, source)?;
5563        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
5564            return None;
5565        }
5566    } else {
5567        if !class_has_displaced_preprocessor_terminator(class_node) {
5568            return None;
5569        }
5570        let matching_other_branch = conditional
5571            .named_children(&mut conditional.walk())
5572            .take_while(|child| !same_node(*child, alternative))
5573            .filter(|child| child.kind() == "template_declaration")
5574            .filter_map(first_class_like_child)
5575            .any(|candidate| {
5576                cpp_body_node(candidate).is_none()
5577                    && class_like_name(candidate, source).as_deref() == Some(name.as_str())
5578            });
5579        if !matching_other_branch {
5580            return None;
5581        }
5582    }
5583
5584    let mut tail_members = Vec::new();
5585    let mut saw_class = false;
5586    let mut declaration_cursor = declaration_node.walk();
5587    for child in declaration_node.named_children(&mut declaration_cursor) {
5588        if same_node(child, class_node) {
5589            saw_class = true;
5590        } else if saw_class {
5591            tail_members.push(child);
5592        }
5593    }
5594
5595    let mut member_siblings = Vec::new();
5596    let mut saw_template = false;
5597    let mut terminator = None;
5598    for index in 0..alternative.child_count() {
5599        let Some(child) = alternative.child(index) else {
5600            continue;
5601        };
5602        if same_node(child, template_node) {
5603            saw_template = true;
5604            continue;
5605        }
5606        if !saw_template {
5607            continue;
5608        }
5609        if displaced_fragmented_class_terminator(alternative, index) {
5610            terminator = alternative.child(index + 1);
5611            break;
5612        }
5613        if child.is_named() {
5614            member_siblings.push(child);
5615        }
5616    }
5617    let terminator = terminator?;
5618    Some(RecoveredFragmentedPreprocessorClass {
5619        declaration_node,
5620        class_node,
5621        body,
5622        name,
5623        range: Range {
5624            start_byte: class_node.start_byte(),
5625            end_byte: terminator.end_byte(),
5626            start_line: class_node.start_position().row + 1,
5627            end_line: terminator.end_position().row + 1,
5628        },
5629        tail_members,
5630        member_siblings,
5631    })
5632}
5633
5634fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
5635    (0..class_node.child_count()).any(|index| {
5636        class_node.child(index).is_some_and(|child| {
5637            child.kind() == "ERROR"
5638                && (0..child.child_count()).any(|error_index| {
5639                    child
5640                        .child(error_index)
5641                        .is_some_and(|token| token.kind() == "#endif")
5642                })
5643        })
5644    })
5645}
5646
5647/// The real `#endif` that tree-sitter consumed inside an error subtree.
5648///
5649/// A preprocessor directive inside a malformed array bound can cause later
5650/// declarations to remain children of the conditional. The non-missing token
5651/// still gives the exact structured boundary. Ignore nested conditionals and
5652/// select the last error-owned token. Tree-sitter can pair a later outer
5653/// `#endif` with this conditional, so the direct terminator is not necessarily
5654/// missing.
5655pub fn cpp_displaced_preprocessor_terminator<'tree>(
5656    conditional: Node<'tree>,
5657) -> Option<Node<'tree>> {
5658    if !conditional.has_error() {
5659        return None;
5660    }
5661    let has_concrete_direct_terminator = conditional
5662        .child_count()
5663        .checked_sub(1)
5664        .and_then(|index| conditional.child(index))
5665        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
5666    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
5667        // A structured alternative proves that the direct `#endif` closes
5668        // this family. An error-owned terminator inside either branch belongs
5669        // to a damaged nested conditional, not to this one.
5670        return None;
5671    }
5672    let mut displaced = None;
5673    let mut stack = (0..conditional.child_count())
5674        .filter_map(|index| conditional.child(index))
5675        .map(|child| (child, false))
5676        .collect::<Vec<_>>();
5677    while let Some((node, inside_error)) = stack.pop() {
5678        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
5679            continue;
5680        }
5681        if node.kind() == "#endif" && !node.is_missing() && inside_error {
5682            if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
5683                displaced = Some(node);
5684            }
5685            continue;
5686        }
5687        if node != conditional
5688            && matches!(
5689                node.kind(),
5690                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
5691            )
5692        {
5693            continue;
5694        }
5695        let inside_error = inside_error || node.kind() == "ERROR";
5696        for index in 0..node.child_count() {
5697            if let Some(child) = node.child(index) {
5698                stack.push((child, inside_error));
5699            }
5700        }
5701    }
5702    displaced
5703}
5704
5705/// The effective end of a conditional whose real terminator tree-sitter
5706/// displaced into declaration recovery.
5707///
5708/// Most damaged conditionals retain a concrete `#endif` token below an
5709/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
5710/// boundary. A preprocessor family that selects the middle of a declaration
5711/// can lose the directive tokens entirely. In that shape tree-sitter leaves
5712/// the declaration's `typedef` token as the sole child of the immediately
5713/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
5714/// declarator name inside the conditional's first declaration. The declaration
5715/// end is then the smallest structured boundary that contains the whole split
5716/// declaration.
5717#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5718pub struct CppDisplacedPreprocessorBoundary {
5719    pub end_byte: usize,
5720    pub end_line: usize,
5721}
5722
5723pub fn cpp_displaced_preprocessor_boundary(
5724    conditional: Node<'_>,
5725) -> Option<CppDisplacedPreprocessorBoundary> {
5726    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
5727        return Some(CppDisplacedPreprocessorBoundary {
5728            end_byte: terminator.end_byte(),
5729            end_line: terminator.end_position().row + 1,
5730        });
5731    }
5732    if let Some(declaration) = displaced_split_declaration(conditional) {
5733        return Some(CppDisplacedPreprocessorBoundary {
5734            end_byte: declaration.end_byte(),
5735            end_line: declaration.end_position().row + 1,
5736        });
5737    }
5738    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
5739        return Some(CppDisplacedPreprocessorBoundary {
5740            end_byte: terminator.end_byte(),
5741            end_line: terminator.end_position().row + 1,
5742        });
5743    }
5744    None
5745}
5746
5747fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
5748    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
5749        return None;
5750    }
5751    let mut cursor = conditional.walk();
5752    let declarations = conditional
5753        .named_children(&mut cursor)
5754        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
5755        .collect::<Vec<_>>();
5756    let declaration = *declarations.first()?;
5757    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
5758        return None;
5759    }
5760    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
5761    let mut terminator = None;
5762    let mut stack = (0..declaration.child_count())
5763        .filter_map(|index| declaration.child(index))
5764        .filter(|child| child.start_byte() < declarator_start)
5765        .map(|child| (child, false))
5766        .collect::<Vec<_>>();
5767    while let Some((node, inside_error)) = stack.pop() {
5768        let inside_error = inside_error || node.kind() == "ERROR";
5769        if inside_error && node.kind() == "#endif" && !node.is_missing() {
5770            terminator = Some(node);
5771            continue;
5772        }
5773        for index in 0..node.child_count() {
5774            if let Some(child) = node.child(index)
5775                && child.start_byte() < declarator_start
5776            {
5777                stack.push((child, inside_error));
5778            }
5779        }
5780    }
5781    terminator
5782}
5783
5784fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
5785    if !conditional.has_error()
5786        || conditional.child_by_field_name("alternative").is_some()
5787        || conditional
5788            .prev_named_sibling()
5789            .filter(|sibling| {
5790                sibling.kind() == "ERROR"
5791                    && sibling.child_count() == 1
5792                    && sibling
5793                        .child(0)
5794                        .is_some_and(|child| child.kind() == "typedef")
5795            })
5796            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
5797            .is_none()
5798    {
5799        return None;
5800    }
5801    let mut cursor = conditional.walk();
5802    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
5803    let declaration_index = children
5804        .iter()
5805        .position(|child| child.kind() == "declaration" && child.has_error())?;
5806    let declaration = children[declaration_index];
5807    if !children
5808        .iter()
5809        .skip(declaration_index + 1)
5810        .any(|child| child.end_byte() > declaration.end_byte())
5811    {
5812        return None;
5813    }
5814    let declarator = declaration.child_by_field_name("declarator")?;
5815    let mut error_end = None;
5816    let mut names = Vec::new();
5817    let mut stack = vec![declarator];
5818    while let Some(node) = stack.pop() {
5819        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
5820            error_end =
5821                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
5822            continue;
5823        }
5824        if matches!(node.kind(), "identifier" | "type_identifier") {
5825            names.push(node.start_byte());
5826        }
5827        for index in (0..node.named_child_count()).rev() {
5828            if let Some(child) = node.named_child(index) {
5829                stack.push(child);
5830            }
5831        }
5832    }
5833    let error_end = error_end?;
5834    names
5835        .into_iter()
5836        .any(|start| start >= error_end)
5837        .then_some(declaration)
5838}
5839
5840fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
5841    let Some(error) = parent.child(error_index) else {
5842        return false;
5843    };
5844    if error.kind() != "ERROR"
5845        || error.child_count() != 1
5846        || error.child(0).is_none_or(|child| child.kind() != "}")
5847    {
5848        return false;
5849    }
5850    let Some(semicolon) = parent.child(error_index + 1) else {
5851        return false;
5852    };
5853    semicolon.kind() == "expression_statement"
5854        && semicolon.child_count() == 1
5855        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
5856}
5857
5858/// Locate the real end of a class-like declaration when a macro invocation
5859/// without a source semicolon absorbs the class's `};` into its parsed field.
5860/// The grammar then keeps following namespace declarations as later children
5861/// of the same field list. The direct ERROR-plus-semicolon pair proves the
5862/// boundary structurally; no source-text delimiter scan is needed.
5863fn displaced_macro_class_tail(
5864    declaration_node: Node<'_>,
5865    body: Node<'_>,
5866    source: &str,
5867) -> Option<DisplacedMacroClassTail> {
5868    if !matches!(
5869        declaration_node.kind(),
5870        "class_specifier" | "struct_specifier" | "union_specifier"
5871    ) || body.kind() != "field_declaration_list"
5872    {
5873        return None;
5874    }
5875
5876    let child_count = body.named_child_count();
5877    for index in 0..child_count {
5878        let child = body.named_child(index)?;
5879        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
5880            continue;
5881        };
5882        let split_index = index + 1;
5883        if split_index >= child_count {
5884            return None;
5885        }
5886        let mut cursor = body.walk();
5887        if !body
5888            .named_children(&mut cursor)
5889            .skip(split_index)
5890            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
5891        {
5892            return None;
5893        }
5894        return Some(DisplacedMacroClassTail {
5895            split_index,
5896            class_range: Range {
5897                start_byte: declaration_node.start_byte(),
5898                end_byte: terminator.end_byte(),
5899                start_line: declaration_node.start_position().row + 1,
5900                end_line: terminator.end_position().row + 1,
5901            },
5902        });
5903    }
5904    None
5905}
5906
5907fn displaced_macro_field_terminator<'tree>(
5908    field: Node<'tree>,
5909    source: &str,
5910) -> Option<Node<'tree>> {
5911    if field.kind() != "field_declaration" {
5912        return None;
5913    }
5914    let macro_type = field.child_by_field_name("type")?;
5915    if macro_type.kind() != "type_identifier"
5916        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
5917        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
5918    {
5919        return None;
5920    }
5921    for index in 0..field.child_count() {
5922        let error = field.child(index)?;
5923        if error.kind() != "ERROR"
5924            || error.child_count() != 1
5925            || error.child(0).is_none_or(|child| child.kind() != "}")
5926        {
5927            continue;
5928        }
5929        let semicolon = field.child(index + 1)?;
5930        if semicolon.kind() == ";" {
5931            return Some(semicolon);
5932        }
5933    }
5934    None
5935}
5936
5937fn recover_fragmented_partial_specialization<'tree>(
5938    template_node: Node<'tree>,
5939    declaration_child: Node<'tree>,
5940    source: &str,
5941) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
5942    if declaration_child.kind() != "function_definition" {
5943        return None;
5944    }
5945    let class_node = declaration_child.child_by_field_name("type")?;
5946    if !matches!(
5947        class_node.kind(),
5948        "class_specifier" | "struct_specifier" | "union_specifier"
5949    ) || !class_node
5950        .child_by_field_name("name")
5951        .and_then(|name| direct_identifier_name(name, source))
5952        .is_some_and(|name| cpp_export_macro_token(&name))
5953    {
5954        return None;
5955    }
5956    let declarator = declaration_child.child_by_field_name("declarator")?;
5957    if declarator.kind() != "template_function" {
5958        return None;
5959    }
5960    let metadata = cpp_template_metadata(template_node, declaration_child, source)?;
5961    if metadata.specialization_arguments.is_empty() {
5962        return None;
5963    }
5964    let body = declaration_child.child_by_field_name("body")?;
5965    if body.kind() != "compound_statement" {
5966        return None;
5967    }
5968    let complete_prefix = body.named_child(0).filter(|first| {
5969        first.kind() == "labeled_statement"
5970            && first.has_error()
5971            && first
5972                .named_child(first.named_child_count().saturating_sub(1))
5973                .is_some_and(recovered_declaration_has_class_terminator)
5974    });
5975    let complete_body = complete_prefix.is_some();
5976    let mut prefix_members = Vec::new();
5977    if let Some(prefix) = complete_prefix {
5978        prefix_members.push(prefix);
5979    } else {
5980        let mut body_cursor = body.walk();
5981        for child in body.named_children(&mut body_cursor) {
5982            if !is_structurally_valid_fragmented_class_prefix_member(child) {
5983                break;
5984            }
5985            prefix_members.push(child);
5986        }
5987    }
5988    let containing_declarations = template_node.parent()?;
5989    if !matches!(
5990        containing_declarations.kind(),
5991        "declaration_list" | "compound_statement"
5992    ) {
5993        return None;
5994    }
5995    let mut member_siblings = Vec::new();
5996    let mut following_declarations = Vec::new();
5997    let terminator;
5998    if complete_body {
5999        terminator = complete_prefix?;
6000        let mut cursor = body.walk();
6001        let mut after_prefix = false;
6002        for child in body.named_children(&mut cursor) {
6003            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
6004                after_prefix = true;
6005            } else if after_prefix {
6006                following_declarations.push(child);
6007            }
6008        }
6009    } else {
6010        let mut found_template = false;
6011        let mut cursor = containing_declarations.walk();
6012        let mut class_terminator = None;
6013        for child in containing_declarations.children(&mut cursor) {
6014            if same_node(child, template_node) {
6015                found_template = true;
6016                continue;
6017            }
6018            if found_template && child.kind() == "}" {
6019                class_terminator = Some(child);
6020                break;
6021            }
6022            if found_template && child.is_named() {
6023                member_siblings.push(child);
6024            }
6025        }
6026        terminator = class_terminator?;
6027    }
6028    let name = format!(
6029        "{}<{}>",
6030        metadata.primary_name,
6031        metadata
6032            .specialization_arguments
6033            .iter()
6034            .map(|argument| argument.text.as_str())
6035            .collect::<Vec<_>>()
6036            .join(", ")
6037    );
6038    Some(RecoveredFragmentedPartialSpecialization {
6039        declaration_node: declaration_child,
6040        name,
6041        range: Range {
6042            start_byte: declaration_child.start_byte(),
6043            end_byte: terminator.end_byte(),
6044            start_line: declaration_child.start_position().row + 1,
6045            end_line: terminator.end_position().row + 1,
6046        },
6047        prefix_members,
6048        member_siblings,
6049        following_declarations,
6050    })
6051}
6052
6053fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
6054    if declaration.kind() != "declaration" {
6055        return false;
6056    }
6057    // With an export macro between `class` and its name, tree-sitter folds a
6058    // complete class body into a function-shaped declaration. The class's own
6059    // `};` remains structurally identifiable as a direct ERROR child holding
6060    // `}`, immediately followed by the declaration's direct `;` child.
6061    (0..declaration.child_count().saturating_sub(1)).any(|index| {
6062        let Some(error) = declaration.child(index) else {
6063            return false;
6064        };
6065        error.kind() == "ERROR"
6066            && error.child_count() == 1
6067            && error.child(0).is_some_and(|child| child.kind() == "}")
6068            && declaration
6069                .child(index + 1)
6070                .is_some_and(|child| child.kind() == ";")
6071    })
6072}
6073
6074fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
6075    if node.has_error() {
6076        return false;
6077    }
6078    match node.kind() {
6079        "declaration"
6080        | "field_declaration"
6081        | "alias_declaration"
6082        | "type_definition"
6083        | "static_assert_declaration" => true,
6084        "labeled_statement" => node
6085            .named_child(node.named_child_count().saturating_sub(1))
6086            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
6087        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
6088            matches!(
6089                child.kind(),
6090                "declaration"
6091                    | "field_declaration"
6092                    | "alias_declaration"
6093                    | "type_definition"
6094                    | "function_definition"
6095            )
6096        }),
6097        _ => false,
6098    }
6099}
6100
6101fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
6102    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
6103        .then(|| node.child_by_field_name("declarator"))
6104        .flatten()
6105        .and_then(|declarator| extract_variable_name(declarator, source))
6106}
6107
6108fn cpp_template_metadata(
6109    template_node: Node<'_>,
6110    declaration_child: Node<'_>,
6111    source: &str,
6112) -> Option<CppTemplateMetadata> {
6113    let parameters_node = template_node.child_by_field_name("parameters")?;
6114    let name_node = cpp_templated_class_name_node(declaration_child)?;
6115    let primary_node = match name_node.kind() {
6116        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
6117        _ => name_node,
6118    };
6119    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
6120    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
6121        return None;
6122    }
6123
6124    let mut parameter_nodes = Vec::new();
6125    let mut parameter_names = Vec::new();
6126    let mut cursor = parameters_node.walk();
6127    for parameter in parameters_node.named_children(&mut cursor) {
6128        let Some(name) = cpp_template_parameter_name(parameter, source) else {
6129            continue;
6130        };
6131        parameter_names.push(name);
6132        parameter_nodes.push(parameter);
6133    }
6134    let parameters = parameter_nodes
6135        .into_iter()
6136        .zip(parameter_names.iter().cloned())
6137        .map(|(parameter, name)| CppTemplateParameterMetadata {
6138            name,
6139            kind: cpp_template_parameter_kind(parameter),
6140            variadic: matches!(
6141                parameter.kind(),
6142                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
6143            ),
6144            default: cpp_template_parameter_default_expression(parameter, source, &parameter_names),
6145        })
6146        .collect();
6147    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
6148        Vec::new()
6149    } else {
6150        cpp_template_argument_expressions(name_node, source, &parameter_names).unwrap_or_default()
6151    };
6152    let alias_target = (declaration_child.kind() == "alias_declaration")
6153        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names))
6154        .flatten();
6155    Some(CppTemplateMetadata {
6156        primary_name,
6157        primary_fq_name: String::new(),
6158        parameters,
6159        specialization_arguments,
6160        alias_target,
6161    })
6162}
6163
6164fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
6165    match node.kind() {
6166        "class_specifier" | "struct_specifier" | "union_specifier" => {
6167            node.child_by_field_name("name")
6168        }
6169        "function_definition" => {
6170            let declarator = node.child_by_field_name("declarator")?;
6171            if matches!(declarator.kind(), "identifier" | "template_function") {
6172                Some(declarator)
6173            } else {
6174                None
6175            }
6176        }
6177        "alias_declaration" => node.child_by_field_name("name"),
6178        _ => None,
6179    }
6180}
6181
6182fn cpp_template_alias_target(
6183    alias: Node<'_>,
6184    source: &str,
6185    parameter_names: &[String],
6186) -> Option<CppTemplateAliasTargetMetadata> {
6187    let mut type_node = alias.child_by_field_name("type")?;
6188    while type_node.kind() == "type_descriptor" {
6189        type_node = type_node.child_by_field_name("type")?;
6190    }
6191    let global = type_node.child_by_field_name("scope").is_none()
6192        && type_node.child(0).is_some_and(|child| child.kind() == "::");
6193    let mut components = Vec::new();
6194    cpp_template_target_components(type_node, source, &mut components)?;
6195    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names);
6196    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
6197        components,
6198        global,
6199        arguments,
6200    })
6201}
6202
6203fn cpp_template_target_components(
6204    node: Node<'_>,
6205    source: &str,
6206    out: &mut Vec<String>,
6207) -> Option<()> {
6208    match node.kind() {
6209        "identifier" | "namespace_identifier" | "type_identifier" => {
6210            out.push(node_text(node, source).to_string());
6211            Some(())
6212        }
6213        "template_type" => {
6214            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6215        }
6216        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
6217            if let Some(scope) = node.child_by_field_name("scope") {
6218                cpp_template_target_components(scope, source, out)?;
6219            }
6220            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6221        }
6222        _ => None,
6223    }
6224}
6225
6226fn cpp_template_argument_expressions(
6227    mut node: Node<'_>,
6228    source: &str,
6229    parameter_names: &[String],
6230) -> Option<Vec<CppTemplateExpression>> {
6231    loop {
6232        match node.kind() {
6233            "template_type" | "template_function" => {
6234                let arguments = node.child_by_field_name("arguments")?;
6235                let mut cursor = arguments.walk();
6236                return Some(
6237                    arguments
6238                        .named_children(&mut cursor)
6239                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
6240                        .map(|argument| cpp_template_expression(argument, source, parameter_names))
6241                        .collect(),
6242                );
6243            }
6244            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
6245                node = node
6246                    .child_by_field_name("name")
6247                    .or_else(|| node.child_by_field_name("type"))?;
6248            }
6249            _ => return None,
6250        }
6251    }
6252}
6253
6254fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
6255    let candidate = node
6256        .child_by_field_name("name")
6257        .or_else(|| node.child_by_field_name("declarator"))
6258        .or_else(|| {
6259            let mut cursor = node.walk();
6260            node.named_children(&mut cursor).find(|child| {
6261                matches!(
6262                    child.kind(),
6263                    "identifier" | "type_identifier" | "field_identifier"
6264                )
6265            })
6266        })?;
6267    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
6268    (!name.is_empty()).then_some(name)
6269}
6270
6271fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
6272    match node.kind() {
6273        "type_parameter_declaration"
6274        | "optional_type_parameter_declaration"
6275        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
6276        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
6277        _ => CppTemplateParameterKind::Value,
6278    }
6279}
6280
6281fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
6282    node.child_by_field_name("default_type")
6283        .or_else(|| node.child_by_field_name("default_value"))
6284}
6285
6286fn cpp_template_parameter_default_expression(
6287    parameter: Node<'_>,
6288    source: &str,
6289    parameter_names: &[String],
6290) -> Option<CppTemplateExpression> {
6291    let default = cpp_template_parameter_default(parameter)?;
6292    let base = cpp_template_expression(default, source, parameter_names);
6293    let Some(pointer_error) = parameter.next_named_sibling() else {
6294        return Some(base);
6295    };
6296    let Some(pointer_declarator) =
6297        recovered_abstract_pointer_declarator_term(pointer_error, source)
6298    else {
6299        return Some(base);
6300    };
6301    Some(CppTemplateExpression {
6302        text: format!(
6303            "{}{}",
6304            base.text,
6305            normalize_cpp_whitespace(node_text(pointer_error, source))
6306        ),
6307        term: CppTemplateTerm::Node {
6308            kind: "type_descriptor".to_string(),
6309            children: vec![base.term, pointer_declarator],
6310        },
6311    })
6312}
6313
6314fn recovered_abstract_pointer_declarator_term(
6315    node: Node<'_>,
6316    source: &str,
6317) -> Option<CppTemplateTerm> {
6318    if node.kind() != "ERROR" || node.child_count() == 0 {
6319        return None;
6320    }
6321    let mut children = Vec::new();
6322    for index in 0..node.child_count() {
6323        let child = node.child(index)?;
6324        if child.kind() != "*" {
6325            return None;
6326        }
6327        children.push(CppTemplateTerm::Atom {
6328            kind: "*".to_string(),
6329            text: normalize_cpp_whitespace(node_text(child, source)),
6330        });
6331    }
6332    Some(CppTemplateTerm::Node {
6333        kind: "abstract_pointer_declarator".to_string(),
6334        children,
6335    })
6336}
6337
6338fn cpp_template_expression(
6339    node: Node<'_>,
6340    source: &str,
6341    parameter_names: &[String],
6342) -> CppTemplateExpression {
6343    let text = normalize_cpp_whitespace(node_text(node, source));
6344    CppTemplateExpression {
6345        text,
6346        term: cpp_template_term(node, source, parameter_names),
6347    }
6348}
6349
6350pub fn cpp_template_term(
6351    node: Node<'_>,
6352    source: &str,
6353    parameter_names: &[String],
6354) -> CppTemplateTerm {
6355    enum Work<'tree> {
6356        Visit(Node<'tree>),
6357        Build { kind: String, child_count: usize },
6358    }
6359
6360    let mut work = vec![Work::Visit(node)];
6361    let mut terms = Vec::new();
6362    while let Some(next) = work.pop() {
6363        match next {
6364            Work::Visit(current) => {
6365                let text = normalize_cpp_whitespace(node_text(current, source));
6366                if parameter_names.contains(&text) {
6367                    terms.push(CppTemplateTerm::Parameter(text));
6368                    continue;
6369                }
6370                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
6371                    let mut cursor = current.walk();
6372                    let named = current
6373                        .named_children(&mut cursor)
6374                        .filter(|child| !child.is_extra() && child.kind() != "comment")
6375                        .collect::<Vec<_>>();
6376                    if let [child] = named.as_slice() {
6377                        work.push(Work::Visit(*child));
6378                        continue;
6379                    }
6380                }
6381                if current.child_count() == 0 {
6382                    terms.push(CppTemplateTerm::Atom {
6383                        kind: if matches!(
6384                            current.kind(),
6385                            "identifier"
6386                                | "type_identifier"
6387                                | "field_identifier"
6388                                | "namespace_identifier"
6389                        ) {
6390                            "identifier".to_string()
6391                        } else {
6392                            current.kind().to_string()
6393                        },
6394                        text,
6395                    });
6396                    continue;
6397                }
6398                let children = (0..current.child_count())
6399                    .filter_map(|index| current.child(index))
6400                    .filter(|child| !child.is_extra() && child.kind() != "comment")
6401                    .collect::<Vec<_>>();
6402                work.push(Work::Build {
6403                    kind: current.kind().to_string(),
6404                    child_count: children.len(),
6405                });
6406                work.extend(children.into_iter().rev().map(Work::Visit));
6407            }
6408            Work::Build { kind, child_count } => {
6409                let children = terms.split_off(terms.len() - child_count);
6410                terms.push(CppTemplateTerm::Node { kind, children });
6411            }
6412        }
6413    }
6414    terms.pop().expect("template term traversal emits one root")
6415}
6416
6417fn enclosing_cpp_declaration_node(mut node: Node<'_>) -> Option<Node<'_>> {
6418    loop {
6419        match node.kind() {
6420            "declaration"
6421            | "function_declaration"
6422            | "field_declaration"
6423            | "function_definition" => return Some(node),
6424            _ => node = node.parent()?,
6425        }
6426    }
6427}
6428
6429fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
6430    let mut params = Vec::new();
6431    let mut cursor = parameters_node.walk();
6432    for child in parameters_node.children(&mut cursor) {
6433        match child.kind() {
6434            "parameter_declaration" | "optional_parameter_declaration" => {
6435                params.push(cpp_parameter_type(child, source));
6436            }
6437            "variadic_parameter_declaration" => {
6438                params.push(cpp_parameter_type(child, source));
6439            }
6440            "variadic_parameter" | "..." => params.push("...".to_string()),
6441            _ => {}
6442        }
6443    }
6444
6445    if params.is_empty() {
6446        "()".to_string()
6447    } else {
6448        format!("({})", params.join(", "))
6449    }
6450}
6451
6452fn cpp_signature_metadata(
6453    signature: String,
6454    function_declarator: Node<'_>,
6455    source: &str,
6456) -> SignatureMetadata {
6457    let dispatch = cpp_callable_dispatch_extensibility(function_declarator);
6458    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
6459    let return_type_text = cpp_callable_return_type_text(function_declarator, source);
6460    let return_type_identity = cpp_callable_return_type_identity(function_declarator, source);
6461    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
6462        return enrich(
6463            SignatureMetadata::new(signature, Vec::new())
6464                .with_return_type_text(return_type_text)
6465                .with_return_type_identity(return_type_identity),
6466        );
6467    };
6468    let callable_arity = cpp_callable_arity(parameters_node, source);
6469    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
6470    let search_from = cpp_signature_search_start(&signature, function_declarator, source);
6471    let Some(relative_start) = signature
6472        .get(search_from..)
6473        .and_then(|suffix| suffix.find(&parameter_text))
6474    else {
6475        return enrich(
6476            SignatureMetadata::new(signature, Vec::new())
6477                .with_callable_arity(callable_arity)
6478                .with_return_type_text(return_type_text)
6479                .with_return_type_identity(return_type_identity),
6480        );
6481    };
6482    let parameters_start = search_from + relative_start;
6483    let parameters_end = parameters_start + parameter_text.len();
6484    let mut search_start = parameters_start;
6485    let parameters = cpp_parameter_label_nodes(parameters_node)
6486        .into_iter()
6487        .filter_map(|label_node| {
6488            let label = normalize_cpp_whitespace(node_text(label_node, source));
6489            if label.is_empty() || search_start > parameters_end {
6490                return None;
6491            }
6492            let haystack = signature.get(search_start..parameters_end)?;
6493            let relative_start = haystack.find(&label)?;
6494            let start_byte = search_start + relative_start;
6495            let end_byte = start_byte + label.len();
6496            search_start = end_byte;
6497            Some(ParameterMetadata::new(label, start_byte, end_byte))
6498        })
6499        .collect();
6500    enrich(
6501        SignatureMetadata::new(signature, parameters)
6502            .with_callable_arity(callable_arity)
6503            .with_return_type_text(return_type_text)
6504            .with_return_type_identity(return_type_identity),
6505    )
6506}
6507
6508fn cpp_callable_is_structural_constructor(function_declarator: Node<'_>, source: &str) -> bool {
6509    let Some(name_node) = function_declarator
6510        .child_by_field_name("declarator")
6511        .or_else(|| function_declarator.child_by_field_name("name"))
6512        .or_else(|| last_named_child(function_declarator))
6513    else {
6514        return false;
6515    };
6516    let Some(callable_name) = direct_identifier_name(name_node, source) else {
6517        return false;
6518    };
6519
6520    let mut current = function_declarator.parent();
6521    while let Some(ancestor) = current {
6522        let owner_name = match ancestor.kind() {
6523            "class_specifier" | "struct_specifier" | "union_specifier" => {
6524                class_like_name(ancestor, source)
6525            }
6526            "ERROR" => malformed_class_error_owner_name(ancestor, source),
6527            _ => None,
6528        };
6529        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
6530            return true;
6531        }
6532        current = ancestor.parent();
6533    }
6534    false
6535}
6536
6537/// Recover the owner name from the direct grammar shape retained when a later
6538/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
6539/// `ERROR` node:
6540///
6541/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
6542///
6543/// Direct-child checks keep this distinct from an unrelated nested class inside
6544/// a broader error region. The closing brace may be displaced past the error
6545/// node, so the opening body token is the available structural boundary.
6546fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
6547    if node.kind() != "ERROR" {
6548        return None;
6549    }
6550    let keyword = node.child(0)?;
6551    if !matches!(keyword.kind(), "class" | "struct" | "union") {
6552        return None;
6553    }
6554    let name_node = node.child(1)?;
6555    let name = direct_identifier_name(name_node, source)?;
6556    let has_body = (2..node.child_count())
6557        .filter_map(|index| node.child(index))
6558        .any(|child| child.kind() == "{");
6559    has_body.then_some(name)
6560}
6561
6562fn cpp_callable_return_type_identity(
6563    function_declarator: Node<'_>,
6564    source: &str,
6565) -> Option<StructuredTypeIdentity> {
6566    if cpp_callable_is_structural_constructor(function_declarator, source) {
6567        return None;
6568    }
6569    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
6570    if let Some((return_type, _)) = cpp_macro_displaced_callable_parts(function_declarator, source)
6571    {
6572        return cpp_structured_type_identity(return_type, source, &lexical_scope);
6573    }
6574    let mut cursor = function_declarator.walk();
6575    if let Some(trailing) = function_declarator
6576        .named_children(&mut cursor)
6577        .find(|child| child.kind() == "trailing_return_type")
6578        && let Some(type_descriptor) = trailing.named_child(0)
6579    {
6580        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
6581    }
6582
6583    let mut current = function_declarator;
6584    let mut wrappers = Vec::new();
6585    while let Some(parent) = current.parent() {
6586        if matches!(
6587            parent.kind(),
6588            "function_definition" | "declaration" | "field_declaration"
6589        ) {
6590            let type_node = parent.child_by_field_name("type")?;
6591            if cpp_export_macro_token(node_text(type_node, source))
6592                && (0..parent.named_child_count()).any(|index| {
6593                    parent
6594                        .named_child(index)
6595                        .is_some_and(|child| child.kind() == "ERROR")
6596                })
6597            {
6598                return None;
6599            }
6600            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
6601            for wrapper in wrappers.into_iter().rev() {
6602                identity = cpp_wrap_structured_type(identity, wrapper)?;
6603            }
6604            return Some(identity);
6605        }
6606        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
6607            || (matches!(
6608                parent.kind(),
6609                "pointer_declarator"
6610                    | "reference_declarator"
6611                    | "array_declarator"
6612                    | "parenthesized_declarator"
6613            ) && parent.named_child_count() == 1
6614                && parent.named_child(0) == Some(current));
6615        if !wraps_current_declarator {
6616            return None;
6617        }
6618        match parent.kind() {
6619            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
6620            "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
6621            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
6622            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
6623            _ => return None,
6624        }
6625        current = parent;
6626    }
6627    None
6628}
6629
6630fn cpp_structured_type_identity(
6631    node: Node<'_>,
6632    source: &str,
6633    lexical_scope: &[String],
6634) -> Option<StructuredTypeIdentity> {
6635    enum Work<'tree> {
6636        Visit(Node<'tree>),
6637        Wrap(CppStructuredTypeWrapper),
6638        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
6639        BuildGeneric { argument_count: usize },
6640    }
6641
6642    let mut work = vec![Work::Visit(node)];
6643    let mut values = Vec::new();
6644    let mut builder = StructuredTypeIdentityBuilder::default();
6645    while let Some(next) = work.pop() {
6646        match next {
6647            Work::Visit(current) => match current.kind() {
6648                "type_descriptor" => {
6649                    let type_node = current
6650                        .child_by_field_name("type")
6651                        .or_else(|| current.named_child(0))?;
6652                    let mut wrappers = Vec::new();
6653                    let mut cursor = current.walk();
6654                    for child in current.named_children(&mut cursor) {
6655                        if child.id() != type_node.id() {
6656                            wrappers.extend(cpp_structured_declarator_wrappers(child));
6657                        }
6658                    }
6659                    work.push(Work::ApplyWrappers(wrappers));
6660                    work.push(Work::Visit(type_node));
6661                }
6662                "pointer_declarator" | "abstract_pointer_declarator" => {
6663                    let child = current
6664                        .child_by_field_name("declarator")
6665                        .or_else(|| current.named_child(0))?;
6666                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
6667                    work.push(Work::Visit(child));
6668                }
6669                "reference_declarator" => {
6670                    let child = current
6671                        .child_by_field_name("declarator")
6672                        .or_else(|| current.named_child(0))?;
6673                    work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
6674                    work.push(Work::Visit(child));
6675                }
6676                "array_declarator" | "abstract_array_declarator" => {
6677                    let child = current
6678                        .child_by_field_name("declarator")
6679                        .or_else(|| current.named_child(0))?;
6680                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
6681                    work.push(Work::Visit(child));
6682                }
6683                "template_type" => {
6684                    let name_node = current.child_by_field_name("name")?;
6685                    let arguments = current
6686                        .child_by_field_name("arguments")
6687                        .map(|arguments_node| {
6688                            let mut cursor = arguments_node.walk();
6689                            arguments_node
6690                                .named_children(&mut cursor)
6691                                .filter(|child| !child.is_extra() && child.kind() != "comment")
6692                                .collect::<Vec<_>>()
6693                        })
6694                        .unwrap_or_default();
6695                    work.push(Work::BuildGeneric {
6696                        argument_count: arguments.len(),
6697                    });
6698                    work.extend(arguments.into_iter().rev().map(Work::Visit));
6699                    work.push(Work::Visit(name_node));
6700                }
6701                "qualified_identifier"
6702                | "scoped_identifier"
6703                | "scoped_type_identifier"
6704                | "type_identifier"
6705                | "field_identifier"
6706                | "identifier"
6707                | "namespace_identifier"
6708                | "primitive_type" => {
6709                    values.push(builder.named(cpp_structured_named_type(
6710                        current,
6711                        source,
6712                        lexical_scope,
6713                    )?)?);
6714                }
6715                _ => {
6716                    let child = current.child_by_field_name("type").or_else(|| {
6717                        (current.named_child_count() == 1)
6718                            .then(|| current.named_child(0))
6719                            .flatten()
6720                    })?;
6721                    work.push(Work::Visit(child));
6722                }
6723            },
6724            Work::Wrap(wrapper) => {
6725                let root = values.pop()?;
6726                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
6727            }
6728            Work::ApplyWrappers(wrappers) => {
6729                let mut root = values.pop()?;
6730                for wrapper in wrappers.into_iter().rev() {
6731                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
6732                }
6733                values.push(root);
6734            }
6735            Work::BuildGeneric { argument_count } => {
6736                let value_count = argument_count.checked_add(1)?;
6737                let start = values.len().checked_sub(value_count)?;
6738                let mut built = values.split_off(start);
6739                let base = built.remove(0);
6740                values.push(builder.generic(base, built)?);
6741            }
6742        }
6743    }
6744    (values.len() == 1)
6745        .then(|| values.pop())
6746        .flatten()
6747        .and_then(|root| builder.finish(root))
6748}
6749
6750fn cpp_structured_named_type(
6751    node: Node<'_>,
6752    source: &str,
6753    lexical_scope: &[String],
6754) -> Option<StructuredTypeName> {
6755    let path = cpp_structured_type_path(node, source)?;
6756    let absolute = node.child_by_field_name("scope").is_none()
6757        && node.child(0).is_some_and(|child| child.kind() == "::");
6758    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
6759}
6760
6761#[derive(Clone, Copy)]
6762enum CppStructuredTypeWrapper {
6763    Pointer,
6764    Reference,
6765    Array,
6766}
6767
6768fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
6769    let mut wrappers = Vec::new();
6770    let mut current = node;
6771    loop {
6772        match current.kind() {
6773            "pointer_declarator" | "abstract_pointer_declarator" => {
6774                wrappers.push(CppStructuredTypeWrapper::Pointer)
6775            }
6776            "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
6777            "array_declarator" | "abstract_array_declarator" => {
6778                wrappers.push(CppStructuredTypeWrapper::Array)
6779            }
6780            _ => break,
6781        }
6782        let Some(child) = current
6783            .child_by_field_name("declarator")
6784            .or_else(|| current.named_child(0))
6785        else {
6786            break;
6787        };
6788        current = child;
6789    }
6790    wrappers
6791}
6792
6793fn cpp_wrap_structured_type(
6794    identity: StructuredTypeIdentity,
6795    wrapper: CppStructuredTypeWrapper,
6796) -> Option<StructuredTypeIdentity> {
6797    match wrapper {
6798        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
6799        CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
6800        CppStructuredTypeWrapper::Array => identity.wrap_array(),
6801    }
6802}
6803
6804fn cpp_wrap_structured_type_node(
6805    builder: &mut StructuredTypeIdentityBuilder,
6806    inner: StructuredTypeNodeId,
6807    wrapper: CppStructuredTypeWrapper,
6808) -> Option<StructuredTypeNodeId> {
6809    match wrapper {
6810        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
6811        CppStructuredTypeWrapper::Reference => builder.reference(inner),
6812        CppStructuredTypeWrapper::Array => builder.array(inner),
6813    }
6814}
6815
6816fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
6817    let mut path = Vec::new();
6818    let mut stack = vec![node];
6819    while let Some(current) = stack.pop() {
6820        match current.kind() {
6821            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
6822                let component = node_text(current, source).to_string();
6823                if component.is_empty() {
6824                    return None;
6825                }
6826                path.push(component);
6827            }
6828            "template_type" | "dependent_type" => {
6829                stack.push(current.child_by_field_name("name")?);
6830            }
6831            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
6832                stack.push(current.child_by_field_name("name")?);
6833                if let Some(scope) = current.child_by_field_name("scope") {
6834                    stack.push(scope);
6835                }
6836            }
6837            _ => return None,
6838        }
6839    }
6840    (!path.is_empty()).then_some(path)
6841}
6842
6843fn cpp_callable_lexical_scope(node: Node<'_>, source: &str) -> Vec<String> {
6844    let mut groups = Vec::new();
6845    let mut current = node.parent();
6846    while let Some(parent) = current {
6847        if matches!(
6848            parent.kind(),
6849            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
6850        ) && let Some(name_node) = parent.child_by_field_name("name")
6851            && let Some(components) = cpp_structured_type_path(name_node, source)
6852            && !components.is_empty()
6853        {
6854            groups.push(components);
6855        }
6856        current = parent.parent();
6857    }
6858    groups.reverse();
6859    groups.into_iter().flatten().collect()
6860}
6861
6862fn cpp_callable_dispatch_extensibility(function_declarator: Node<'_>) -> DispatchExtensibility {
6863    let mut declaration = None;
6864    let mut current = Some(function_declarator);
6865    while let Some(node) = current {
6866        match node.kind() {
6867            "template_declaration"
6868            | "preproc_if"
6869            | "preproc_ifdef"
6870            | "preproc_else"
6871            | "preproc_elif"
6872            | "preproc_call"
6873            | "ERROR" => return DispatchExtensibility::Open,
6874            "declaration" | "field_declaration" | "function_definition" => {
6875                declaration.get_or_insert(node);
6876            }
6877            "translation_unit" => break,
6878            _ => {}
6879        }
6880        current = node.parent();
6881    }
6882    let Some(declaration) = declaration else {
6883        return DispatchExtensibility::Open;
6884    };
6885
6886    let mut saw_virtual_boundary = false;
6887    let mut stack = vec![declaration];
6888    while let Some(node) = stack.pop() {
6889        match node.kind() {
6890            "compound_statement" | "field_declaration_list" => continue,
6891            "final" | "final_specifier" => return DispatchExtensibility::Closed,
6892            "virtual"
6893            | "override"
6894            | "virtual_specifier"
6895            | "pure_virtual_clause"
6896            | "template_parameter_list"
6897            | "template_method"
6898            | "template_function"
6899            | "ERROR" => saw_virtual_boundary = true,
6900            _ => {}
6901        }
6902        let mut cursor = node.walk();
6903        stack.extend(node.children(&mut cursor));
6904    }
6905
6906    if saw_virtual_boundary {
6907        DispatchExtensibility::Open
6908    } else {
6909        DispatchExtensibility::Closed
6910    }
6911}
6912
6913fn cpp_callable_linkage(declaration: Node<'_>, source: &str) -> CallableLinkage {
6914    let mut enclosed_by_class = false;
6915    let mut current = declaration.parent();
6916    while let Some(node) = current {
6917        if node.kind() == "namespace_definition"
6918            && node
6919                .child_by_field_name("name")
6920                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6921        {
6922            return CallableLinkage::Internal;
6923        }
6924        if matches!(
6925            node.kind(),
6926            "class_specifier" | "struct_specifier" | "union_specifier"
6927        ) {
6928            if node
6929                .child_by_field_name("name")
6930                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6931            {
6932                return CallableLinkage::Internal;
6933            }
6934            enclosed_by_class = true;
6935        }
6936        if matches!(node.kind(), "function_definition" | "lambda_expression") {
6937            return CallableLinkage::Internal;
6938        }
6939        current = node.parent();
6940    }
6941
6942    if enclosed_by_class {
6943        return CallableLinkage::External;
6944    }
6945
6946    let mut cursor = declaration.walk();
6947    if declaration.named_children(&mut cursor).any(|child| {
6948        child.kind() == "storage_class_specifier"
6949            && normalize_cpp_whitespace(node_text(child, source)) == "static"
6950    }) {
6951        CallableLinkage::Internal
6952    } else {
6953        CallableLinkage::External
6954    }
6955}
6956
6957fn cpp_callable_return_type_text(function_declarator: Node<'_>, source: &str) -> Option<String> {
6958    if cpp_callable_is_structural_constructor(function_declarator, source) {
6959        return None;
6960    }
6961    if let Some((return_type, _)) = cpp_macro_displaced_callable_parts(function_declarator, source)
6962    {
6963        let text = normalize_cpp_whitespace(node_text(return_type, source));
6964        return (!text.is_empty()).then_some(text);
6965    }
6966    let mut cursor = function_declarator.walk();
6967    if let Some(trailing) = function_declarator
6968        .named_children(&mut cursor)
6969        .find(|child| child.kind() == "trailing_return_type")
6970        && let Some(type_descriptor) = trailing.named_child(0)
6971    {
6972        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
6973        if !text.is_empty() {
6974            return Some(text);
6975        }
6976    }
6977
6978    let mut current = function_declarator;
6979    let mut indirection = String::new();
6980    while let Some(parent) = current.parent() {
6981        if matches!(
6982            parent.kind(),
6983            "function_definition" | "declaration" | "field_declaration"
6984        ) {
6985            let type_node = parent.child_by_field_name("type")?;
6986            if cpp_export_macro_token(node_text(type_node, source))
6987                && (0..parent.named_child_count()).any(|index| {
6988                    parent
6989                        .named_child(index)
6990                        .is_some_and(|child| child.kind() == "ERROR")
6991                })
6992            {
6993                // Export/decorator macros commonly occupy the grammar's `type`
6994                // field and leave the semantic return type in an ERROR sibling.
6995                // Do not persist the macro token as a return type. The malformed
6996                // declaration does not carry enough structured evidence here.
6997                return None;
6998            }
6999            let base = normalize_cpp_whitespace(node_text(type_node, source));
7000            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
7001        }
7002        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
7003            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
7004                && parent.named_child_count() == 1
7005                && parent.named_child(0) == Some(current));
7006        if wraps_current_declarator {
7007            match parent.kind() {
7008                "pointer_declarator" => indirection.push('*'),
7009                "reference_declarator" => {
7010                    let reference = parent
7011                        .children(&mut parent.walk())
7012                        .find(|child| !child.is_named())
7013                        .map(|child| node_text(child, source))
7014                        .unwrap_or("&");
7015                    indirection.push_str(reference);
7016                }
7017                "init_declarator" | "parenthesized_declarator" => {}
7018                _ => return None,
7019            }
7020            current = parent;
7021            continue;
7022        }
7023        return None;
7024    }
7025    None
7026}
7027
7028fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
7029    let mut required = 0;
7030    let mut total = 0;
7031    let mut repeated = false;
7032    let mut cursor = parameters_node.walk();
7033    for child in parameters_node.children(&mut cursor) {
7034        match child.kind() {
7035            "parameter_declaration" => {
7036                if child.child_by_field_name("declarator").is_none()
7037                    && child
7038                        .child_by_field_name("type")
7039                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
7040                {
7041                    continue;
7042                }
7043                required += 1;
7044                total += 1;
7045            }
7046            "optional_parameter_declaration" => total += 1,
7047            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7048                repeated = true;
7049            }
7050            _ => {}
7051        }
7052    }
7053    CallableArity::new(required, total, repeated)
7054}
7055
7056fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
7057    let mut labels = Vec::new();
7058    let mut cursor = parameters_node.walk();
7059    for child in parameters_node.children(&mut cursor) {
7060        match child.kind() {
7061            "parameter_declaration" | "optional_parameter_declaration" => {
7062                if let Some(name_node) = child
7063                    .child_by_field_name("declarator")
7064                    .and_then(cpp_declarator_label_node)
7065                {
7066                    labels.push(name_node);
7067                } else {
7068                    labels.push(child);
7069                }
7070            }
7071            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7072                labels.push(child);
7073            }
7074            _ => {}
7075        }
7076    }
7077    labels
7078}
7079
7080fn cpp_signature_search_start(
7081    signature: &str,
7082    function_declarator: Node<'_>,
7083    source: &str,
7084) -> usize {
7085    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator) else {
7086        return 0;
7087    };
7088    let raw = node_text(enclosing, source);
7089    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
7090    let offset = function_declarator
7091        .start_byte()
7092        .saturating_sub(enclosing.start_byte())
7093        .saturating_sub(leading_trim_bytes);
7094    offset.min(signature.len())
7095}
7096
7097fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
7098    match node.kind() {
7099        "identifier" | "field_identifier" => Some(node),
7100        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
7101            .child_by_field_name("declarator")
7102            .or_else(|| last_named_child(node))
7103            .and_then(cpp_declarator_label_node),
7104        "array_declarator" => node
7105            .child_by_field_name("declarator")
7106            .and_then(cpp_declarator_label_node),
7107        "function_declarator" => node
7108            .child_by_field_name("declarator")
7109            .or_else(|| node.child_by_field_name("name"))
7110            .or_else(|| last_named_child(node))
7111            .and_then(cpp_declarator_label_node),
7112        _ => None,
7113    }
7114}
7115
7116fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
7117    let base_type = parameter
7118        .child_by_field_name("type")
7119        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
7120        .unwrap_or_default();
7121    let declarator = cpp_parameter_declarator(parameter);
7122    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
7123    // are discarded, so `f(const int)` and `f(int)` declare one function. A
7124    // qualifier written next to the parameter's type is only top-level when
7125    // the declarator adds no indirection; behind a pointer, reference or array
7126    // declarator the same qualifier belongs to the pointee, referent or
7127    // element and keeps distinguishing the type (#1827).
7128    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
7129    let mut cursor = parameter.walk();
7130    let qualifiers = parameter
7131        .named_children(&mut cursor)
7132        .filter(|child| child.kind() == "type_qualifier")
7133        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
7134        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
7135        .collect::<Vec<_>>()
7136        .join(" ");
7137    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
7138        (true, _) => base_type,
7139        (_, true) => qualifiers,
7140        (false, false) => format!("{qualifiers} {base_type}"),
7141    };
7142    let declarator_suffix = declarator
7143        .map(|node| cpp_declarator_suffix_without_name(node, source))
7144        .unwrap_or_default();
7145
7146    let combined = if type_text.is_empty() {
7147        declarator_suffix
7148    } else if declarator_suffix.is_empty() {
7149        type_text
7150    } else {
7151        format!("{type_text} {declarator_suffix}")
7152    };
7153    normalize_cpp_type_text(&combined)
7154}
7155
7156fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
7157    parameter.child_by_field_name("declarator").or_else(|| {
7158        // Some unnamed prototype parameters expose their abstract declarator
7159        // as a direct named child without the grammar's `declarator` field.
7160        // Recover only the structured abstract-declarator node; the parameter's
7161        // type and qualifiers are distinct children and must not be guessed from
7162        // source text.
7163        let mut cursor = parameter.walk();
7164        parameter
7165            .named_children(&mut cursor)
7166            .find(|child| is_cpp_abstract_declarator(child.kind()))
7167    })
7168}
7169
7170/// Whether a parameter's declarator chain adds indirection - a pointer,
7171/// reference, array or function declarator - to the parameter's written type.
7172fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
7173    let mut current = Some(declarator);
7174    while let Some(node) = current {
7175        if matches!(
7176            node.kind(),
7177            "pointer_declarator"
7178                | "abstract_pointer_declarator"
7179                | "reference_declarator"
7180                | "abstract_reference_declarator"
7181                | "array_declarator"
7182                | "abstract_array_declarator"
7183                | "function_declarator"
7184                | "abstract_function_declarator"
7185        ) {
7186            return true;
7187        }
7188        current = cpp_nested_declarator(node);
7189    }
7190    false
7191}
7192
7193fn is_cpp_abstract_declarator(kind: &str) -> bool {
7194    matches!(
7195        kind,
7196        "abstract_pointer_declarator"
7197            | "abstract_reference_declarator"
7198            | "abstract_array_declarator"
7199            | "abstract_function_declarator"
7200            | "abstract_parenthesized_declarator"
7201    )
7202}
7203
7204fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
7205    node.child_by_field_name("declarator").or_else(|| {
7206        if is_cpp_abstract_declarator(node.kind()) {
7207            let mut cursor = node.walk();
7208            node.named_children(&mut cursor)
7209                .find(|child| is_cpp_abstract_declarator(child.kind()))
7210        } else {
7211            // Named declarators historically use their last named child when
7212            // tree-sitter omits the field. Keep that broad fallback for
7213            // attributed, variadic, and recovered named shapes.
7214            last_named_child(node)
7215        }
7216    })
7217}
7218
7219fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
7220    match node.kind() {
7221        "identifier" | "field_identifier" => String::new(),
7222        "pointer_declarator" | "abstract_pointer_declarator" => {
7223            let inner = cpp_nested_declarator(node)
7224                .map(|child| cpp_declarator_suffix_without_name(child, source))
7225                .unwrap_or_default();
7226            format!("*{inner}")
7227        }
7228        "reference_declarator" | "abstract_reference_declarator" => {
7229            let inner = cpp_nested_declarator(node)
7230                .map(|child| cpp_declarator_suffix_without_name(child, source))
7231                .unwrap_or_default();
7232            let reference = node
7233                .children(&mut node.walk())
7234                .find(|child| matches!(child.kind(), "&" | "&&"))
7235                .map(|child| node_text(child, source))
7236                .unwrap_or("&");
7237            format!("{reference}{inner}")
7238        }
7239        "array_declarator" | "abstract_array_declarator" => {
7240            let inner = cpp_nested_declarator(node)
7241                .map(|child| cpp_declarator_suffix_without_name(child, source))
7242                .unwrap_or_default();
7243            let size = node
7244                .child_by_field_name("size")
7245                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
7246                .unwrap_or_default();
7247            format!("{inner}[{size}]")
7248        }
7249        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
7250            let inner = cpp_nested_declarator(node);
7251            inner
7252                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
7253                .unwrap_or_default()
7254        }
7255        "function_declarator" | "abstract_function_declarator" => {
7256            let inner = cpp_nested_declarator(node)
7257                .map(|child| cpp_declarator_suffix_without_name(child, source))
7258                .unwrap_or_default();
7259            let params = node
7260                .child_by_field_name("parameters")
7261                .map(|child| cpp_parameter_signature(child, source))
7262                .unwrap_or_else(|| "()".to_string());
7263            format!("{inner}{params}")
7264        }
7265        _ => {
7266            let text = normalize_cpp_whitespace(node_text(node, source));
7267            let name = extract_declarator_name(node, source);
7268            if name.is_empty() {
7269                text
7270            } else {
7271                text.replace(&name, "").trim().to_string()
7272            }
7273        }
7274    }
7275}
7276
7277fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
7278    collapse_cpp_whitespace(
7279        suffix
7280            .trim()
7281            .trim_start_matches("->")
7282            .trim_start_matches('{')
7283            .trim_end_matches(';'),
7284    )
7285}
7286
7287pub fn normalize_cpp_whitespace(value: &str) -> String {
7288    collapse_cpp_whitespace(value)
7289}
7290
7291fn normalize_cpp_type_text(value: &str) -> String {
7292    collapse_cpp_whitespace(value)
7293        .replace(", ", ",")
7294        .replace(" <", "<")
7295        .replace("< ", "<")
7296        .replace(" >", ">")
7297}
7298
7299fn collapse_cpp_whitespace(value: &str) -> String {
7300    let mut result = String::new();
7301    let mut prev_space = false;
7302    for ch in value.chars() {
7303        if ch.is_whitespace() {
7304            if !prev_space {
7305                result.push(' ');
7306            }
7307            prev_space = true;
7308        } else {
7309            result.push(ch);
7310            prev_space = false;
7311        }
7312    }
7313    result.trim().to_string()
7314}
7315
7316pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
7317    node_source_text(node, source)
7318}
7319
7320pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
7321    walk_named_tree_preorder(node, true, |node| {
7322        match node.kind() {
7323            "type_identifier" | "identifier" | "qualified_identifier" => {
7324                let text = node_text(node, source).trim();
7325                if !text.is_empty() {
7326                    identifiers.insert(text.to_string());
7327                }
7328            }
7329            _ => {}
7330        }
7331        WalkControl::Continue
7332    });
7333}
7334
7335fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
7336    node.child_by_field_name("body").or_else(|| {
7337        let mut cursor = node.walk();
7338        node.named_children(&mut cursor).find(|child| {
7339            matches!(
7340                child.kind(),
7341                "declaration_list" | "field_declaration_list" | "enumerator_list"
7342            )
7343        })
7344    })
7345}
7346
7347/// Return a class body's actual closing brace when the parser supplied one.
7348///
7349/// A malformed namespace sentinel can leave a class node carrying unrelated
7350/// parser errors even though its own class body is complete.  `has_error()` is
7351/// therefore too coarse an admission predicate for sentinel ownership.  The
7352/// body list, however, exposes the opening and closing punctuation directly;
7353/// a real (non-missing) final `}` proves that the class did not borrow the
7354/// enclosing namespace's close.  Requiring the body to end before its parent
7355/// container also rejects a recovered node whose body swallowed that outer
7356/// boundary.
7357fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
7358    if !matches!(
7359        node.kind(),
7360        "class_specifier" | "struct_specifier" | "union_specifier"
7361    ) {
7362        return None;
7363    }
7364    let body = cpp_body_node(node)?;
7365    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
7366        return None;
7367    }
7368    let open = body.child(0)?;
7369    let close = body.child(body.child_count().checked_sub(1)?)?;
7370    if open.kind() != "{"
7371        || open.is_missing()
7372        || close.kind() != "}"
7373        || close.is_missing()
7374        || close.end_byte() != body.end_byte()
7375        || body.end_byte() > node.end_byte()
7376        || node
7377            .parent()
7378            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
7379    {
7380        return None;
7381    }
7382    Some(close)
7383}
7384
7385fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
7386    if node.kind() == "namespace_definition" {
7387        return true;
7388    }
7389    let mut cursor = node.walk();
7390    node.named_children(&mut cursor)
7391        .any(cpp_contains_namespace_definition)
7392}
7393
7394struct CppNestedNamespaceSentinel<'tree> {
7395    function: Node<'tree>,
7396    body: Node<'tree>,
7397    namespace_components: Vec<String>,
7398}
7399
7400/// Owned structural recovery metadata for a namespace-sentinel region.
7401///
7402/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
7403/// instead of the namespace/class scopes that the declaration visitor restores.
7404/// The inverted usage walk has the original CST, so it needs the same ownership
7405/// evidence without borrowing parser nodes across its file scan.  Keep this
7406/// descriptor deliberately source-range based: callers can match a reference
7407/// node by containment and then resolve its structured type spelling in the
7408/// recovered class scope.
7409#[derive(Debug, Clone)]
7410pub struct CppSentinelRecoveredOwner {
7411    pub range: Range,
7412    /// Start of the qualified owner name (`btree<P>::method`).  A leading
7413    /// return type before this byte is looked up from the namespace; parameters,
7414    /// trailing returns, and the body use the member owner scope.
7415    pub owner_name_start_byte: usize,
7416    /// Number of leading components belonging to the namespace rather than
7417    /// the qualified class owner.  A leading return type is looked up before
7418    /// every owner component, not merely before the innermost class.
7419    pub namespace_component_count: usize,
7420    pub scope_components: Vec<String>,
7421}
7422
7423#[derive(Debug, Clone)]
7424pub struct CppSentinelRecoveredClass {
7425    pub namespace_range: Range,
7426    pub namespace_scope_components: Vec<String>,
7427    pub class_range: Range,
7428    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
7429    pub scope_components: Vec<String>,
7430    /// Qualified out-of-line member definitions owned by this class.  Their
7431    /// ranges may extend beyond `class_range` when the malformed sentinel
7432    /// swallowed the namespace close and left definitions as function siblings.
7433    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
7434}
7435
7436/// Resolve the lexical scope restored for a node in a malformed
7437/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
7438/// outrank class spans, which in turn outrank the surviving namespace body.
7439/// The class ancestor suffix is recovered from the original CST so nested
7440/// members keep their complete `Outer::Inner` owner chain.
7441pub fn cpp_sentinel_recovered_scope_for_node(
7442    node: Node<'_>,
7443    source: &str,
7444    recovered_classes: &[CppSentinelRecoveredClass],
7445) -> Option<Vec<String>> {
7446    let contains =
7447        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
7448    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
7449    for recovered in recovered_classes {
7450        for owner in recovered
7451            .owner_ranges
7452            .iter()
7453            .filter(|owner| contains(owner.range))
7454        {
7455            let replace = best_owner.is_none_or(|existing| {
7456                owner.range.end_byte.saturating_sub(owner.range.start_byte)
7457                    < existing
7458                        .range
7459                        .end_byte
7460                        .saturating_sub(existing.range.start_byte)
7461            });
7462            if replace {
7463                best_owner = Some(owner);
7464            }
7465        }
7466    }
7467    if let Some(owner) = best_owner {
7468        let mut scope = owner.scope_components.clone();
7469        if node.start_byte() < owner.owner_name_start_byte {
7470            scope.truncate(owner.namespace_component_count);
7471        }
7472        return Some(scope);
7473    }
7474
7475    let class = recovered_classes
7476        .iter()
7477        .filter(|recovered| contains(recovered.class_range))
7478        .min_by_key(|recovered| {
7479            recovered
7480                .class_range
7481                .end_byte
7482                .saturating_sub(recovered.class_range.start_byte)
7483        });
7484    let class_scope = class.is_some();
7485    let mut scope = if let Some(class) = class {
7486        class.scope_components.clone()
7487    } else {
7488        let namespace = recovered_classes
7489            .iter()
7490            .filter(|recovered| contains(recovered.namespace_range))
7491            .min_by_key(|recovered| {
7492                recovered
7493                    .namespace_range
7494                    .end_byte
7495                    .saturating_sub(recovered.namespace_range.start_byte)
7496            })?;
7497        let mut scope = namespace.namespace_scope_components.clone();
7498        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
7499        let common_prefix = scope
7500            .iter()
7501            .zip(&parser_namespace)
7502            .take_while(|(recovered, parser)| recovered == parser)
7503            .count();
7504        scope.extend(parser_namespace.into_iter().skip(common_prefix));
7505        scope
7506    };
7507    if class_scope {
7508        let mut ancestor_components = Vec::new();
7509        let mut ancestor = node.parent();
7510        while let Some(current) = ancestor {
7511            if matches!(
7512                current.kind(),
7513                "class_specifier" | "struct_specifier" | "union_specifier"
7514            ) && let Some(name) = current.child_by_field_name("name")
7515                && let Some(name_components) = cpp_name_components(name, source)
7516            {
7517                ancestor_components.push(
7518                    name_components
7519                        .into_iter()
7520                        .map(|component| component.name)
7521                        .collect::<Vec<_>>(),
7522                );
7523            }
7524            ancestor = current.parent();
7525        }
7526        ancestor_components.reverse();
7527        let base_len = scope.len();
7528        for component in ancestor_components.into_iter().flatten() {
7529            if scope.len() >= base_len && scope.last() == Some(&component) {
7530                continue;
7531            }
7532            scope.push(component);
7533        }
7534    }
7535    Some(scope)
7536}
7537
7538struct CppSentinelFragmentedClassTail<'tree> {
7539    class_node: Node<'tree>,
7540    template_node: Option<Node<'tree>>,
7541    name: String,
7542    raw_supertypes: Option<Vec<String>>,
7543    fragmented: FragmentedExportBody,
7544    consumed_start: usize,
7545}
7546
7547struct CppSentinelFragmentedClassErrorPrefix<'tree> {
7548    name: String,
7549    open: Node<'tree>,
7550    raw_supertypes: Option<Vec<String>>,
7551}
7552
7553struct CppSentinelDirectBodyClassRegion {
7554    namespace_components: Vec<String>,
7555    class_start: usize,
7556    class_start_line: usize,
7557    class_close_end: usize,
7558    class_close_line: usize,
7559    name: String,
7560}
7561
7562fn cpp_sentinel_body_class_candidate<'tree>(
7563    child: Node<'tree>,
7564) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
7565    if matches!(
7566        child.kind(),
7567        "class_specifier" | "struct_specifier" | "union_specifier"
7568    ) {
7569        return Some((child, None));
7570    }
7571    if child.kind() != "template_declaration" {
7572        if child.kind() == "declaration" {
7573            return Some((first_class_like_child(child)?, None));
7574        }
7575        return None;
7576    }
7577    let mut cursor = child.walk();
7578    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
7579        if matches!(
7580            candidate.kind(),
7581            "class_specifier" | "struct_specifier" | "union_specifier"
7582        ) {
7583            Some(candidate)
7584        } else if candidate.kind() == "declaration" {
7585            first_class_like_child(candidate)
7586        } else {
7587            None
7588        }
7589    })?;
7590    Some((class_node, Some(child)))
7591}
7592
7593/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
7594/// namespace-sentinel body when a later member macro ends the bogus sentinel
7595/// function before the real class close. The anonymous class/open tokens and
7596/// direct identifier are the structural proof; a retained direct close would
7597/// be an ordinary malformed class rather than the fragmented tail handled here.
7598fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
7599    node: Node<'tree>,
7600    source: &str,
7601) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
7602    let name = malformed_class_error_owner_name(node, source)?;
7603    let mut cursor = node.walk();
7604    let children = node.children(&mut cursor).collect::<Vec<_>>();
7605    let keyword = children.first()?;
7606    let open_index = children.iter().position(|child| child.kind() == "{")?;
7607    if children[open_index + 1..]
7608        .iter()
7609        .any(|child| child.kind() == "}")
7610    {
7611        return None;
7612    }
7613    let raw_supertypes =
7614        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
7615    Some(CppSentinelFragmentedClassErrorPrefix {
7616        name,
7617        open: children[open_index],
7618        raw_supertypes,
7619    })
7620}
7621
7622fn cpp_sentinel_direct_body_class_candidate<'tree>(
7623    child: Node<'tree>,
7624) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
7625    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
7626        return Some(candidate);
7627    }
7628    if child.kind() != "template_declaration" {
7629        return None;
7630    }
7631    let mut cursor = child.walk();
7632    let wrapper = child
7633        .named_children(&mut cursor)
7634        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
7635    Some((first_class_like_child(wrapper)?, Some(child)))
7636}
7637
7638fn cpp_sentinel_direct_namespace_components(
7639    function: Node<'_>,
7640    body: Node<'_>,
7641    source: &str,
7642) -> Option<Vec<String>> {
7643    let mut cursor = function.walk();
7644    let children = function
7645        .named_children(&mut cursor)
7646        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
7647        .collect::<Vec<_>>();
7648    let sentinel_index = children.iter().rposition(|child| {
7649        direct_identifier_name(*child, source)
7650            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
7651    })?;
7652    let mut identifiers = Vec::new();
7653    let mut stack = children[sentinel_index + 1..]
7654        .iter()
7655        .rev()
7656        .copied()
7657        .collect::<Vec<_>>();
7658    while let Some(current) = stack.pop() {
7659        if let Some(name) = direct_identifier_name(current, source) {
7660            identifiers.push(name);
7661            continue;
7662        }
7663        let mut cursor = current.walk();
7664        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
7665        stack.extend(children.into_iter().rev());
7666    }
7667    let [keyword, namespace] = identifiers.as_slice() else {
7668        return None;
7669    };
7670    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
7671        .then(|| vec![namespace.clone()])
7672}
7673
7674fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
7675    let mut sibling = class_semicolon.next_named_sibling();
7676    let namespace_close = loop {
7677        let Some(current) = sibling else {
7678            return false;
7679        };
7680        sibling = current.next_named_sibling();
7681        if current.kind() != "comment" {
7682            break current;
7683        }
7684    };
7685    if !cpp_is_stray_close_brace(namespace_close, source) {
7686        return false;
7687    }
7688    loop {
7689        let Some(current) = sibling else {
7690            return false;
7691        };
7692        sibling = current.next_named_sibling();
7693        if current.kind() == "comment" {
7694            continue;
7695        }
7696        return direct_identifier_name(current, source)
7697            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
7698    }
7699}
7700
7701fn cpp_sentinel_macro_body_class_region(
7702    node: Node<'_>,
7703    source: &str,
7704) -> Option<CppSentinelDirectBodyClassRegion> {
7705    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
7706        return None;
7707    };
7708    if node.kind() != "function_definition" || !node.has_error() {
7709        return None;
7710    }
7711    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
7712    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
7713    let mut cursor = body.walk();
7714    let candidates = body
7715        .named_children(&mut cursor)
7716        .filter_map(cpp_sentinel_direct_body_class_candidate)
7717        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
7718        .collect::<Vec<_>>();
7719    let [(class_node, template_node)] = candidates.as_slice() else {
7720        return None;
7721    };
7722    let original_body = cpp_body_node(*class_node)?;
7723    let name = class_like_name(*class_node, source)?;
7724    if name.is_empty() || cpp_export_macro_token(&name) {
7725        return None;
7726    }
7727
7728    let mut sibling = node.next_named_sibling();
7729    let (class_close_start, class_close_end, class_close_line) = loop {
7730        let current = sibling?;
7731        let next = current.next_named_sibling();
7732        if cpp_is_stray_close_brace(current, source)
7733            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
7734        {
7735            let semicolon = next.expect("checked above");
7736            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
7737                return None;
7738            }
7739            break (
7740                current.start_byte(),
7741                semicolon.end_byte(),
7742                semicolon.end_position().row + 1,
7743            );
7744        }
7745        sibling = next;
7746    };
7747    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
7748    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
7749    let root = tree.root_node();
7750    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
7751    let reparsed = cpp_sentinel_reparsed_class(root, reparsed_template, source)?;
7752    if reparsed.name != name
7753        || reparsed.declaration_node.start_byte() != class_node.start_byte()
7754        || reparsed.body.start_byte() != original_body.start_byte()
7755        || class_close_start <= reparsed.body.end_byte()
7756        || class_close_end <= class_node.end_byte()
7757    {
7758        return None;
7759    }
7760    Some(CppSentinelDirectBodyClassRegion {
7761        namespace_components,
7762        class_start: reparse_start,
7763        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
7764            node.start_position().row + 1
7765        }),
7766        class_close_end,
7767        class_close_line,
7768        name,
7769    })
7770}
7771
7772/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
7773/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
7774///
7775/// The parser puts the namespace opener and the malformed function in one root
7776/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
7777/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
7778/// the malformed function must begin with an all-caps type, then an ERROR whose
7779/// sole identifier is `namespace`, followed by the inner namespace identifier
7780/// and a compound body; and that body must contain a complete named class or a
7781/// structurally fragmented class prefix. A text reparse cannot prove any of
7782/// those ownership boundaries.
7783fn cpp_nested_namespace_sentinel<'tree>(
7784    node: Node<'tree>,
7785    source: &str,
7786) -> Option<CppNestedNamespaceSentinel<'tree>> {
7787    if !node.has_error() {
7788        return None;
7789    }
7790
7791    let (function, mut namespace_components) = if node.kind() == "ERROR" {
7792        let mut cursor = node.walk();
7793        let functions = node
7794            .named_children(&mut cursor)
7795            .filter(|child| child.kind() == "function_definition")
7796            .collect::<Vec<_>>();
7797        let [function] = functions.as_slice() else {
7798            return None;
7799        };
7800        if !function.has_error() {
7801            return None;
7802        }
7803        let mut cursor = node.walk();
7804        let children = node.children(&mut cursor).collect::<Vec<_>>();
7805        let function_index = children
7806            .iter()
7807            .position(|child| same_node(*child, *function))?;
7808        let [outer_keyword, outer_name, outer_open] =
7809            children.get(function_index.checked_sub(3)?..function_index)?
7810        else {
7811            return None;
7812        };
7813        if outer_keyword.kind() != "namespace"
7814            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
7815            || outer_open.kind() != "{"
7816        {
7817            return None;
7818        }
7819        (
7820            *function,
7821            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
7822        )
7823    } else if node.kind() == "function_definition" {
7824        let declaration_list = node.parent()?;
7825        let namespace = declaration_list.parent()?;
7826        if declaration_list.kind() != "declaration_list"
7827            || namespace.kind() != "namespace_definition"
7828            || namespace.child_by_field_name("body") != Some(declaration_list)
7829        {
7830            return None;
7831        }
7832        (node, Vec::new())
7833    } else {
7834        return None;
7835    };
7836
7837    let mut cursor = function.walk();
7838    let named = function
7839        .named_children(&mut cursor)
7840        .filter(|child| child.kind() != "comment")
7841        .collect::<Vec<_>>();
7842    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
7843        return None;
7844    };
7845    if first_type.kind() != "type_identifier" {
7846        return None;
7847    }
7848    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
7849    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
7850        return None;
7851    }
7852    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
7853        return None;
7854    }
7855    let inner_keyword = inner_error.named_child(0)?;
7856    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
7857        return None;
7858    }
7859    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
7860        return None;
7861    }
7862    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
7863    if inner_name.is_empty() || body.kind() != "compound_statement" {
7864        return None;
7865    }
7866    namespace_components.push(inner_name);
7867
7868    let mut cursor = body.walk();
7869    let has_complete_class = body.named_children(&mut cursor).any(|child| {
7870        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
7871            cpp_body_node(class_node).is_some()
7872                && class_like_name(class_node, source)
7873                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
7874        })
7875    });
7876    if !has_complete_class && cpp_sentinel_fragmented_class_tail(function, *body, source).is_none()
7877    {
7878        return None;
7879    }
7880
7881    Some(CppNestedNamespaceSentinel {
7882        function,
7883        body: *body,
7884        namespace_components,
7885    })
7886}
7887
7888/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
7889/// malformed namespace-sentinel function.  The recovery is deliberately
7890/// structural: the class must be a direct body item, its own class node must be
7891/// erroneous and end before a unique anonymous `}` in the enclosing
7892/// declaration-list, and that namespace's next sibling must be a standalone
7893/// `;`.  The complete interior must pass the existing member-shaped reparse
7894/// gate. This avoids source brace scans and does not borrow a close from an
7895/// unrelated later declaration.
7896fn cpp_sentinel_fragmented_class_tail<'tree>(
7897    function: Node<'tree>,
7898    body: Node<'tree>,
7899    source: &str,
7900) -> Option<CppSentinelFragmentedClassTail<'tree>> {
7901    let mut cursor = body.walk();
7902    let candidates = body
7903        .named_children(&mut cursor)
7904        .filter_map(|child| {
7905            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
7906                let class_body = cpp_body_node(class_node)?;
7907                if !class_node.has_error() {
7908                    return None;
7909                }
7910                let name = class_like_name(class_node, source)?;
7911                let raw_supertypes =
7912                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
7913                        .then(|| extract_cpp_supertypes(class_node, source));
7914                return Some((
7915                    class_node,
7916                    template_node,
7917                    name,
7918                    class_body,
7919                    class_body.start_byte().checked_add(1)?,
7920                    raw_supertypes,
7921                ));
7922            }
7923            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
7924            Some((
7925                child,
7926                None,
7927                prefix.name,
7928                prefix.open,
7929                prefix.open.end_byte(),
7930                prefix.raw_supertypes,
7931            ))
7932        })
7933        .collect::<Vec<_>>();
7934    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
7935        candidates.as_slice()
7936    else {
7937        return None;
7938    };
7939    if name.is_empty() || cpp_export_macro_token(name) {
7940        return None;
7941    }
7942
7943    let (close, semicolon) =
7944        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
7945
7946    let reparse_end = close.start_byte();
7947    if *reparse_start >= reparse_end {
7948        return None;
7949    }
7950    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
7951    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
7952        return None;
7953    }
7954    let class_range = Range {
7955        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
7956        end_byte: semicolon.end_byte(),
7957        start_line: template_node.map_or(class_node.start_position().row, |node| {
7958            node.start_position().row
7959        }) + 1,
7960        end_line: semicolon.end_position().row + 1,
7961    };
7962    Some(CppSentinelFragmentedClassTail {
7963        class_node: *class_node,
7964        template_node: *template_node,
7965        name: name.clone(),
7966        raw_supertypes: raw_supertypes.clone(),
7967        fragmented: FragmentedExportBody {
7968            reparse_start: *reparse_start,
7969            reparse_end,
7970            class_range,
7971        },
7972        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
7973    })
7974}
7975
7976/// Recover the class and out-of-line owner scopes from every malformed
7977/// namespace-sentinel region in `root`.
7978///
7979/// This is the shared structural counterpart to
7980/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
7981/// reuses the visitor's sentinel/class admission predicates instead of parsing
7982/// source text a second time.  The returned values own only ranges and names, so
7983/// they can be retained by an inverted usage scan after the tree borrow ends.
7984pub fn cpp_sentinel_recovered_classes(
7985    root: Node<'_>,
7986    source: &str,
7987) -> Vec<CppSentinelRecoveredClass> {
7988    if !root.has_error() {
7989        return Vec::new();
7990    }
7991    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
7992    let mut stack = vec![root];
7993    while let Some(current) = stack.pop() {
7994        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source) {
7995            let namespace_components = cpp_sentinel_recovered_namespace_components(
7996                recovered.function,
7997                &recovered.namespace_components,
7998                source,
7999            );
8000            let fragmented =
8001                cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, source);
8002            let mut class_candidates = Vec::new();
8003            let mut cursor = recovered.body.walk();
8004            for (class_node, template_node) in recovered
8005                .body
8006                .named_children(&mut cursor)
8007                .filter_map(cpp_sentinel_body_class_candidate)
8008            {
8009                let Some(name) = class_like_name(class_node, source) else {
8010                    continue;
8011                };
8012                if name.is_empty() || cpp_export_macro_token(&name) {
8013                    continue;
8014                }
8015                let is_fragmented = fragmented
8016                    .as_ref()
8017                    .is_some_and(|tail| same_node(tail.class_node, class_node));
8018                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
8019                    continue;
8020                }
8021                let class_range = if is_fragmented {
8022                    fragmented
8023                        .as_ref()
8024                        .map(|tail| tail.fragmented.class_range)
8025                        .expect("fragmented class range is present when class matches")
8026                } else {
8027                    cpp_declaration_range(template_node.unwrap_or(class_node))
8028                };
8029                class_candidates.push((class_range, name));
8030            }
8031            if let Some(fragmented) = fragmented
8032                .as_ref()
8033                .filter(|tail| tail.class_node.kind() == "ERROR")
8034            {
8035                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
8036            }
8037
8038            let mut owner_ranges =
8039                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
8040            cpp_sentinel_extend_unique_owner_ranges(
8041                &mut owner_ranges,
8042                cpp_sentinel_recovered_sibling_owner_ranges(
8043                    recovered.function,
8044                    &namespace_components,
8045                    source,
8046                ),
8047            );
8048            for (class_range, name) in class_candidates {
8049                push_cpp_sentinel_recovered_class(
8050                    &mut recovered_classes,
8051                    cpp_declaration_range(recovered.body),
8052                    &namespace_components,
8053                    class_range,
8054                    name,
8055                    &owner_ranges,
8056                );
8057            }
8058
8059            if let Some(declaration_list) = recovered
8060                .function
8061                .parent()
8062                .filter(|parent| parent.kind() == "declaration_list")
8063            {
8064                let outer_namespace =
8065                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
8066                push_cpp_sentinel_sibling_classes(
8067                    &mut recovered_classes,
8068                    declaration_list,
8069                    recovered.function,
8070                    &outer_namespace,
8071                    source,
8072                );
8073            }
8074        } else if let Some(region) = cpp_sentinel_macro_body_class_region(current, source) {
8075            let namespace_components = cpp_sentinel_recovered_namespace_components(
8076                current,
8077                &region.namespace_components,
8078                source,
8079            );
8080            let owner_container = current
8081                .parent()
8082                .filter(|parent| parent.kind() == "declaration_list")
8083                .unwrap_or(current);
8084            let owner_ranges =
8085                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
8086            push_cpp_sentinel_recovered_class(
8087                &mut recovered_classes,
8088                cpp_declaration_range(owner_container),
8089                &namespace_components,
8090                Range {
8091                    start_byte: region.class_start,
8092                    end_byte: region.class_close_end,
8093                    start_line: region.class_start_line,
8094                    end_line: region.class_close_line,
8095                },
8096                region.name,
8097                &owner_ranges,
8098            );
8099        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
8100            // A generic sentinel-prefixed class can be reduced as a malformed
8101            // function/ERROR without the explicit `namespace X` token pair.
8102            // Reuse the declaration visitor's bounded reparse and retain only
8103            // the recovered class identity/range here.
8104            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
8105                region;
8106            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
8107                continue;
8108            };
8109            let root = tree.root_node();
8110            let template_node = cpp_sentinel_reparsed_leading_template(root);
8111            let Some(reparsed_class) = cpp_sentinel_reparsed_class(root, template_node, source)
8112            else {
8113                continue;
8114            };
8115            let class_node = reparsed_class.declaration_node;
8116            let name = reparsed_class.name;
8117            let namespace_components =
8118                cpp_sentinel_recovered_namespace_components(current, &[], source);
8119            let owner_container = current
8120                .parent()
8121                .filter(|parent| parent.kind() == "declaration_list")
8122                .unwrap_or(current);
8123            let mut owner_ranges =
8124                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
8125            cpp_sentinel_extend_unique_owner_ranges(
8126                &mut owner_ranges,
8127                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
8128            );
8129            push_cpp_sentinel_recovered_class(
8130                &mut recovered_classes,
8131                cpp_declaration_range(owner_container),
8132                &namespace_components,
8133                Range {
8134                    start_byte: class_start,
8135                    end_byte: close_end,
8136                    start_line: class_node.start_position().row + 1,
8137                    end_line: class_node.end_position().row + 1,
8138                },
8139                name,
8140                &owner_ranges,
8141            );
8142            if owner_container.kind() == "declaration_list" {
8143                push_cpp_sentinel_sibling_classes(
8144                    &mut recovered_classes,
8145                    owner_container,
8146                    current,
8147                    &namespace_components,
8148                    source,
8149                );
8150            }
8151        }
8152
8153        let mut cursor = current.walk();
8154        stack.extend(current.named_children(&mut cursor));
8155    }
8156    // A shallower sentinel can expose nested classes as apparent namespace
8157    // siblings even after a deeper sentinel proves that a containing class
8158    // owns their ranges. Drop those shadow descriptors; scope recovery starts
8159    // from the proven containing class and appends parser-visible class
8160    // ancestors, preserving the full `Outer::Inner` chain.
8161    let shadowed = recovered_classes
8162        .iter()
8163        .map(|candidate| {
8164            recovered_classes.iter().any(|container| {
8165                container.class_range.start_byte <= candidate.class_range.start_byte
8166                    && container.class_range.end_byte >= candidate.class_range.end_byte
8167                    && container.class_range != candidate.class_range
8168                    && container.namespace_scope_components.len()
8169                        > candidate.namespace_scope_components.len()
8170                    && container
8171                        .namespace_scope_components
8172                        .starts_with(&candidate.namespace_scope_components)
8173            })
8174        })
8175        .collect::<Vec<_>>();
8176    let mut index = 0usize;
8177    recovered_classes.retain(|_| {
8178        let keep = !shadowed[index];
8179        index += 1;
8180        keep
8181    });
8182    recovered_classes
8183}
8184
8185/// A flat sentinel can swallow the first class while leaving later classes and
8186/// their out-of-line definitions as ordinary declaration-list siblings.  Once
8187/// the malformed class proves the sentinel envelope, retain those structurally
8188/// complete sibling classes under the same surviving namespace so every member
8189/// owner in the region uses one recovery contract.
8190fn push_cpp_sentinel_sibling_classes(
8191    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
8192    declaration_list: Node<'_>,
8193    sentinel_node: Node<'_>,
8194    namespace_components: &[String],
8195    source: &str,
8196) {
8197    let owner_ranges =
8198        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
8199    let namespace_range = cpp_declaration_range(declaration_list);
8200    let mut cursor = declaration_list.walk();
8201    for (class_node, template_node) in declaration_list
8202        .named_children(&mut cursor)
8203        .filter(|child| !same_node(*child, sentinel_node))
8204        .filter_map(cpp_sentinel_body_class_candidate)
8205    {
8206        let Some(name) = class_like_name(class_node, source) else {
8207            continue;
8208        };
8209        if name.is_empty()
8210            || cpp_export_macro_token(&name)
8211            || cpp_complete_class_body_close(class_node).is_none()
8212        {
8213            continue;
8214        }
8215        push_cpp_sentinel_recovered_class(
8216            recovered_classes,
8217            namespace_range,
8218            namespace_components,
8219            cpp_declaration_range(template_node.unwrap_or(class_node)),
8220            name,
8221            &owner_ranges,
8222        );
8223    }
8224}
8225
8226fn push_cpp_sentinel_recovered_class(
8227    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
8228    namespace_range: Range,
8229    namespace_components: &[String],
8230    class_range: Range,
8231    name: String,
8232    owner_ranges: &[CppSentinelRecoveredOwner],
8233) {
8234    let mut scope_components = namespace_components.to_vec();
8235    scope_components.push(name);
8236    let owner_ranges = owner_ranges
8237        .iter()
8238        .filter(|owner| owner.scope_components.starts_with(&scope_components))
8239        .cloned()
8240        .collect::<Vec<_>>();
8241    if recovered_classes.iter().any(|existing| {
8242        existing.class_range == class_range && existing.scope_components == scope_components
8243    }) {
8244        return;
8245    }
8246    recovered_classes.push(CppSentinelRecoveredClass {
8247        namespace_range,
8248        namespace_scope_components: namespace_components.to_vec(),
8249        class_range,
8250        scope_components,
8251        owner_ranges,
8252    });
8253}
8254
8255fn cpp_sentinel_recovered_namespace_components(
8256    function: Node<'_>,
8257    recovered_components: &[String],
8258    source: &str,
8259) -> Vec<String> {
8260    let mut ancestor_components = Vec::new();
8261    let mut ancestor = function.parent();
8262    while let Some(current) = ancestor {
8263        if current.kind() == "namespace_definition"
8264            && let Some(name_node) = current.child_by_field_name("name")
8265            && let Some(components) = cpp_name_components(name_node, source)
8266        {
8267            ancestor_components.push(
8268                components
8269                    .into_iter()
8270                    .map(|component| component.name)
8271                    .collect::<Vec<_>>(),
8272            );
8273        }
8274        ancestor = current.parent();
8275    }
8276    ancestor_components.reverse();
8277    let mut ancestors = ancestor_components
8278        .into_iter()
8279        .flatten()
8280        .collect::<Vec<_>>();
8281
8282    let overlap = (0..=ancestors.len().min(recovered_components.len()))
8283        .rev()
8284        .find(|length| {
8285            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
8286        })
8287        .unwrap_or(0);
8288    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
8289    ancestors
8290}
8291
8292fn cpp_sentinel_recovered_owner_ranges(
8293    body: Node<'_>,
8294    namespace_components: &[String],
8295    source: &str,
8296) -> Vec<CppSentinelRecoveredOwner> {
8297    let mut owners = Vec::new();
8298    walk_named_tree_preorder(body, true, |node| {
8299        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
8300    });
8301    owners
8302}
8303
8304fn cpp_sentinel_collect_owner_range(
8305    node: Node<'_>,
8306    namespace_components: &[String],
8307    source: &str,
8308    owners: &mut Vec<CppSentinelRecoveredOwner>,
8309) -> WalkControl {
8310    if node.kind() != "function_definition" {
8311        return WalkControl::Continue;
8312    }
8313    let Some(function_declarator) = extract_function_declarator(node) else {
8314        return WalkControl::Continue;
8315    };
8316    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
8317        return WalkControl::Continue;
8318    };
8319    let Some(mut components) = cpp_name_components(name_node, source) else {
8320        return WalkControl::Continue;
8321    };
8322    if components.len() <= 1 {
8323        return WalkControl::Continue;
8324    }
8325    components.pop();
8326    let mut owner_components = components
8327        .into_iter()
8328        .map(|component| component.name)
8329        .collect::<Vec<_>>();
8330    let overlap = (0..=namespace_components.len().min(owner_components.len()))
8331        .rev()
8332        .find(|length| {
8333            owner_components[..*length]
8334                == namespace_components[namespace_components.len().saturating_sub(*length)..]
8335        })
8336        .unwrap_or(0);
8337    let mut scope_components = namespace_components.to_vec();
8338    scope_components.extend(owner_components.drain(overlap..));
8339    if scope_components.len() <= namespace_components.len() {
8340        return WalkControl::Continue;
8341    }
8342    let range = cpp_declaration_range(node);
8343    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
8344        existing.range == range && existing.scope_components == scope_components
8345    }) {
8346        owners.push(CppSentinelRecoveredOwner {
8347            range,
8348            owner_name_start_byte: name_node.start_byte(),
8349            namespace_component_count: namespace_components.len(),
8350            scope_components,
8351        });
8352    }
8353    WalkControl::Continue
8354}
8355
8356fn cpp_sentinel_extend_unique_owner_ranges(
8357    owners: &mut Vec<CppSentinelRecoveredOwner>,
8358    additional: Vec<CppSentinelRecoveredOwner>,
8359) {
8360    for owner in additional {
8361        if !owners.iter().any(|existing| {
8362            existing.range == owner.range && existing.scope_components == owner.scope_components
8363        }) {
8364            owners.push(owner);
8365        }
8366    }
8367}
8368
8369fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
8370    if node.kind() != "ERROR" || node.named_child_count() != 1 {
8371        return false;
8372    }
8373    let Some(end_name) = node.named_child(0) else {
8374        return false;
8375    };
8376    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
8377        return false;
8378    }
8379    let mut cursor = node.walk();
8380    node.children(&mut cursor)
8381        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
8382}
8383
8384/// Collect owner definitions that the malformed sentinel left as later
8385/// declaration-list siblings. Parser-visible namespace siblings are a hard
8386/// boundary: their declarations must keep their own lexical namespace.
8387fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
8388    parent: Node<'_>,
8389    sentinel_node: Node<'_>,
8390    namespace_components: &[String],
8391    source: &str,
8392) -> Vec<CppSentinelRecoveredOwner> {
8393    let mut owners = Vec::new();
8394    let mut after_sentinel = false;
8395    let mut cursor = parent.walk();
8396    for child in parent.named_children(&mut cursor) {
8397        if !after_sentinel {
8398            if same_node(child, sentinel_node) {
8399                after_sentinel = true;
8400            }
8401            continue;
8402        }
8403        walk_named_tree_preorder(child, true, |node| {
8404            if node.kind() == "namespace_definition" {
8405                return WalkControl::SkipChildren;
8406            }
8407            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
8408        });
8409    }
8410    owners
8411}
8412
8413/// Collect owner definitions after a malformed namespace, stopping only at
8414/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
8415/// enclosing container is not trusted to belong to the recovered namespace.
8416fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
8417    parent: Node<'_>,
8418    sentinel_node: Node<'_>,
8419    namespace_components: &[String],
8420    source: &str,
8421) -> Option<Vec<CppSentinelRecoveredOwner>> {
8422    let mut owners = Vec::new();
8423    let mut after_namespace = false;
8424    let mut cursor = parent.walk();
8425    for child in parent.named_children(&mut cursor) {
8426        if !after_namespace {
8427            if same_node(child, sentinel_node) {
8428                after_namespace = true;
8429            }
8430            continue;
8431        }
8432        if cpp_sentinel_namespace_end(child, source) {
8433            return Some(owners);
8434        }
8435        walk_named_tree_preorder(child, true, |node| {
8436            if node.kind() == "namespace_definition" {
8437                return WalkControl::SkipChildren;
8438            }
8439            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
8440        });
8441    }
8442    None
8443}
8444
8445fn cpp_sentinel_recovered_sibling_owner_ranges(
8446    sentinel_node: Node<'_>,
8447    namespace_components: &[String],
8448    source: &str,
8449) -> Vec<CppSentinelRecoveredOwner> {
8450    let Some(declaration_list) = sentinel_node
8451        .parent()
8452        .filter(|parent| parent.kind() == "declaration_list")
8453    else {
8454        return Vec::new();
8455    };
8456    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
8457        declaration_list,
8458        sentinel_node,
8459        namespace_components,
8460        source,
8461    );
8462
8463    let Some(namespace) = declaration_list
8464        .parent()
8465        .filter(|parent| parent.kind() == "namespace_definition")
8466    else {
8467        return owners;
8468    };
8469    let Some(outer_parent) = namespace.parent() else {
8470        return owners;
8471    };
8472    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
8473        outer_parent,
8474        namespace,
8475        namespace_components,
8476        source,
8477    ) {
8478        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
8479    }
8480    owners
8481}
8482
8483fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
8484    let mut current = function_declarator.child_by_field_name("declarator")?;
8485    loop {
8486        if matches!(
8487            current.kind(),
8488            "qualified_identifier"
8489                | "scoped_identifier"
8490                | "scoped_type_identifier"
8491                | "identifier"
8492                | "field_identifier"
8493                | "operator_name"
8494                | "destructor_name"
8495                | "literal_operator_name"
8496        ) {
8497            return Some(current);
8498        }
8499        current = current
8500            .child_by_field_name("declarator")
8501            .or_else(|| current.child_by_field_name("name"))
8502            .or_else(|| last_named_child(current))?;
8503    }
8504}
8505
8506fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
8507    match node.kind() {
8508        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
8509            let mut components = match node.child_by_field_name("scope") {
8510                Some(scope) => cpp_name_components(scope, source)?,
8511                None => Vec::new(),
8512            };
8513            let name = node.child_by_field_name("name")?;
8514            components.push(canonical_cpp_qualified_component(name, source)?);
8515            Some(components)
8516        }
8517        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
8518    }
8519}
8520
8521fn cpp_sentinel_fragment_boundary<'tree>(
8522    function: Node<'tree>,
8523    class_node: Node<'tree>,
8524    class_body: Node<'tree>,
8525    source: &str,
8526) -> Option<(Node<'tree>, Node<'tree>)> {
8527    let declaration_list = function.parent()?;
8528    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
8529        return None;
8530    }
8531    let namespace = declaration_list.parent()?;
8532    if namespace.kind() != "namespace_definition"
8533        || namespace.child_by_field_name("body") != Some(declaration_list)
8534    {
8535        return None;
8536    }
8537    let mut cursor = declaration_list.walk();
8538    let closes = declaration_list
8539        .children(&mut cursor)
8540        .filter(|child| {
8541            !child.is_named()
8542                && child.kind() == "}"
8543                && child.start_byte() >= function.end_byte()
8544                && child.start_byte() > class_node.end_byte()
8545                && child.start_byte() > class_body.start_byte()
8546        })
8547        .collect::<Vec<_>>();
8548    let [close] = closes.as_slice() else {
8549        return None;
8550    };
8551    let semicolon = namespace.next_named_sibling()?;
8552    if !cpp_is_stray_semicolon(semicolon, source)
8553        || close.end_byte() != namespace.end_byte()
8554        || semicolon.start_byte() < namespace.end_byte()
8555    {
8556        return None;
8557    }
8558    Some((*close, semicolon))
8559}
8560
8561/// Detect the bogus declaration/function tree that tree-sitter recovers for a
8562/// region prefixed by an object-like macro sentinel the parser cannot see
8563/// (issue #941), and return the byte range `[start, end)` of the swallowed
8564/// declaration interior to reparse.
8565///
8566/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
8567/// `function_definition` whose first non-comment named child is the sentinel
8568/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
8569/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
8570/// the real items.
8571/// `start` is the end of the sentinel identifier -- everything after it is the
8572/// genuine source. `end` is the node's end, extended across any trailing empty
8573/// `;` statement the mis-parse displaced past the node (the class/struct closing
8574/// semicolon), so the reparse sees a complete, brace-balanced item.
8575///
8576/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
8577/// node (`has_error`). Unknown annotation/export macros can make a real callable
8578/// error-recovered even though tree-sitter still preserves its declarator, so a
8579/// preserved callable is admitted only when a displaced class keyword precedes
8580/// that declarator. The clean-reparse-to-items gate in
8581/// `cpp_reparsed_items_are_indexable` is the final arbiter.
8582/// Return the reparse start and, when present, the structurally recovered class
8583/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
8584/// separately from the reparse start because an opaque template-declaration
8585/// macro may precede it.
8586fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
8587    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
8588    {
8589        return None;
8590    }
8591    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
8592    // retain a valid function declarator despite the unknown export macro making
8593    // the outer node erroneous. Remember that declarator for the ordering gate
8594    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
8595    // class keyword precedes a spurious callable assembled from a later member.
8596    let mut declarator_cursor = node.walk();
8597    let preserved_callable = node
8598        .children_by_field_name("declarator", &mut declarator_cursor)
8599        .find_map(extract_function_declarator);
8600    // Leading documentation comments are attached to the malformed
8601    // `function_definition` as named children.  They are not part of the
8602    // sentinel prefix, so select the first non-comment child structurally
8603    // rather than requiring the sentinel to be child zero.  This is the shape
8604    // emitted for nlohmann/json's `basic_json`: its class documentation comment
8605    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
8606    // envelope otherwise ends at the first nested union.
8607    let mut cursor = node.walk();
8608    let first = node
8609        .named_children(&mut cursor)
8610        .find(|child| child.kind() != "comment")?;
8611    if first.kind() != "type_identifier" {
8612        return None;
8613    }
8614    let sentinel = normalize_cpp_whitespace(node_text(first, source));
8615    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
8616        return None;
8617    }
8618    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
8619    // makes the trailing sentinel of one region and the leading sentinel of the
8620    // next both land as bare macro-token identifiers ahead of the real content.
8621    // Advance past every leading macro-token identifier so the reparse begins at
8622    // genuine source rather than another sentinel that would re-form the bogus
8623    // shape and fail the reparse gate.
8624    let mut start = first.end_byte();
8625    let mut after_first = false;
8626    let mut cursor = node.walk();
8627    for child in node.named_children(&mut cursor) {
8628        if !after_first {
8629            if same_node(child, first) {
8630                after_first = true;
8631            }
8632            continue;
8633        }
8634        if matches!(child.kind(), "identifier" | "type_identifier")
8635            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
8636        {
8637            start = child.end_byte();
8638        } else {
8639            break;
8640        }
8641    }
8642    // An additional opaque template-declaration macro before a class can be
8643    // folded into the bogus function's qualified declarator.  In that shape
8644    // the macro is not a direct sibling we can skip above; tree-sitter exposes
8645    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
8646    // Reparse from that keyword (or a real preceding `template` keyword) so the
8647    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
8648    // a class nested in a genuine sentinel-wrapped namespace lies after the
8649    // body opening and must not change the established region start.
8650    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
8651    let mut class_start = None;
8652    let mut template_start = None;
8653    let mut stack = vec![node];
8654    while let Some(current) = stack.pop() {
8655        if current.start_byte() >= prefix_end {
8656            continue;
8657        }
8658        if matches!(
8659            current.kind(),
8660            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
8661        ) {
8662            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
8663                "class" | "struct" | "union" | "enum" => {
8664                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
8665                        seen.min(current.start_byte())
8666                    }));
8667                }
8668                "template" => {
8669                    template_start =
8670                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
8671                            seen.min(current.start_byte())
8672                        }));
8673                }
8674                _ => {}
8675            }
8676        }
8677        let mut cursor = current.walk();
8678        stack.extend(current.children(&mut cursor));
8679    }
8680    if preserved_callable.is_some_and(|callable| {
8681        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
8682    }) {
8683        return None;
8684    }
8685    if let Some(class_start) = class_start {
8686        start = template_start
8687            .filter(|template_start| *template_start < class_start)
8688            .unwrap_or(class_start);
8689    }
8690    Some((start, class_start))
8691}
8692
8693/// Locate a sentinel-prefixed class whose malformed declaration was split across
8694/// root-level siblings. The true class close is represented structurally as a
8695/// lone `}` error followed by the class's displaced `;`; nested method/body
8696/// errors are not direct siblings of the sentinel node and therefore cannot
8697/// satisfy this pair.
8698fn cpp_sentinel_macro_class_region(
8699    node: Node<'_>,
8700    source: &str,
8701) -> Option<(usize, usize, usize, usize, usize, usize)> {
8702    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
8703        return None;
8704    };
8705    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
8706        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
8707        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
8708    if class_start >= body_open_start {
8709        return None;
8710    }
8711    let sibling_close = {
8712        let mut sibling = node.next_named_sibling();
8713        let mut found = None;
8714        while let Some(current) = sibling {
8715            let next = current.next_named_sibling();
8716            if cpp_is_stray_close_brace(current, source)
8717                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
8718            {
8719                let semicolon = next.expect("checked above");
8720                found = Some((
8721                    current.start_byte(),
8722                    semicolon.end_byte(),
8723                    semicolon.end_position().row + 1,
8724                ));
8725                break;
8726            }
8727            sibling = next;
8728        }
8729        found
8730    };
8731    let (class_close_start, class_close_end, class_close_line) =
8732        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
8733            (class_close_start, class_close_end, class_close_line)
8734        } else {
8735            // When the malformed envelope itself is an ERROR, tree-sitter can
8736            // leave the class's balanced close in the source while promoting
8737            // all following members to siblings. Reparse the complete suffix
8738            // and use the first body-bearing class node's own field range as
8739            // the partition boundary. This keeps balancing in tree-sitter and
8740            // preserves the source's original byte offsets.
8741            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
8742            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
8743            let reparsed_class =
8744                cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)?;
8745            let body = reparsed_class.body;
8746            let class_close_end = body.end_byte();
8747            let class_close_start = class_close_end.checked_sub(1)?;
8748            let class_close_line = body.end_position().row + 1;
8749            (class_close_start, class_close_end, class_close_line)
8750        };
8751    if class_close_start <= class_start {
8752        return None;
8753    }
8754
8755    // Reparse only far enough to expose the class body opening. This is a
8756    // structured check that the candidate really begins with a body-bearing
8757    // class-like item; the original malformed tree cannot provide that node.
8758    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
8759    let class_root = tree.root_node();
8760    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
8761    let reparsed_class = cpp_sentinel_reparsed_class(class_root, template_node, source)?;
8762    let body = reparsed_class.body;
8763    // The class body opening must agree with the malformed wrapper's structured
8764    // body field. This rejects an inner nested class while permitting later
8765    // members to remain fragmented as root-level siblings in the bounded parse.
8766    if body.start_byte() != body_open_start {
8767        return None;
8768    }
8769    let body_start = body.start_byte().checked_add(1)?;
8770    (body_start < class_close_start).then_some((
8771        reparse_start,
8772        class_start,
8773        body_start,
8774        class_close_start,
8775        class_close_end,
8776        class_close_line,
8777    ))
8778}
8779
8780/// Find the `{` token immediately following the class/struct/union/enum token
8781/// at `class_start` in the malformed tree. The token is anonymous in the C++
8782/// grammar, so this deliberately walks all children (not only named children)
8783/// and relies on sibling structure rather than source-text searching.
8784fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
8785    let mut stack = vec![node];
8786    while let Some(current) = stack.pop() {
8787        if current.start_byte() == class_start
8788            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
8789        {
8790            let mut sibling = current.next_sibling();
8791            while let Some(candidate) = sibling {
8792                if candidate.kind() == "{" {
8793                    return Some(candidate.start_byte());
8794                }
8795                sibling = candidate.next_sibling();
8796            }
8797        }
8798        let mut cursor = current.walk();
8799        stack.extend(current.children(&mut cursor));
8800    }
8801    None
8802}
8803
8804/// The class body that tree-sitter displaced out of a sentinel-prefixed
8805/// declaration and left as the malformed node's next sibling.
8806///
8807/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
8808/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
8809/// the last child of that `ERROR` and its `{` opens a sibling
8810/// `compound_statement` instead. The body is still the malformed tree's own
8811/// structured token, which is what the caller's `body.start_byte() !=
8812/// body_open_start` agreement check needs; it just is not reachable by walking
8813/// forward from the class token inside the node.
8814fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
8815    node.next_named_sibling()
8816        .filter(|sibling| sibling.kind() == "compound_statement")
8817}
8818
8819fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
8820    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
8821    let mut end = if class_start.is_some() {
8822        cpp_macro_prefixed_class_end(source, start)?
8823    } else {
8824        node.end_byte()
8825    };
8826    if class_start.is_none()
8827        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
8828    {
8829        end = end.max(namespace_end);
8830    }
8831    let mut sibling = node.next_named_sibling();
8832    while let Some(current) = sibling {
8833        if !cpp_is_stray_semicolon(current, source) {
8834            break;
8835        }
8836        end = current.end_byte();
8837        sibling = current.next_named_sibling();
8838    }
8839    (start < end).then_some((start, end))
8840}
8841
8842/// Extend a sentinel reparse through a following namespace that tree-sitter
8843/// flattened into the sentinel node's sibling list.
8844///
8845/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
8846/// unknown macro becomes a false function return type and consumes the first
8847/// namespace body. A second `namespace detail` then loses its enclosing node:
8848/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
8849/// attaches its declarations to the surrounding error tree. Reparse from that
8850/// structured keyword so tree-sitter, rather than a source-text brace scan,
8851/// supplies the complete namespace boundary.
8852fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
8853    let mut sibling = node.next_sibling();
8854    let keyword = loop {
8855        let candidate = sibling?;
8856        sibling = candidate.next_sibling();
8857        if candidate.kind() != "comment" {
8858            break candidate;
8859        }
8860    };
8861    if keyword.kind() != "namespace" {
8862        return None;
8863    }
8864    let name = loop {
8865        let candidate = sibling?;
8866        sibling = candidate.next_sibling();
8867        if candidate.kind() != "comment" {
8868            break candidate;
8869        }
8870    };
8871    if cpp_namespace_name_components(name, source).is_empty() {
8872        return None;
8873    }
8874    let open = loop {
8875        let candidate = sibling?;
8876        sibling = candidate.next_sibling();
8877        if candidate.kind() != "comment" {
8878            break candidate;
8879        }
8880    };
8881    if open.kind() != "{" {
8882        return None;
8883    }
8884
8885    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
8886    let root = tree.root_node();
8887    let mut cursor = root.walk();
8888    let namespace = root
8889        .named_children(&mut cursor)
8890        .find(|candidate| candidate.kind() != "comment")?;
8891    (namespace.kind() == "namespace_definition"
8892        && namespace.start_byte() == keyword.start_byte()
8893        && namespace.child_by_field_name("body").is_some())
8894    .then_some(namespace.end_byte())
8895}
8896
8897/// Parse the source suffix beginning at a structurally recovered class/template
8898/// keyword and return the end of its first body-bearing class item.  The parser,
8899/// rather than a brace scanner, owns nested-body balancing.  This is needed when
8900/// the original error tree truncates the class and scatters later members as
8901/// top-level siblings.
8902fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
8903    let tree = cpp_reparse_region_items(source, start, source.len())?;
8904    let root = tree.root_node();
8905    let mut cursor = root.walk();
8906    for item in root.named_children(&mut cursor) {
8907        if item.end_byte() <= start || item.kind() == "comment" {
8908            continue;
8909        }
8910        let mut stack = vec![item];
8911        while let Some(current) = stack.pop() {
8912            if matches!(
8913                current.kind(),
8914                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
8915            ) && cpp_body_node(current).is_some()
8916            {
8917                return Some(current.end_byte());
8918            }
8919            let mut cursor = current.walk();
8920            stack.extend(current.named_children(&mut cursor));
8921        }
8922        // The recovered prefix is required to begin with the class item.  If
8923        // the first real item is something else, fail closed rather than skip
8924        // arbitrary source looking for a later class.
8925        return None;
8926    }
8927    None
8928}
8929
8930/// An empty `;` statement: the displaced closing semicolon of a struct/class that
8931/// the sentinel mis-parse split off past the bogus function node.
8932fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
8933    node.kind() == "expression_statement"
8934        && node.named_child_count() == 0
8935        && node_text(node, source).trim() == ";"
8936}
8937
8938/// Recover the real field name when a leading object-like annotation macro
8939/// displaces a qualified type into tree-sitter's bit-field recovery shape.
8940///
8941/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
8942/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
8943/// `bitfield_clause` containing an error plus an assignment.  The assignment's
8944/// left field is the only structured declaration name in that malformed tail.
8945/// A real bit-field is excluded by the all-caps macro type and required error.
8946fn recovered_macro_qualified_field_declarators<'tree>(
8947    node: Node<'tree>,
8948    source: &str,
8949) -> Option<Vec<Node<'tree>>> {
8950    if node.kind() != "field_declaration" {
8951        return None;
8952    }
8953    let macro_type = node.child_by_field_name("type")?;
8954    if macro_type.kind() != "type_identifier"
8955        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8956    {
8957        return None;
8958    }
8959    let pseudo_declarator = node.child_by_field_name("declarator")?;
8960    if pseudo_declarator.kind() != "field_identifier" {
8961        return None;
8962    }
8963    let mut cursor = node.walk();
8964    let clause = node
8965        .named_children(&mut cursor)
8966        .find(|child| child.kind() == "bitfield_clause")?;
8967    if !(0..clause.named_child_count()).any(|index| {
8968        clause
8969            .named_child(index)
8970            .is_some_and(|child| child.kind() == "ERROR")
8971    }) {
8972        return None;
8973    }
8974    let mut recovered = Vec::new();
8975    let mut stack = vec![clause];
8976    while let Some(current) = stack.pop() {
8977        if current.kind() == "assignment_expression"
8978            && let Some(left) = current.child_by_field_name("left")
8979            && extract_variable_name(left, source).is_some()
8980        {
8981            recovered.push(left);
8982            break;
8983        }
8984        let mut cursor = current.walk();
8985        stack.extend(current.named_children(&mut cursor));
8986    }
8987    if recovered.is_empty() {
8988        return None;
8989    }
8990    let mut cursor = node.walk();
8991    recovered.extend(
8992        node.children_by_field_name("declarator", &mut cursor)
8993            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
8994    );
8995    Some(recovered)
8996}
8997
8998/// Recover a macro-qualified constructor that tree-sitter represents as one
8999/// field declaration. The constructor call remains inside the direct recovery
9000/// error, while each member initializer becomes a false function declarator.
9001/// The class owner proves the constructor name and lets the caller ignore those
9002/// initializer declarators.
9003fn recovered_macro_qualified_constructor_call<'tree>(
9004    node: Node<'tree>,
9005    class_name: &str,
9006    source: &str,
9007) -> Option<Node<'tree>> {
9008    if node.kind() != "field_declaration" {
9009        return None;
9010    }
9011    let macro_type = node.child_by_field_name("type")?;
9012    if macro_type.kind() != "type_identifier"
9013        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9014    {
9015        return None;
9016    }
9017    let mut cursor = node.walk();
9018    let bitfield = node
9019        .named_children(&mut cursor)
9020        .find(|child| child.kind() == "bitfield_clause")?;
9021    let error = bitfield
9022        .named_child(0)
9023        .filter(|child| child.kind() == "ERROR")?;
9024    let mut stack = vec![error];
9025    while let Some(current) = stack.pop() {
9026        if current.kind() == "call_expression"
9027            && current
9028                .child_by_field_name("function")
9029                .is_some_and(|function| node_text(function, source) == class_name)
9030            && current
9031                .child_by_field_name("arguments")
9032                .is_some_and(|arguments| arguments.kind() == "argument_list")
9033        {
9034            return Some(current);
9035        }
9036        let mut cursor = current.walk();
9037        stack.extend(current.named_children(&mut cursor));
9038    }
9039    None
9040}
9041
9042/// Recover a macro-qualified member function declaration that tree-sitter
9043/// represents as a pseudo-field. An object-like export macro before a qualified
9044/// return type can displace the namespace and type into an ERROR/bitfield
9045/// recovery, leaving the callable as a structured `call_expression`.
9046///
9047/// The caller must route this shape before ordinary declarator classification;
9048/// otherwise the displaced namespace identifier is published as a field.
9049fn recovered_macro_qualified_function_call<'tree>(
9050    node: Node<'tree>,
9051    source: &str,
9052) -> Option<Node<'tree>> {
9053    if node.kind() != "field_declaration" {
9054        return None;
9055    }
9056    let macro_type = node.child_by_field_name("type")?;
9057    if macro_type.kind() != "type_identifier"
9058        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9059    {
9060        return None;
9061    }
9062    let declarator = node.child_by_field_name("declarator")?;
9063    if declarator.kind() != "field_identifier" {
9064        return None;
9065    }
9066    let mut cursor = node.walk();
9067    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9068    if !named.iter().any(|child| {
9069        child.kind() == "storage_class_specifier"
9070            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
9071    }) {
9072        return None;
9073    }
9074    let bitfield = named
9075        .iter()
9076        .find(|child| child.kind() == "bitfield_clause")?;
9077    let mut bitfield_cursor = bitfield.walk();
9078    let payload = bitfield
9079        .named_children(&mut bitfield_cursor)
9080        .collect::<Vec<_>>();
9081    let [displaced_error, call] = payload.as_slice() else {
9082        return None;
9083    };
9084    if displaced_error.kind() != "ERROR"
9085        || displaced_error.named_child_count() != 1
9086        || displaced_error
9087            .named_child(0)
9088            .is_none_or(|child| child.kind() != "identifier")
9089        || call.kind() != "call_expression"
9090        || call
9091            .child_by_field_name("function")
9092            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
9093        || call
9094            .child_by_field_name("arguments")
9095            .is_none_or(|arguments| arguments.kind() != "argument_list")
9096    {
9097        return None;
9098    }
9099    Some(*call)
9100}
9101
9102fn recovered_macro_qualified_function_parameters(
9103    arguments: Node<'_>,
9104    source: &str,
9105) -> Option<(String, Vec<String>)> {
9106    if arguments.kind() != "argument_list" {
9107        return None;
9108    }
9109    let mut cursor = arguments.walk();
9110    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
9111    if named.is_empty() {
9112        return Some(("()".to_string(), Vec::new()));
9113    }
9114    let mut types = Vec::new();
9115    let mut labels = Vec::new();
9116    let mut index = 0;
9117    while index < named.len() {
9118        let parameter_type = named[index];
9119        let parameter_name = named.get(index + 1).copied()?;
9120        if !matches!(
9121            parameter_type.kind(),
9122            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
9123        ) || parameter_name.kind() != "ERROR"
9124            || parameter_name.named_child_count() != 1
9125            || parameter_name
9126                .named_child(0)
9127                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
9128        {
9129            return None;
9130        }
9131        let parameter_name = parameter_name.named_child(0)?;
9132        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
9133        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
9134        index += 2;
9135    }
9136    Some((format!("({})", types.join(", ")), labels))
9137}
9138
9139/// Recognize the phantom field tree-sitter emits for a macro-qualified
9140/// function return type.  For example,
9141/// `static API result_type ThresholdForSmallA() { ... }` can become a
9142/// `field_declaration` (`API` as the type and `result_type` as a field name)
9143/// followed by a clean `function_definition` for `ThresholdForSmallA`.
9144///
9145/// Keep this predicate entirely tied to the CST envelope: the type must be an
9146/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
9147/// the declaration must carry a missing semicolon rather than a real one, and
9148/// the immediate named sibling must expose a function declarator.  A real
9149/// macro-decorated field with an explicit semicolon therefore remains a field.
9150pub fn recovered_macro_return_type_node<'tree>(
9151    node: Node<'tree>,
9152    source: &str,
9153) -> Option<Node<'tree>> {
9154    if node.kind() != "field_declaration" {
9155        return None;
9156    }
9157    let macro_type = node.child_by_field_name("type")?;
9158    if macro_type.kind() != "type_identifier"
9159        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9160    {
9161        return None;
9162    }
9163    let declarator = node.child_by_field_name("declarator")?;
9164    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
9165        return None;
9166    }
9167    let mut has_missing_semicolon = false;
9168    let mut has_real_semicolon = false;
9169    for index in 0..node.child_count() {
9170        let Some(child) = node.child(index) else {
9171            continue;
9172        };
9173        if child.kind() != ";" {
9174            continue;
9175        }
9176        if child.is_missing() {
9177            has_missing_semicolon = true;
9178        } else {
9179            has_real_semicolon = true;
9180        }
9181    }
9182    if !has_missing_semicolon || has_real_semicolon {
9183        return None;
9184    }
9185    let mut next = node.next_named_sibling();
9186    while next.is_some_and(|sibling| sibling.kind() == "comment") {
9187        next = next.and_then(|sibling| sibling.next_named_sibling());
9188    }
9189    let next = next?;
9190    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
9191        return None;
9192    }
9193    let function_declarator = next.child_by_field_name("declarator")?;
9194    extract_function_declarator(function_declarator).map(|_| declarator)
9195}
9196
9197/// Whether `name` is a type parameter of a template declaration that lexically
9198/// encloses `node`. The malformed macro-return field uses the parameter name as
9199/// its pseudo-declarator; preserving that field is necessary to publish a
9200/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
9201/// ancestors instead of interpreting source text so nested templates and
9202/// parser-recovered regions retain their real lexical scopes.
9203pub(crate) fn cpp_active_template_type_parameter(node: Node<'_>, name: &str, source: &str) -> bool {
9204    let mut ancestor = node.parent();
9205    while let Some(current) = ancestor {
9206        if current.kind() == "template_declaration"
9207            && let Some(parameters) = current.child_by_field_name("parameters")
9208        {
9209            let mut cursor = parameters.walk();
9210            if parameters.named_children(&mut cursor).any(|parameter| {
9211                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
9212                    && cpp_template_parameter_name(parameter, source)
9213                        .is_some_and(|parameter_name| parameter_name == name)
9214            }) {
9215                return true;
9216            }
9217        }
9218        ancestor = current.parent();
9219    }
9220    false
9221}
9222
9223/// Reparse the region `[start, end)` of `source` as C++, confined to the region
9224/// via included ranges so every reparsed node keeps its original byte offset and
9225/// line number. The existing visitors read node text from the original source,
9226/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
9227/// `parse_rust_region_tree` technique.
9228fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
9229    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
9230}
9231
9232fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
9233    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
9234        return None;
9235    }
9236    let semicolon = node.next_sibling()?;
9237    if semicolon.kind() != ";" || semicolon.is_missing() {
9238        return None;
9239    }
9240    let row = node.start_position().row;
9241    let mut start = node.start_byte();
9242    let mut sibling = node.prev_sibling();
9243    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
9244        if previous.kind() == ";" {
9245            break;
9246        }
9247        start = previous.start_byte();
9248        sibling = previous.prev_sibling();
9249    }
9250    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
9251}
9252
9253fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
9254    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
9255        return false;
9256    }
9257    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
9258        return false;
9259    }
9260    let Some(declarator) = (if node.kind() == "function_definition" {
9261        node.child_by_field_name("declarator")
9262            .and_then(extract_function_declarator)
9263    } else {
9264        node.named_child(0)
9265            .filter(|child| child.kind() == "function_declarator")
9266    }) else {
9267        return false;
9268    };
9269    let Some(name) = cpp_function_declarator_name_node(declarator) else {
9270        return false;
9271    };
9272    declarator.start_byte() == node.start_byte()
9273        && name.kind() == "identifier"
9274        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
9275}
9276
9277/// Reparse a fragmented class-body interior while preserving its original byte
9278/// and line offsets. Unlike an included-range translation-unit parse, a padded
9279/// prefix keeps C++ preprocessor directives after an access label in the same
9280/// recovery shape tree-sitter produces for a complete class body.
9281fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
9282    let bytes = source.as_bytes();
9283    let prefix = bytes.get(..start)?;
9284    let interior = bytes.get(start..end)?;
9285    let mut padded = Vec::with_capacity(end);
9286    padded.extend(
9287        prefix
9288            .iter()
9289            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
9290    );
9291    padded.extend_from_slice(interior);
9292    let padded = String::from_utf8(padded).ok()?;
9293    let mut parser = Parser::new();
9294    parser
9295        .set_language(&tree_sitter_cpp::LANGUAGE.into())
9296        .ok()?;
9297    parser.parse(&padded, None)
9298}
9299
9300/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
9301/// reparsed interior is indexed only when every top-level named node is a
9302/// well-formed C++ item (or a comment) and at least one real item is present.
9303/// Expression/statement soup surfaces as a top-level `ERROR` or
9304/// `expression_statement`, neither of which is an item kind, so it is rejected.
9305///
9306/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
9307/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
9308/// swallowed by a preceding dangling sentinel) reparses to a real
9309/// `namespace_definition` whose body still holds a bogus `function_definition`,
9310/// so the subtree legitimately carries an error. Container items are admitted
9311/// even with an internal error; the inner bogus function is recovered recursively
9312/// when `visit_function_definition` walks it. Each recursion strips at least one
9313/// leading sentinel, so the region strictly shrinks and recovery terminates.
9314///
9315/// A top-level `function_definition` is the one place we stay strict: it is
9316/// admitted only when it is clean or is itself a sentinel candidate. A function
9317/// that has an error and is not a sentinel is a real callable with a broken body,
9318/// so we refuse the whole reparse and let the ordinary path handle it (preserving
9319/// its real return type rather than re-deriving an implicit one).
9320fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
9321    let mut cursor = root.walk();
9322    let mut saw_item = false;
9323    for child in root.named_children(&mut cursor) {
9324        match child.kind() {
9325            "comment" => {}
9326            "function_definition" => {
9327                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
9328                    return false;
9329                }
9330                saw_item = true;
9331            }
9332            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
9333            _ => return false,
9334        }
9335    }
9336    saw_item
9337}
9338
9339/// Robustness gate for a reparsed fragmented multiple-base export class body
9340/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
9341/// kinds a class body produces when reparsed at translation-unit scope: the
9342/// access-specifier label preceding the first member surfaces as a
9343/// `labeled_statement` wrapping that member, and members surface as
9344/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
9345/// Statement or expression soup surfaces as other top-level kinds and is rejected,
9346/// so only a genuinely member-shaped body is ever re-owned as members; anything
9347/// ambiguous falls back to indexing the class alone.
9348fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
9349    if node.kind() != "ERROR" {
9350        return false;
9351    }
9352    let mut stack = Vec::new();
9353    let mut saw_function_declarator = false;
9354    let mut cursor = node.walk();
9355    for child in node.named_children(&mut cursor) {
9356        stack.push(child);
9357    }
9358    while let Some(current) = stack.pop() {
9359        match current.kind() {
9360            // Tree-sitter may wrap adjacent copy-control declarations in a
9361            // nested ERROR. Keep descending only through ERROR wrappers; the
9362            // actual declaration payload must be a function_declarator.
9363            "ERROR" => {
9364                let mut cursor = current.walk();
9365                stack.extend(current.named_children(&mut cursor));
9366            }
9367            "function_declarator" => saw_function_declarator = true,
9368            _ => return false,
9369        }
9370    }
9371    saw_function_declarator
9372}
9373
9374fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
9375    if node.kind() != "ERROR" {
9376        return false;
9377    }
9378    let mut cursor = node.walk();
9379    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9380    let [explicit, constructor_error, destructor] = named.as_slice() else {
9381        return false;
9382    };
9383    let Some(constructor) = constructor_error.named_child(0) else {
9384        return false;
9385    };
9386    let Some(constructor_name) =
9387        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
9388    else {
9389        return false;
9390    };
9391    let Some(destructor_name) =
9392        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
9393    else {
9394        return false;
9395    };
9396    let Some(destroyed_type) = destructor_name.named_child(0) else {
9397        return false;
9398    };
9399    explicit.kind() == "explicit_function_specifier"
9400        && constructor_error.kind() == "ERROR"
9401        && constructor_error.named_child_count() == 1
9402        && constructor.kind() == "function_declarator"
9403        && constructor_name.kind() == "identifier"
9404        && destructor.kind() == "function_declarator"
9405        && destructor_name.kind() == "destructor_name"
9406        && destroyed_type.kind() == "identifier"
9407        && node_text(constructor_name, source) == node_text(destroyed_type, source)
9408}
9409
9410fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
9411    if node.kind() != "compound_statement" {
9412        return false;
9413    }
9414    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
9415        return false;
9416    };
9417    if prefix.kind() == "labeled_statement"
9418        && prefix.named_child(0).is_some_and(|label| {
9419            matches!(
9420                node_text(label, source).trim(),
9421                "public" | "private" | "protected"
9422            )
9423        })
9424    {
9425        return prefix.named_children(&mut prefix.walk()).any(|child| {
9426            child.kind() == "declaration"
9427                && child.has_error()
9428                && child
9429                    .named_children(&mut child.walk())
9430                    .any(cpp_reparsed_member_error_is_indexable)
9431        });
9432    }
9433    // A malformed constructor initializer can be split into a declaration
9434    // followed by its compound body when the class prefix already contains
9435    // realistic members. Keep this admission tied to that exact structured
9436    // declaration/error/body chain rather than accepting arbitrary blocks.
9437    prefix.kind() == "declaration"
9438        && prefix.has_error()
9439        && prefix
9440            .named_children(&mut prefix.walk())
9441            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
9442}
9443
9444fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
9445    if !cpp_reparsed_member_error_is_indexable(node) {
9446        return false;
9447    }
9448    let Some(preproc) = node.next_named_sibling() else {
9449        return false;
9450    };
9451    preproc.kind() == "preproc_if"
9452        && preproc.has_error()
9453        && preproc
9454            .named_children(&mut preproc.walk())
9455            .any(|child| child.kind() == "expression_statement" && child.has_error())
9456        && preproc
9457            .next_named_sibling()
9458            .is_some_and(|body| body.kind() == "compound_statement")
9459}
9460
9461/// Return a function body whose braces and ownership are explicit in the
9462/// reparsed class-member tree. An error below a real function envelope is
9463/// recoverable by the ordinary function visitor; a missing/deferred body is
9464/// not, because accepting it would let statement soup masquerade as a member.
9465fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
9466    if node.kind() != "function_definition" {
9467        return None;
9468    }
9469    let body = node.child_by_field_name("body")?;
9470    if body.kind() != "compound_statement" {
9471        return None;
9472    }
9473    let open = body.child(0)?;
9474    let close = body.child(body.child_count().checked_sub(1)?)?;
9475    if open.kind() != "{"
9476        || open.is_missing()
9477        || close.kind() != "}"
9478        || close.is_missing()
9479        || close.end_byte() != body.end_byte()
9480        || body.end_byte() != node.end_byte()
9481    {
9482        return None;
9483    }
9484    Some(body)
9485}
9486
9487fn cpp_reparsed_member_function_errors_are_in_body(
9488    node: Node<'_>,
9489    body: Node<'_>,
9490    source: &str,
9491) -> bool {
9492    let mut cursor = node.walk();
9493    node.children(&mut cursor).all(|child| {
9494        same_node(child, body)
9495            || cpp_reparsed_member_attribute_error(child, source)
9496            || cpp_reparsed_member_signature_identifier_errors(child)
9497            || (!child.has_error() && !child.is_error() && !child.is_missing())
9498    })
9499}
9500
9501/// A complete callable can still carry parser errors in its signature when a
9502/// project annotation is not part of the C++ grammar (`nonneg int`,
9503/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
9504/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
9505/// leaves inside the already-proven callable envelope; structured statements,
9506/// literals, missing tokens, and other malformed signature payload remain
9507/// rejected.
9508fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
9509    if !node.has_error() && !node.is_error() && !node.is_missing() {
9510        return false;
9511    }
9512    let mut stack = vec![node];
9513    let mut saw_error = false;
9514    while let Some(current) = stack.pop() {
9515        if current.is_missing() {
9516            return false;
9517        }
9518        if current.kind() == "ERROR" {
9519            saw_error = true;
9520            let mut cursor = current.walk();
9521            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
9522            if children
9523                .iter()
9524                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
9525            {
9526                return false;
9527            }
9528            stack.extend(children);
9529            continue;
9530        }
9531        let mut cursor = current.walk();
9532        stack.extend(current.children(&mut cursor));
9533    }
9534    saw_error
9535}
9536
9537fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
9538    node.kind() == "ERROR"
9539        && node.named_child_count() == 1
9540        && node.named_child(0).is_some_and(|attribute| {
9541            attribute.kind() == "identifier"
9542                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
9543        })
9544}
9545
9546/// A C++ attribute placed between a member's declarator and body can make
9547/// tree-sitter expose the callable as
9548/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
9549/// Keep this admission tied to that exact node geometry. In particular, an
9550/// arbitrary ERROR or identifier before a compound statement is not enough.
9551fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
9552    let Some(body) = cpp_reparsed_member_function_body(node) else {
9553        return false;
9554    };
9555    let mut cursor = node.walk();
9556    let named = node
9557        .named_children(&mut cursor)
9558        .filter(|child| child.kind() != "comment")
9559        .collect::<Vec<_>>();
9560    let [type_node, error, attribute, body_node] = named.as_slice() else {
9561        return false;
9562    };
9563    if !same_node(*body_node, body)
9564        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
9565        || attribute.kind() != "identifier"
9566        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
9567        || error.kind() != "ERROR"
9568        || error.named_child_count() != 1
9569    {
9570        return false;
9571    }
9572    error
9573        .named_child(0)
9574        .is_some_and(cpp_reparsed_attribute_callable_declarator)
9575}
9576
9577fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
9578    cpp_structured_type_path(node, source).is_some()
9579        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
9580}
9581
9582fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
9583    let Some(body) = cpp_reparsed_member_function_body(node) else {
9584        return false;
9585    };
9586    let mut cursor = node.walk();
9587    let named = node
9588        .named_children(&mut cursor)
9589        .filter(|child| child.kind() != "comment")
9590        .collect::<Vec<_>>();
9591    let [friend, return_error, declarator, body_node] = named.as_slice() else {
9592        return false;
9593    };
9594    let Some(return_type) = return_error.named_child(0) else {
9595        return false;
9596    };
9597    same_node(*body_node, body)
9598        && friend.kind() == "type_identifier"
9599        && node_text(*friend, source) == "friend"
9600        && return_error.kind() == "ERROR"
9601        && return_error.named_child_count() == 1
9602        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
9603        && extract_function_declarator(*declarator)
9604            .and_then(cpp_function_declarator_name_node)
9605            .is_some()
9606}
9607
9608fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
9609    let Some(body) = cpp_reparsed_member_function_body(node) else {
9610        return false;
9611    };
9612    let mut cursor = node.walk();
9613    let named = node
9614        .named_children(&mut cursor)
9615        .filter(|child| child.kind() != "comment")
9616        .collect::<Vec<_>>();
9617    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
9618        return false;
9619    };
9620    let Some(return_type) = return_error.named_child(0) else {
9621        return false;
9622    };
9623    same_node(*body_node, body)
9624        && prefix
9625            .iter()
9626            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
9627        && attribute.kind() == "type_identifier"
9628        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
9629        && return_error.kind() == "ERROR"
9630        && return_error.named_child_count() == 1
9631        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
9632        && extract_function_declarator(*declarator)
9633            .and_then(cpp_function_declarator_name_node)
9634            .is_some()
9635}
9636
9637/// An included-range reparse that begins inside a malformed class can merge an
9638/// access label and following template member. Tree-sitter then emits the label
9639/// as the `template_type` name, the template parameter list as its arguments,
9640/// an ERROR-wrapped return type, the callable declarator, and its complete
9641/// body. Admit only that exact structured displacement.
9642fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
9643    let Some(body) = cpp_reparsed_member_function_body(node) else {
9644        return false;
9645    };
9646    let mut cursor = node.walk();
9647    let named = node
9648        .named_children(&mut cursor)
9649        .filter(|child| child.kind() != "comment")
9650        .collect::<Vec<_>>();
9651    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
9652        return false;
9653    };
9654    let Some(template_name) = template_type.child_by_field_name("name") else {
9655        return false;
9656    };
9657    let Some(arguments) = template_type.child_by_field_name("arguments") else {
9658        return false;
9659    };
9660    let Some(return_type) = return_error.named_child(0) else {
9661        return false;
9662    };
9663    let mut cursor = template_type.walk();
9664    let template_errors = template_type
9665        .named_children(&mut cursor)
9666        .filter(|child| child.kind() == "ERROR")
9667        .collect::<Vec<_>>();
9668    let [comment_error] = template_errors.as_slice() else {
9669        return false;
9670    };
9671    let mut cursor = comment_error.walk();
9672    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
9673    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
9674        return false;
9675    };
9676    same_node(*body_node, body)
9677        && template_type.kind() == "template_type"
9678        && template_name.kind() == "type_identifier"
9679        && matches!(
9680            node_text(template_name, source).trim(),
9681            "public" | "private" | "protected"
9682        )
9683        && arguments.kind() == "template_argument_list"
9684        && arguments.named_child_count() > 0
9685        && !arguments.has_error()
9686        && !colon.is_named()
9687        && colon.kind() == ":"
9688        && comments.iter().all(|child| child.kind() == "comment")
9689        && !template_keyword.is_named()
9690        && template_keyword.kind() == "template"
9691        && return_error.kind() == "ERROR"
9692        && return_error.named_child_count() == 1
9693        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
9694        && extract_function_declarator(*declarator)
9695            .and_then(cpp_function_declarator_name_node)
9696            .is_some()
9697}
9698
9699/// Return the constructor declaration tree-sitter can merge into an access
9700/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
9701/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
9702/// declaration's apparent type; the callable name must still exactly match the
9703/// recovered class, so unrelated labeled statements are never re-owned.
9704fn cpp_reparsed_preprocessor_constructor<'tree>(
9705    node: Node<'tree>,
9706    class_name: &str,
9707    source: &str,
9708) -> Option<Node<'tree>> {
9709    if node.kind() != "labeled_statement" {
9710        return None;
9711    }
9712    let mut cursor = node.walk();
9713    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9714    let [label, directive_error, declaration] = named.as_slice() else {
9715        return None;
9716    };
9717    if label.kind() != "statement_identifier"
9718        || !matches!(
9719            node_text(*label, source),
9720            "public" | "private" | "protected"
9721        )
9722        || directive_error.kind() != "ERROR"
9723        || directive_error.child_count() != 1
9724        || directive_error
9725            .child(0)
9726            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
9727        || declaration.kind() != "declaration"
9728        || declaration.named_child_count() != 2
9729    {
9730        return None;
9731    }
9732    let apparent_type = declaration.child_by_field_name("type")?;
9733    if apparent_type.kind() != "type_identifier"
9734        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
9735    {
9736        return None;
9737    }
9738    let declarator = declaration.child_by_field_name("declarator")?;
9739    let function = extract_function_declarator(declarator)?;
9740    let name = cpp_function_declarator_name_node(function)?;
9741    (node_text(name, source) == class_name).then_some(*declaration)
9742}
9743
9744fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
9745    if extract_function_declarator(node)
9746        .and_then(cpp_function_declarator_name_node)
9747        .is_some()
9748    {
9749        return true;
9750    }
9751    node.kind() == "init_declarator"
9752        && node
9753            .child_by_field_name("declarator")
9754            .is_some_and(|declarator| declarator.kind() == "identifier")
9755        && node
9756            .child_by_field_name("value")
9757            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
9758}
9759
9760/// Return true for the constrained/attribute form that tree-sitter splits into
9761/// an ERROR declaration, a preprocessor `requires` clause, and a following
9762/// compound statement. The three nodes must remain immediate named siblings;
9763/// this deliberately does not search source text or skip unrelated statements.
9764fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
9765    if node.kind() != "ERROR" || node.named_child_count() != 3 {
9766        return false;
9767    }
9768    let mut cursor = node.walk();
9769    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9770    let [type_node, function_declarator, attribute] = named.as_slice() else {
9771        return false;
9772    };
9773    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
9774        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
9775        || attribute.kind() != "identifier"
9776        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
9777    {
9778        return false;
9779    }
9780    let Some(preproc) =
9781        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
9782    else {
9783        return false;
9784    };
9785    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
9786        .filter(|sibling| sibling.kind() == "compound_statement")
9787    else {
9788        return false;
9789    };
9790    let Some(open) = body.child(0) else {
9791        return false;
9792    };
9793    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
9794        return false;
9795    };
9796    let Some(condition) = preproc.child_by_field_name("condition") else {
9797        return false;
9798    };
9799    let mut cursor = preproc.walk();
9800    let payload = preproc
9801        .named_children(&mut cursor)
9802        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
9803        .collect::<Vec<_>>();
9804    let [requires_statement] = payload.as_slice() else {
9805        return false;
9806    };
9807    let requires_clause = requires_statement.named_child(0);
9808
9809    open.kind() == "{"
9810        && !open.is_missing()
9811        && close.kind() == "}"
9812        && !close.is_missing()
9813        && close.end_byte() == body.end_byte()
9814        && requires_statement.kind() == "expression_statement"
9815        && requires_statement.named_child_count() == 1
9816        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
9817}
9818
9819fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
9820    let mut sibling = node.next_named_sibling();
9821    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
9822        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
9823    }
9824    sibling
9825}
9826
9827fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
9828    let mut sibling = node.prev_named_sibling();
9829    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
9830        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
9831    }
9832    sibling
9833}
9834
9835fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
9836    let Some(preproc) =
9837        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
9838    else {
9839        return false;
9840    };
9841    let Some(error) =
9842        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
9843    else {
9844        return false;
9845    };
9846    cpp_reparsed_attribute_requires_error(error, source)
9847}
9848
9849fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
9850    node: Node<'tree>,
9851    source: &str,
9852) -> Option<Node<'tree>> {
9853    if node.kind() != "ERROR" {
9854        return None;
9855    }
9856    let mut cursor = node.walk();
9857    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9858    let [parameter, macro_name, message] = named.as_slice() else {
9859        return None;
9860    };
9861    let parameter_name = parameter.named_child(0)?;
9862    (parameter.kind() == "type_parameter_declaration"
9863        && parameter_name.kind() == "type_identifier"
9864        && macro_name.kind() == "type_identifier"
9865        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
9866        && message.kind() == "string_literal")
9867        .then_some(parameter_name)
9868}
9869
9870/// Recognize the alternate constraint-macro prefix where tree-sitter retains
9871/// the complete qualified constraint as a fourth child instead of moving it
9872/// into the following function. Keep the gate tied to a two-type template
9873/// constraint that names the declared type parameter.
9874fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
9875    node: Node<'tree>,
9876    source: &str,
9877) -> Option<Node<'tree>> {
9878    if node.kind() != "ERROR" {
9879        return None;
9880    }
9881    let mut cursor = node.walk();
9882    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9883    let [parameter, macro_name, message, constraint] = named.as_slice() else {
9884        return None;
9885    };
9886    let parameter_name = parameter.named_child(0)?;
9887    let constraint_scope = constraint.child_by_field_name("scope")?;
9888    let constraint_template = constraint.child_by_field_name("name")?;
9889    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
9890    let mut argument_cursor = constraint_arguments.walk();
9891    let constraint_types = constraint_arguments
9892        .named_children(&mut argument_cursor)
9893        .collect::<Vec<_>>();
9894    if parameter.kind() != "type_parameter_declaration"
9895        || parameter_name.kind() != "type_identifier"
9896        || macro_name.kind() != "type_identifier"
9897        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
9898        || message.kind() != "string_literal"
9899        || constraint.kind() != "qualified_identifier"
9900        || constraint_scope.kind() != "namespace_identifier"
9901        || !matches!(
9902            constraint_template.kind(),
9903            "template_function" | "template_type"
9904        )
9905        || !matches!(constraint_types.as_slice(), [left, right]
9906            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
9907        || constraint_arguments.has_error()
9908    {
9909        return None;
9910    }
9911    let parameter_text = node_text(parameter_name, source);
9912    let mut stack = constraint_types;
9913    while let Some(current) = stack.pop() {
9914        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
9915            return Some(parameter_name);
9916        }
9917        let mut cursor = current.walk();
9918        stack.extend(current.named_children(&mut cursor));
9919    }
9920    None
9921}
9922
9923fn cpp_reparsed_template_macro_companion_is_indexable(
9924    node: Node<'_>,
9925    parameter_name: Node<'_>,
9926    source: &str,
9927) -> bool {
9928    let Some(body) = cpp_reparsed_member_function_body(node) else {
9929        return false;
9930    };
9931    let mut cursor = node.walk();
9932    let named = node
9933        .named_children(&mut cursor)
9934        .filter(|child| child.kind() != "comment")
9935        .collect::<Vec<_>>();
9936    let [
9937        constraint,
9938        close_error,
9939        storage,
9940        return_error,
9941        declarator,
9942        body_node,
9943    ] = named.as_slice()
9944    else {
9945        return false;
9946    };
9947    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
9948        return false;
9949    };
9950    let Some(constraint_template) = constraint.child_by_field_name("name") else {
9951        return false;
9952    };
9953    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
9954        return false;
9955    };
9956    let Some(return_type) = return_error.named_child(0) else {
9957        return false;
9958    };
9959    let mut cursor = constraint_arguments.walk();
9960    let constraint_types = constraint_arguments
9961        .named_children(&mut cursor)
9962        .collect::<Vec<_>>();
9963    same_node(*body_node, body)
9964        && constraint.kind() == "qualified_identifier"
9965        && constraint_scope.kind() == "namespace_identifier"
9966        && constraint_template.kind() == "template_type"
9967        && matches!(constraint_types.as_slice(), [left, right]
9968            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
9969        && !constraint_arguments.has_error()
9970        && close_error.kind() == "ERROR"
9971        && close_error.named_child_count() == 0
9972        && storage.kind() == "storage_class_specifier"
9973        && return_error.kind() == "ERROR"
9974        && return_error.named_child_count() == 1
9975        && return_type.kind() == "identifier"
9976        && node_text(return_type, source) == node_text(parameter_name, source)
9977        && extract_function_declarator(*declarator)
9978            .and_then(cpp_function_declarator_name_node)
9979            .is_some()
9980}
9981
9982fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
9983    node: Node<'tree>,
9984    parameter_name: Node<'_>,
9985    source: &str,
9986) -> Option<Node<'tree>> {
9987    let body = cpp_reparsed_member_function_body(node)?;
9988    let constraint = node.child_by_field_name("type")?;
9989    let constraint_template = constraint.child_by_field_name("name")?;
9990    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
9991    let mut argument_cursor = constraint_arguments.walk();
9992    let constraint_types = constraint_arguments
9993        .named_children(&mut argument_cursor)
9994        .collect::<Vec<_>>();
9995    if constraint.kind() != "qualified_identifier"
9996        || constraint_template.kind() != "template_type"
9997        || !matches!(constraint_types.as_slice(), [left, right]
9998            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
9999        || constraint_arguments.has_error()
10000        || node
10001            .child_by_field_name("body")
10002            .is_none_or(|candidate| !same_node(candidate, body))
10003    {
10004        return None;
10005    }
10006
10007    let mut cursor = node.walk();
10008    let recovery_errors = node
10009        .named_children(&mut cursor)
10010        .filter(|child| child.kind() == "ERROR")
10011        .collect::<Vec<_>>();
10012    if !recovery_errors
10013        .iter()
10014        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
10015        || !recovery_errors.iter().all(|error| {
10016            error.named_child_count() == 0
10017                || cpp_reparsed_constraint_macro_error(*error, source)
10018                || (error.named_child_count() == 1
10019                    && error
10020                        .named_child(0)
10021                        .is_some_and(|child| child.kind() == "function_declarator"))
10022        })
10023    {
10024        return None;
10025    }
10026
10027    let parameter_text = node_text(parameter_name, source);
10028    let mut declarators = node
10029        .child_by_field_name("declarator")
10030        .and_then(extract_function_declarator)
10031        .into_iter()
10032        .collect::<Vec<_>>();
10033    for error in recovery_errors {
10034        let mut stack = vec![error];
10035        while let Some(current) = stack.pop() {
10036            if current.kind() == "function_declarator" {
10037                declarators.push(current);
10038            }
10039            let mut cursor = current.walk();
10040            stack.extend(current.named_children(&mut cursor));
10041        }
10042    }
10043    declarators.into_iter().find(|declarator| {
10044        cpp_function_declarator_name_node(*declarator)
10045            .is_some_and(|name| name.kind() == "identifier")
10046            && declarator
10047                .child_by_field_name("parameters")
10048                .is_some_and(|parameters| {
10049                    parameters
10050                        .named_children(&mut parameters.walk())
10051                        .filter_map(|parameter| parameter.child_by_field_name("type"))
10052                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
10053                })
10054    })
10055}
10056
10057fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
10058    node: Node<'_>,
10059    parameter_name: Node<'_>,
10060    source: &str,
10061) -> bool {
10062    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
10063}
10064
10065fn cpp_reparsed_template_macro_function_companion_is_indexable(
10066    node: Node<'_>,
10067    parameter_name: Node<'_>,
10068    source: &str,
10069) -> bool {
10070    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
10071        return false;
10072    }
10073    let Some(return_type) = node.child_by_field_name("type") else {
10074        return false;
10075    };
10076    let Some(function_declarator) = node
10077        .child_by_field_name("declarator")
10078        .and_then(extract_function_declarator)
10079    else {
10080        return false;
10081    };
10082    if cpp_function_declarator_name_node(function_declarator).is_none()
10083        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
10084    {
10085        return false;
10086    }
10087    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
10088        return false;
10089    };
10090    let parameter_text = node_text(parameter_name, source);
10091    parameters
10092        .named_children(&mut parameters.walk())
10093        .any(|parameter| {
10094            parameter
10095                .child_by_field_name("type")
10096                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
10097        })
10098}
10099
10100fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
10101    if node.kind() != "ERROR" {
10102        return false;
10103    }
10104    let mut stack = vec![node];
10105    while let Some(current) = stack.pop() {
10106        let macro_shape = match current.kind() {
10107            "call_expression" => current
10108                .child_by_field_name("function")
10109                .zip(current.child_by_field_name("arguments")),
10110            "init_declarator" => current
10111                .child_by_field_name("declarator")
10112                .zip(current.child_by_field_name("value")),
10113            _ => None,
10114        };
10115        if let Some((name, arguments)) = macro_shape
10116            && name.kind() == "identifier"
10117            && arguments.kind() == "argument_list"
10118            && arguments.named_child_count() >= 2
10119            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
10120        {
10121            return true;
10122        }
10123        let mut cursor = current.walk();
10124        stack.extend(current.named_children(&mut cursor));
10125    }
10126    false
10127}
10128
10129fn cpp_recovered_template_macro_constructor<'tree>(
10130    node: Node<'tree>,
10131    source: &str,
10132) -> Option<(Node<'tree>, Node<'tree>)> {
10133    let mut prefix = node.prev_named_sibling()?;
10134    while prefix.kind() == "comment" {
10135        prefix = prefix.prev_named_sibling()?;
10136    }
10137    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
10138    let parameter = parameter_name
10139        .parent()
10140        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
10141    let declarator =
10142        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
10143    Some((declarator, parameter))
10144}
10145
10146fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
10147    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
10148        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
10149            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
10150                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
10151                    function,
10152                    parameter_name,
10153                    source,
10154                )
10155        });
10156    }
10157    let Some(parameter_name) =
10158        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
10159    else {
10160        return false;
10161    };
10162    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
10163        cpp_reparsed_template_macro_function_companion_is_indexable(
10164            function,
10165            parameter_name,
10166            source,
10167        )
10168    })
10169}
10170
10171fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10172    let function_name = node
10173        .child_by_field_name("declarator")
10174        .and_then(extract_function_declarator)
10175        .and_then(cpp_function_declarator_name_node);
10176    if let Some(body) = cpp_reparsed_member_function_body(node)
10177        && function_name.is_some()
10178        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
10179    {
10180        return true;
10181    }
10182    cpp_reparsed_attribute_member_function(node, source)
10183        || cpp_reparsed_friend_function_is_indexable(node, source)
10184        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
10185        || cpp_reparsed_access_template_function_is_indexable(node, source)
10186        || cpp_recovered_template_macro_constructor(node, source).is_some()
10187}
10188
10189/// Recognize the three top-level nodes produced when an unknown attribute
10190/// macro separates an inline member's declarator from its body in a reparsed
10191/// class interior: an errorful declaration with a missing semicolon, the macro
10192/// call expression, and the complete compound body. Their adjacency and exact
10193/// structured shapes prove one recoverable member envelope; arbitrary calls or
10194/// blocks do not pass this gate.
10195fn cpp_reparsed_macro_attribute_member_sequence(
10196    children: &[Node<'_>],
10197    index: usize,
10198    source: &str,
10199) -> bool {
10200    let Some(prefix) = children.get(index).copied() else {
10201        return false;
10202    };
10203    let declaration = if prefix.kind() == "labeled_statement" {
10204        prefix
10205            .named_child(prefix.named_child_count().saturating_sub(1))
10206            .filter(|child| child.kind() == "declaration")
10207    } else {
10208        (prefix.kind() == "declaration").then_some(prefix)
10209    };
10210    let Some(declaration) = declaration else {
10211        return false;
10212    };
10213    if !declaration.has_error()
10214        || declaration
10215            .child_by_field_name("declarator")
10216            .and_then(extract_function_declarator)
10217            .and_then(cpp_function_declarator_name_node)
10218            .is_none()
10219    {
10220        return false;
10221    }
10222    let Some(attribute_statement) = children.get(index + 1).copied() else {
10223        return false;
10224    };
10225    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
10226        .then(|| attribute_statement.named_child(0))
10227        .flatten()
10228        .filter(|child| child.kind() == "call_expression")
10229    else {
10230        return false;
10231    };
10232    let Some(attribute_name) = attribute_call
10233        .child_by_field_name("function")
10234        .filter(|function| function.kind() == "identifier")
10235        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
10236    else {
10237        return false;
10238    };
10239    if !cpp_export_macro_token(&attribute_name) {
10240        return false;
10241    }
10242    let Some(body) = children.get(index + 2).copied() else {
10243        return false;
10244    };
10245    body.kind() == "compound_statement"
10246        && body.child(0).is_some_and(|open| open.kind() == "{")
10247        && body
10248            .child(body.child_count().saturating_sub(1))
10249            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
10250        && declaration.end_byte() <= attribute_statement.start_byte()
10251        && attribute_statement.end_byte() <= body.start_byte()
10252}
10253
10254fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
10255    let mut cursor = root.walk();
10256    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
10257    let mut saw_member = false;
10258    let mut index = 0;
10259    while index < children.len() {
10260        let child = children[index];
10261        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
10262            saw_member = true;
10263            index += 3;
10264            continue;
10265        }
10266        if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
10267            let Some(tree) = cpp_reparse_fragmented_class_body(
10268                source,
10269                fragmented.reparse_start,
10270                fragmented.reparse_end,
10271            ) else {
10272                return false;
10273            };
10274            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
10275                return false;
10276            }
10277            saw_member = true;
10278            index += 1;
10279            while index < children.len()
10280                && children[index].end_byte() <= fragmented.class_range.end_byte
10281            {
10282                index += 1;
10283            }
10284            continue;
10285        }
10286        match child.kind() {
10287            "comment" => {}
10288            "labeled_statement" => saw_member = true,
10289            "function_definition" => {
10290                if child.has_error()
10291                    && !cpp_reparsed_member_function_is_indexable(child, source)
10292                    && cpp_sentinel_macro_region(child, source).is_none()
10293                {
10294                    return false;
10295                }
10296                saw_member = true;
10297            }
10298            "ERROR"
10299                if (cpp_reparsed_member_error_is_indexable(child)
10300                    || cpp_reparsed_adjacent_copy_control_error(child, source))
10301                    && (child
10302                        .next_named_sibling()
10303                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
10304                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
10305            {
10306                saw_member = true;
10307            }
10308            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
10309                saw_member = true;
10310            }
10311            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
10312                saw_member = true;
10313            }
10314            "expression_statement"
10315                if cpp_is_stray_semicolon(child, source)
10316                    && child.prev_named_sibling().is_some_and(|error| {
10317                        cpp_reparsed_member_error_is_indexable(error)
10318                            || cpp_reparsed_adjacent_copy_control_error(error, source)
10319                    }) =>
10320            {
10321                saw_member = true;
10322            }
10323            "compound_statement"
10324                if cpp_reparsed_constructor_body_is_indexable(child, source)
10325                    || cpp_reparsed_attribute_requires_body(child, source) =>
10326            {
10327                saw_member = true;
10328            }
10329            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
10330            _ => return false,
10331        }
10332        index += 1;
10333    }
10334    saw_member
10335}
10336
10337/// Detect the malformed constructor shape that tree-sitter exposes as an
10338/// access-label statement followed by initializer-looking declarations. The
10339/// declarations are not class members: visiting their `location(loc)` and
10340/// `string(s)` function declarators would publish synthetic functions. The
10341/// export-class fallback keeps the original sibling nodes and therefore avoids
10342/// this parser artifact. The returned range identifies the real constructor
10343/// header, which can be reparsed independently as a structured declarator.
10344fn cpp_reparsed_synthetic_initializer_constructor_range(
10345    root: Node<'_>,
10346    class_name: &str,
10347    source: &str,
10348    constructor_end: usize,
10349) -> Option<std::ops::Range<usize>> {
10350    let mut stack = {
10351        let mut cursor = root.walk();
10352        root.named_children(&mut cursor).collect::<Vec<_>>()
10353    };
10354    while let Some(current) = stack.pop() {
10355        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
10356            current,
10357            class_name,
10358            source,
10359            constructor_end,
10360        ) {
10361            return Some(range);
10362        }
10363        if current.kind() == "ERROR" {
10364            let mut cursor = current.walk();
10365            stack.extend(current.named_children(&mut cursor));
10366        }
10367    }
10368    None
10369}
10370
10371fn cpp_reparsed_synthetic_initializer_constructor(
10372    node: Node<'_>,
10373    class_name: &str,
10374    source: &str,
10375    constructor_end: usize,
10376) -> Option<std::ops::Range<usize>> {
10377    if node.kind() != "labeled_statement" {
10378        return None;
10379    }
10380    let mut cursor = node.walk();
10381    let named = node
10382        .named_children(&mut cursor)
10383        .filter(|child| child.kind() != "comment")
10384        .collect::<Vec<_>>();
10385    let label = named.first()?;
10386    if label.kind() != "statement_identifier"
10387        || !matches!(
10388            node_text(*label, source).trim(),
10389            "public" | "private" | "protected"
10390        )
10391    {
10392        return None;
10393    }
10394    let call_error_index = named.iter().position(|child| {
10395        if child.kind() != "ERROR" {
10396            return false;
10397        }
10398        let mut stack = vec![*child];
10399        while let Some(current) = stack.pop() {
10400            if current.kind() == "call_expression"
10401                && current
10402                    .child_by_field_name("function")
10403                    .is_some_and(|function| {
10404                        function.kind() == "identifier"
10405                            && node_text(function, source).trim() == class_name
10406                    })
10407            {
10408                return true;
10409            }
10410            let mut cursor = current.walk();
10411            stack.extend(current.named_children(&mut cursor));
10412        }
10413        false
10414    })?;
10415    let constructor_call = {
10416        let mut stack = vec![named[call_error_index]];
10417        let mut found = None;
10418        while let Some(current) = stack.pop() {
10419            if current.kind() == "call_expression"
10420                && current
10421                    .child_by_field_name("function")
10422                    .is_some_and(|function| {
10423                        function.kind() == "identifier"
10424                            && node_text(function, source).trim() == class_name
10425                    })
10426            {
10427                found = Some(current);
10428                break;
10429            }
10430            let mut cursor = current.walk();
10431            stack.extend(current.named_children(&mut cursor));
10432        }
10433        found
10434    };
10435    let constructor_call = constructor_call?;
10436    named.iter().skip(call_error_index + 1).find(|child| {
10437        child.kind() == "declaration" && child.has_error() && {
10438            let mut cursor = child.walk();
10439            child.named_children(&mut cursor).any(|declarator| {
10440                declarator.kind() == "init_declarator"
10441                    && declarator
10442                        .child_by_field_name("declarator")
10443                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
10444                    && declarator
10445                        .child_by_field_name("value")
10446                        .is_some_and(|value| value.kind() == "initializer_list")
10447            })
10448        }
10449    })?;
10450    Some(constructor_call.start_byte()..constructor_end)
10451}
10452
10453fn cpp_reparsed_exact_constructor_declarator<'tree>(
10454    root: Node<'tree>,
10455    start: usize,
10456    class_name: &str,
10457    source: &str,
10458) -> Option<Node<'tree>> {
10459    let mut candidate = None;
10460    let mut stack = vec![root];
10461    while let Some(current) = stack.pop() {
10462        if current.kind() == "function_declarator"
10463            && current.start_byte() == start
10464            && cpp_function_declarator_name_node(current)
10465                .is_some_and(|name| node_text(name, source).trim() == class_name)
10466        {
10467            if candidate.is_some() {
10468                return None;
10469            }
10470            candidate = Some(current);
10471            continue;
10472        }
10473        let mut cursor = current.walk();
10474        stack.extend(current.named_children(&mut cursor));
10475    }
10476    candidate
10477}
10478
10479fn cpp_is_indexable_item_kind(kind: &str) -> bool {
10480    matches!(
10481        kind,
10482        "namespace_definition"
10483            | "class_specifier"
10484            | "struct_specifier"
10485            | "union_specifier"
10486            | "enum_specifier"
10487            | "function_definition"
10488            | "template_declaration"
10489            | "declaration"
10490            | "field_declaration"
10491            | "alias_declaration"
10492            | "static_assert_declaration"
10493            | "type_definition"
10494            | "using_declaration"
10495            | "linkage_specification"
10496            | "preproc_def"
10497            | "preproc_function_def"
10498            | "preproc_include"
10499            | "preproc_if"
10500            | "preproc_ifdef"
10501            | "preproc_call"
10502    )
10503}
10504
10505#[cfg(test)]
10506mod tests {
10507    use super::*;
10508    use crate::adapter::parse_cpp_file;
10509    use brokk_bifrost_core::analyzer::parsed_file::{
10510        finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
10511    };
10512    use std::fmt::Write;
10513
10514    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
10515        let mut parser = tree_sitter::Parser::new();
10516        parser
10517            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10518            .unwrap();
10519        let tree = parser.parse(source, None).unwrap();
10520        let file = ProjectFile::new(std::env::temp_dir(), name);
10521        parse_cpp_file(&file, source, &tree)
10522    }
10523
10524    #[test]
10525    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
10526        let source = r#"namespace control {
10527template <typename T>
10528class AnySpan;
10529template <typename T>
10530class ABSL_ATTRIBUTE_VIEW AnySpan {
10531 public:
10532  int begin() const;
10533};
10534}
10535
10536namespace absl {
10537ABSL_NAMESPACE_BEGIN
10538template <typename T>
10539class ABSL_ATTRIBUTE_VIEW Span {
10540 public:
10541  int begin() const;
10542  int back() const;
10543};
10544
10545int begin();
10546int back();
10547}
10548"#;
10549        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
10550        let declarations = parsed.declarations();
10551        assert!(
10552            declarations
10553                .iter()
10554                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
10555        );
10556        for method in ["begin", "back"] {
10557            assert!(declarations.iter().any(|unit| {
10558                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
10559            }));
10560            assert!(
10561                declarations.iter().any(|unit| {
10562                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
10563                })
10564            );
10565        }
10566        assert!(
10567            declarations
10568                .iter()
10569                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
10570        );
10571        assert!(
10572            declarations
10573                .iter()
10574                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
10575        );
10576        assert!(
10577            declarations
10578                .iter()
10579                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
10580        );
10581    }
10582
10583    #[test]
10584    fn explicit_global_member_definition_has_canonical_package_boundary() {
10585        let source = r#"
10586namespace arangodb::aql {
10587class ExecutionPlan {
10588 public:
10589  template<class... Args> Node* createNode(Args&&... args);
10590};
10591}
10592
10593template<class... Args>
10594Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
10595"#;
10596        let parsed = parse_cpp_declarations(source, "global-member.cpp");
10597
10598        assert!(parsed.declarations().iter().any(|unit| {
10599            unit.is_function()
10600                && unit.package_name() == "arangodb::aql"
10601                && unit.short_name() == "ExecutionPlan.createNode"
10602                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
10603        }));
10604    }
10605
10606    #[test]
10607    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
10608        let source = r#"
10609#ifndef TINYXML2_INCLUDED
10610#define TINYXML2_INCLUDED
10611namespace tinyxml2 {
10612class TINYXML2_LIB XMLUtil {
10613 public:
10614  static const char* SkipWhiteSpace(const char* p) {
10615    while (*p) {
10616      if (*p == ' ') {
10617        ++p;
10618      }
10619    }
10620    return p;
10621  }
10622  static bool StringEqual(const char* p, const char* q) {
10623    return p == q;
10624  }
10625  class TINYXML2_LIB Helper {
10626   public:
10627    void Touch();
10628  };
10629  static void ToStr(int value, char* buffer);
10630 private:
10631  static const char* writeBoolTrue;
10632};
10633
10634class TINYXML2_LIB XMLNode {
10635 public:
10636  virtual XMLNode* ShallowClone() const = 0;
10637  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
10638};
10639}
10640#endif
10641"#;
10642        let mut parser = tree_sitter::Parser::new();
10643        parser
10644            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10645            .unwrap();
10646        let tree = parser.parse(source, None).unwrap();
10647        let mut boundary_found = false;
10648        walk_named_tree_preorder(tree.root_node(), true, |node| {
10649            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
10650                && name == "XMLUtil"
10651            {
10652                boundary_found = fragmented_export_sibling_class_boundary(node, source)
10653                    .and_then(|boundary| {
10654                        recover_exported_class_function_definition(boundary, source)
10655                    })
10656                    .is_some_and(|(_, name, _)| name == "XMLNode");
10657            }
10658            WalkControl::Continue
10659        });
10660        assert!(
10661            boundary_found,
10662            "fixture must exercise the recovered sibling boundary"
10663        );
10664
10665        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
10666        assert!(
10667            parsed
10668                .declarations()
10669                .iter()
10670                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
10671            "{:#?}",
10672            parsed.declarations()
10673        );
10674        assert!(
10675            parsed
10676                .declarations()
10677                .iter()
10678                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
10679            "{:#?}",
10680            parsed.declarations()
10681        );
10682        assert!(parsed.declarations().iter().any(|unit| {
10683            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
10684        }));
10685        assert!(
10686            parsed
10687                .declarations()
10688                .iter()
10689                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
10690        );
10691        assert!(
10692            parsed
10693                .declarations()
10694                .iter()
10695                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
10696        );
10697    }
10698
10699    #[test]
10700    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
10701        // Clang's diagnostic suite intentionally contains this ill-formed
10702        // spelling. The analyzer must retain the parser's explicit-global AST
10703        // boundary instead of constructing `cwg311::::cwg311::X`.
10704        let parsed = parse_cpp_declarations(
10705            r#"
10706namespace cwg311 {
10707namespace X { namespace Y {} }
10708namespace ::cwg311::X {}
10709}
10710"#,
10711            "explicit-global-namespace.cpp",
10712        );
10713
10714        assert!(parsed.declarations().iter().any(|unit| {
10715            unit.kind() == CodeUnitType::Module
10716                && unit.short_name() == "cwg311::X"
10717                && unit.fq_name() == "cwg311::X"
10718        }));
10719        assert!(
10720            parsed
10721                .declarations()
10722                .iter()
10723                .all(|unit| !unit.short_name().contains("::::")),
10724            "recovered namespace names must not retain empty scope components: {:#?}",
10725            parsed.declarations()
10726        );
10727    }
10728
10729    #[test]
10730    fn repeated_scope_separator_does_not_create_empty_function_owner() {
10731        let scope = ScopeInfo {
10732            package_name: "X".to_string(),
10733            module: None,
10734            class_unit: None,
10735            template_signature: None,
10736            template_metadata: None,
10737            declarations_are_fields: false,
10738            recovered_specialization_member_scope: false,
10739            visible_using_namespaces: Vec::new(),
10740        };
10741
10742        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
10743
10744        assert_eq!(owner, None);
10745        assert_eq!(name, "doit");
10746        assert_eq!(package, "X");
10747    }
10748
10749    #[test]
10750    fn trailing_decltype_expression_is_not_a_function_declarator() {
10751        let source = r#"
10752namespace boost { namespace detail {
10753#if ! defined(BOOST_NO_SFINAE_EXPR) && \
10754    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
10755    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
10756#define BOOST_THREAD_PROVIDES_INVOKE
10757#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
10758template <class Fp, class A0, class ...Args>
10759inline auto
10760invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
10761       BOOST_THREAD_RV_REF(Args) ...args)
10762    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
10763{
10764    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
10765}
10766#endif
10767#endif
10768}}
10769"#;
10770        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
10771
10772        assert!(
10773            parsed
10774                .declarations()
10775                .iter()
10776                .all(|unit| unit.short_name() != ".*f")
10777        );
10778    }
10779
10780    fn find_class_named<'tree>(
10781        root: Node<'tree>,
10782        source: &str,
10783        expected_name: &str,
10784    ) -> Option<Node<'tree>> {
10785        let mut stack = vec![root];
10786        while let Some(node) = stack.pop() {
10787            if node.kind() == "class_specifier"
10788                && node
10789                    .child_by_field_name("name")
10790                    .is_some_and(|name| node_text(name, source) == expected_name)
10791            {
10792                return Some(node);
10793            }
10794            let mut cursor = node.walk();
10795            stack.extend(node.named_children(&mut cursor));
10796        }
10797        None
10798    }
10799
10800    #[test]
10801    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
10802        let source = r#"EXPORT void definition(struct Value value) {}
10803EXPORT void prototype(struct Value value);
10804"#;
10805        let mut parser = tree_sitter::Parser::new();
10806        parser
10807            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10808            .unwrap();
10809        let tree = parser.parse(source, None).unwrap();
10810        let root = tree.root_node();
10811        let mut cursor = root.walk();
10812        let callables = root
10813            .named_children(&mut cursor)
10814            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
10815            .collect::<Vec<_>>();
10816
10817        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
10818        for callable in callables {
10819            assert!(callable.has_error(), "fixture must exercise error recovery");
10820            assert!(
10821                cpp_sentinel_macro_parts(callable, source).is_none(),
10822                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
10823            );
10824        }
10825    }
10826
10827    #[test]
10828    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
10829        let source = r#"namespace absl {
10830ABSL_NAMESPACE_BEGIN
10831// Generate a floating-point variate conforming to a Beta distribution:
10832template <typename RealType = double>
10833class beta_distribution {
10834 public:
10835  using result_type = RealType;
10836
10837
10838  beta_distribution() : beta_distribution(1) {}
10839
10840  explicit beta_distribution(result_type alpha, result_type beta = 1)
10841      : param_(alpha, beta) {}
10842
10843  explicit beta_distribution(const param_type& p) : param_(p) {}
10844
10845  void reset() {}
10846
10847  // Generating functions
10848  template <typename URBG>
10849  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
10850    return (*this)(g, param_);
10851  }
10852
10853};
10854ABSL_NAMESPACE_END
10855}  // namespace absl
10856"#;
10857        let mut parser = tree_sitter::Parser::new();
10858        parser
10859            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10860            .unwrap();
10861        let tree = parser.parse(source, None).unwrap();
10862        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
10863        let body = namespace
10864            .child_by_field_name("body")
10865            .expect("fixture namespace body");
10866        let sentinel = body.named_child(0).expect("sentinel envelope");
10867        let callable = sentinel
10868            .child_by_field_name("declarator")
10869            .and_then(extract_function_declarator)
10870            .and_then(cpp_function_declarator_name_node)
10871            .expect("preserved callable name");
10872
10873        assert_eq!(sentinel.kind(), "function_definition");
10874        assert_eq!(callable.kind(), "operator_name");
10875        assert!(
10876            cpp_sentinel_macro_parts(sentinel, source).is_some(),
10877            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
10878        );
10879    }
10880
10881    #[test]
10882    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
10883        let source = r#"namespace absl {
10884ABSL_NAMESPACE_BEGIN
10885// absl::discrete_distribution
10886//
10887// A discrete distribution produces random integers i, where 0 <= i < n
10888template <typename IntType = int>
10889class discrete_distribution {
10890 public:
10891  using result_type = IntType;
10892  class param_type {
10893   public:
10894    param_type() { init(); }
10895    template <typename InputIterator>
10896    explicit param_type(InputIterator begin, InputIterator end)
10897        : p_(begin, end) {
10898      init();
10899    }
10900  };
10901  discrete_distribution() : param_() {}
10902  explicit discrete_distribution(const param_type& p) : param_(p) {}
10903};
10904ABSL_NAMESPACE_END
10905}  // namespace absl
10906"#;
10907        let mut parser = tree_sitter::Parser::new();
10908        parser
10909            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10910            .unwrap();
10911        let tree = parser.parse(source, None).unwrap();
10912        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
10913        let body = namespace
10914            .child_by_field_name("body")
10915            .expect("fixture namespace body");
10916        let sentinel = body.named_child(0).expect("sentinel envelope");
10917        let callable = sentinel
10918            .child_by_field_name("declarator")
10919            .and_then(extract_function_declarator)
10920            .and_then(cpp_function_declarator_name_node)
10921            .expect("preserved callable name");
10922
10923        assert_eq!(sentinel.kind(), "function_definition");
10924        assert_eq!(callable.kind(), "identifier");
10925        assert!(
10926            cpp_sentinel_macro_parts(sentinel, source).is_some(),
10927            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
10928        );
10929    }
10930
10931    #[test]
10932    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
10933        let source = r#"
10934#define CPPCHECKLIB
10935class Library {
10936    struct Container {
10937        CPPCHECKLIB static std::string toString(Yield yield);
10938        CPPCHECKLIB static std::string toString(Action action);
10939    };
10940};
10941"#;
10942        let mut parser = tree_sitter::Parser::new();
10943        parser
10944            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10945            .unwrap();
10946        let tree = parser.parse(source, None).unwrap();
10947        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
10948        let parsed = parse_cpp_file(&file, source, &tree);
10949        assert!(
10950            parsed
10951                .declarations()
10952                .iter()
10953                .all(|unit| unit.fq_name() != "Library$Container.std"),
10954            "the qualified return-type namespace must not become a field: {:#?}",
10955            parsed.declarations()
10956        );
10957        for expected in ["(Yield)", "(Action)"] {
10958            assert!(
10959                parsed.declarations().iter().any(|unit| {
10960                    unit.is_function()
10961                        && unit.fq_name() == "Library$Container.toString"
10962                        && unit.signature() == Some(expected)
10963                }),
10964                "recovered toString overload {expected} is missing: {:#?}",
10965                parsed.declarations()
10966            );
10967        }
10968    }
10969
10970    #[test]
10971    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
10972        let source = r#"
10973#define SIMPLECPP_LIB
10974namespace simplecpp {
10975using TokenString = std::string;
10976struct Location { int line{}; };
10977class SIMPLECPP_LIB Token {
10978  TokenString prefix;
10979  void prefix_method() {}
10980 public:
10981  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
10982      whitespaceahead(wsahead), location(loc), string(s)
10983      // The comment must not hide the constructor body from recovery.
10984      {
10985      flags();
10986  }
10987  TokenString string;
10988  bool whitespaceahead;
10989  Location location;
10990  Token *previous{};
10991 private:
10992  void flags() {
10993      whitespaceahead = true;
10994  }
10995};
10996}
10997"#;
10998        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
10999
11000        let location_fields = parsed
11001            .declarations()
11002            .iter()
11003            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
11004            .collect::<Vec<_>>();
11005        assert_eq!(
11006            location_fields.len(),
11007            1,
11008            "location should have one class-owned declaration: {:#?}",
11009            parsed.declarations()
11010        );
11011        assert!(
11012            location_fields[0].is_field(),
11013            "location has wrong kind: {:#?}",
11014            parsed.declarations()
11015        );
11016        assert!(
11017            parsed.declarations().iter().all(|unit| {
11018                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
11019            })
11020        );
11021        assert!(
11022            parsed.declarations().iter().all(|unit| {
11023                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
11024            })
11025        );
11026        assert!(
11027            parsed
11028                .declarations()
11029                .iter()
11030                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
11031        );
11032        assert!(
11033            parsed
11034                .declarations()
11035                .iter()
11036                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
11037            "the recovered class must retain its constructor: {:#?}",
11038            parsed.declarations()
11039        );
11040        assert!(
11041            parsed
11042                .declarations()
11043                .iter()
11044                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
11045        );
11046        assert!(parsed.declarations().iter().any(|unit| {
11047            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
11048        }));
11049        let constructor = parsed
11050            .declarations()
11051            .iter()
11052            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
11053            .expect("recovered constructor");
11054        let constructor_start = source.find("Token(const").expect("constructor start");
11055        let constructor_end = source
11056            .get(
11057                ..source
11058                    .find("  TokenString string;")
11059                    .expect("constructor end"),
11060            )
11061            .expect("constructor slice")
11062            .trim_end()
11063            .len();
11064        assert!(
11065            parsed
11066                .navigation_ranges
11067                .get(constructor)
11068                .is_some_and(|ranges| {
11069                    ranges.iter().any(|range| {
11070                        range.start_byte == constructor_start && range.end_byte == constructor_end
11071                    })
11072                }),
11073            "constructor navigation must span the full body: {:#?}",
11074            parsed.navigation_ranges
11075        );
11076        assert_eq!(
11077            parsed
11078                .signature_metadata
11079                .get(constructor)
11080                .and_then(|metadata| metadata.first())
11081                .and_then(SignatureMetadata::callable_linkage),
11082            Some(CallableLinkage::External)
11083        );
11084        let token_class = parsed
11085            .declarations()
11086            .iter()
11087            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
11088            .expect("recovered Token class");
11089        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
11090        assert!(
11091            parsed
11092                .navigation_ranges
11093                .get(token_class)
11094                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
11095            "class navigation must include the terminating semicolon: {:#?}",
11096            parsed.navigation_ranges
11097        );
11098    }
11099
11100    #[test]
11101    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
11102        let source = r#"
11103#define SIMPLECPP_LIB
11104namespace simplecpp {
11105using TokenString = std::string;
11106class Macro;
11107struct Location {
11108  unsigned int fileIndex{};
11109  unsigned int line{};
11110  unsigned int col{};
11111};
11112struct Output {
11113  int type;
11114};
11115class SIMPLECPP_LIB Token {
11116 public:
11117  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
11118      whitespaceahead(wsahead), location(loc), string(s) {
11119      flags();
11120  }
11121  Token(const Token &tok) :
11122      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
11123      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
11124      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
11125  Token &operator=(const Token &tok) = delete;
11126  const TokenString& str() const { return string; }
11127  void setstr(const std::string &s) { string = s; flags(); }
11128  bool isOneOf(const char ops[]) const;
11129  TokenString macro;
11130  char op;
11131  bool comment;
11132  bool name;
11133  bool number;
11134  bool whitespaceahead;
11135  Location location;
11136  Token *previous{};
11137  Token *next{};
11138 private:
11139  void flags() {
11140      name = !string.empty();
11141      comment = false;
11142      number = false;
11143      op = 0;
11144  }
11145  TokenString string;
11146};
11147}
11148struct Following {
11149  int type;
11150};
11151class SIMPLECPP_LIB Later {
11152 public:
11153  Later(int value) : value(value) {}
11154  int value;
11155};
11156"#;
11157        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
11158        assert!(
11159            parsed
11160                .declarations()
11161                .iter()
11162                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
11163        );
11164        assert!(
11165            !parsed
11166                .declarations()
11167                .iter()
11168                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
11169        );
11170        assert!(
11171            parsed
11172                .declarations()
11173                .iter()
11174                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
11175        );
11176        assert!(
11177            !parsed
11178                .declarations()
11179                .iter()
11180                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
11181        );
11182        assert!(
11183            parsed
11184                .declarations()
11185                .iter()
11186                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
11187        );
11188        assert!(
11189            parsed
11190                .declarations()
11191                .iter()
11192                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
11193        );
11194        assert!(
11195            parsed
11196                .declarations()
11197                .iter()
11198                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
11199        );
11200        assert!(
11201            parsed
11202                .declarations()
11203                .iter()
11204                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
11205        );
11206        assert!(
11207            parsed
11208                .declarations()
11209                .iter()
11210                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
11211        );
11212        assert!(
11213            parsed
11214                .declarations()
11215                .iter()
11216                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
11217        );
11218        assert!(parsed.declarations().iter().all(|unit| {
11219            !matches!(
11220                unit.fq_name().as_str(),
11221                "simplecpp.Token.Following" | "simplecpp.Token.Later"
11222            )
11223        }));
11224        assert!(
11225            !parsed
11226                .declarations()
11227                .iter()
11228                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
11229            "the following struct must remain outside the recovered Token class"
11230        );
11231    }
11232
11233    #[test]
11234    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
11235        let source = r#"
11236#define SIMPLECPP_LIB
11237namespace {
11238namespace simplecpp {
11239using TokenString = std::string;
11240struct Location { int line{}; };
11241class SIMPLECPP_LIB HiddenToken {
11242 public:
11243  HiddenToken(const TokenString &s, const Location &loc) :
11244      location(loc), string(s) {
11245      flags();
11246  }
11247  TokenString string;
11248  Location location;
11249  HiddenToken *previous{};
11250 private:
11251  void flags() {}
11252};
11253}
11254}
11255"#;
11256        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
11257        let constructor = parsed
11258            .declarations()
11259            .iter()
11260            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
11261            .expect("recovered anonymous-namespace constructor");
11262        assert_eq!(
11263            parsed
11264                .signature_metadata
11265                .get(constructor)
11266                .and_then(|metadata| metadata.first())
11267                .and_then(SignatureMetadata::callable_linkage),
11268            Some(CallableLinkage::Internal)
11269        );
11270    }
11271
11272    #[test]
11273    fn macro_qualified_static_field_keeps_real_declarator() {
11274        let source = r#"#define JSON_INLINE_VARIABLE
11275struct Reader {
11276static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
11277static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
11278static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
11279};"#;
11280        let mut parser = tree_sitter::Parser::new();
11281        parser
11282            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11283            .unwrap();
11284        let tree = parser.parse(source, None).unwrap();
11285        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
11286        let parsed = parse_cpp_file(&file, source, &tree);
11287        for expected in [
11288            "Reader.npos",
11289            "Reader.other",
11290            "Reader.pointer",
11291            "Reader.reference",
11292        ] {
11293            assert!(
11294                parsed
11295                    .declarations()
11296                    .iter()
11297                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
11298                "real macro-decorated field {expected} is missing: {:#?}",
11299                parsed.declarations()
11300            );
11301        }
11302        assert!(
11303            parsed
11304                .declarations()
11305                .iter()
11306                .all(|unit| unit.fq_name() != "Reader.std"),
11307            "qualified type prefix became a pseudo-field: {:#?}",
11308            parsed.declarations()
11309        );
11310        let root = tree.root_node();
11311        let mut stack = vec![root];
11312        let mut signatures = Vec::new();
11313        while let Some(current) = stack.pop() {
11314            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
11315            {
11316                signatures.extend(
11317                    declarators
11318                        .into_iter()
11319                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
11320                );
11321            }
11322            let mut cursor = current.walk();
11323            stack.extend(current.named_children(&mut cursor));
11324        }
11325        signatures.sort();
11326        assert_eq!(
11327            signatures,
11328            [
11329                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
11330                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
11331                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
11332                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
11333            ]
11334        );
11335    }
11336
11337    fn member_function_linkage(source: &str) -> CallableLinkage {
11338        let mut parser = tree_sitter::Parser::new();
11339        parser
11340            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11341            .unwrap();
11342        let tree = parser.parse(source, None).unwrap();
11343        let mut stack = vec![tree.root_node()];
11344        while let Some(node) = stack.pop() {
11345            if node.kind() == "function_definition" {
11346                let mut current = node.parent();
11347                while let Some(parent) = current {
11348                    if matches!(
11349                        parent.kind(),
11350                        "class_specifier" | "struct_specifier" | "union_specifier"
11351                    ) {
11352                        return cpp_callable_linkage(node, source);
11353                    }
11354                    current = parent.parent();
11355                }
11356            }
11357            let mut cursor = node.walk();
11358            stack.extend(node.named_children(&mut cursor));
11359        }
11360        panic!("fixture has no member function definition");
11361    }
11362
11363    #[test]
11364    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
11365        assert_eq!(
11366            member_function_linkage("struct Named { int method() { return 1; } };"),
11367            CallableLinkage::External
11368        );
11369        assert_eq!(
11370            member_function_linkage(
11371                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
11372            ),
11373            CallableLinkage::Internal
11374        );
11375        assert_eq!(
11376            member_function_linkage("struct { int method() { return 1; } } instance;"),
11377            CallableLinkage::Internal
11378        );
11379        assert_eq!(
11380            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
11381            CallableLinkage::Internal
11382        );
11383    }
11384
11385    #[test]
11386    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
11387        let source = r#"
11388#ifndef PROTON_VALUE_HPP
11389#define PROTON_VALUE_HPP
11390namespace proton {
11391namespace internal {
11392class value_base {
11393  protected:
11394    internal::data& data();
11395    internal::data data_;
11396  friend class codec::encoder;
11397  friend class codec::decoder;
11398};
11399}
11400class value : public internal::value_base, private internal::comparable<value> {
11401  private:
11402    template<class T, class U=void> struct assignable :
11403        public std::enable_if<codec::is_encodable<T>::value, U> {};
11404    template<class U> struct assignable<value, U> {};
11405  public:
11406    PN_CPP_EXTERN value();
11407    PN_CPP_EXTERN value(const value&);
11408    PN_CPP_EXTERN value& operator=(const value&);
11409    PN_CPP_EXTERN value(value&&);
11410    PN_CPP_EXTERN value& operator=(value&&);
11411    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
11412    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
11413        codec::encoder e(*this);
11414        e << x;
11415        return *this;
11416    }
11417    PN_CPP_EXTERN type_id type() const;
11418    PN_CPP_EXTERN bool empty() const;
11419    PN_CPP_EXTERN void clear();
11420    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
11421    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
11422  friend PN_CPP_EXTERN void swap(value&, value&);
11423  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
11424  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
11425  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
11426    value(pn_data_t* d);
11427    void reset(pn_data_t* d = 0);
11428};
11429}
11430#endif
11431"#;
11432        let mut parser = tree_sitter::Parser::new();
11433        parser
11434            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11435            .unwrap();
11436        let tree = parser.parse(source, None).unwrap();
11437        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
11438        let parsed = parse_cpp_file(&file, source, &tree);
11439        let macro_constructors = parsed
11440            .signature_metadata
11441            .iter()
11442            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
11443            .flat_map(|(_, metadata)| metadata)
11444            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
11445            .collect::<Vec<_>>();
11446
11447        assert_eq!(
11448            macro_constructors.len(),
11449            3,
11450            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
11451            parsed.declarations()
11452        );
11453        assert!(
11454            macro_constructors.iter().all(|metadata| {
11455                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
11456            }),
11457            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
11458        );
11459    }
11460
11461    #[test]
11462    fn recovered_export_class_typedef_uses_displaced_alias_name() {
11463        let source = r#"
11464namespace spi {
11465class Filter {
11466public:
11467    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
11468};
11469}
11470namespace filter {
11471class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
11472{
11473public:
11474    typedef spi::Filter BASE_CLASS;
11475    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
11476    BEGIN_LOG4CXX_CAST_MAP()
11477    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
11478    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
11479    END_LOG4CXX_CAST_MAP()
11480    FilterDecision decide() const;
11481};
11482}
11483"#;
11484        let mut parser = tree_sitter::Parser::new();
11485        parser
11486            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11487            .unwrap();
11488        let tree = parser.parse(source, None).unwrap();
11489        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
11490        let parsed = parse_cpp_file(&file, source, &tree);
11491        assert!(
11492            parsed.declarations().iter().any(|unit| {
11493                unit.is_class()
11494                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
11495                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
11496            }),
11497            "the displaced typedef alias must retain its declared name: {:#?}",
11498            parsed.declarations()
11499        );
11500        assert!(
11501            parsed
11502                .declarations()
11503                .iter()
11504                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
11505            "the qualified underlying type must not become a false nested alias: {:#?}",
11506            parsed.declarations()
11507        );
11508    }
11509
11510    #[test]
11511    fn exported_single_base_recovery_uses_displaced_class_name() {
11512        let source = r#"
11513class CORE_EXPORT QgsPoint : public AbstractGeometry
11514{
11515    Q_GADGET
11516
11517    Q_PROPERTY( double x READ x WRITE setX )
11518    Q_PROPERTY( double y READ y WRITE setY )
11519    Q_PROPERTY( double z READ z WRITE setZ )
11520    Q_PROPERTY( double m READ m WRITE setM )
11521
11522  public:
11523#ifndef SIP_RUN
11524    QgsPoint(
11525      double x = std::numeric_limits<double>::quiet_NaN(),
11526      double y = std::numeric_limits<double>::quiet_NaN(),
11527      double z = std::numeric_limits<double>::quiet_NaN(),
11528      double m = std::numeric_limits<double>::quiet_NaN(),
11529      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
11530    );
11531#else
11532    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 )];
11533    % MethodCode
11534    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
11535    {
11536      int state;
11537      sipIsErr = 0;
11538      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
11539      if ( !sipIsErr )
11540      {
11541        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
11542      }
11543      sipReleaseType( p, sipType_QgsPointXY, state );
11544    }
11545    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
11546    {
11547      int state;
11548      sipIsErr = 0;
11549
11550      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
11551      if ( !sipIsErr )
11552      {
11553        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
11554      }
11555      sipReleaseType( p, sipType_QPointF, state );
11556    }
11557    else if (
11558      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
11559      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
11560      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
11561      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
11562    {
11563      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
11564      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
11565      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
11566      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
11567      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
11568      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
11569    }
11570    else // Invalid ctor arguments
11571    {
11572      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
11573      sipIsErr = 1;
11574    }
11575    % End
11576#endif
11577
11578    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
11579    explicit QgsPoint( QPointF p ) SIP_SKIP;
11580    explicit QgsPoint(
11581      Qgis::WkbType wkbType,
11582      double x = std::numeric_limits<double>::quiet_NaN(),
11583      double y = std::numeric_limits<double>::quiet_NaN(),
11584      double z = std::numeric_limits<double>::quiet_NaN(),
11585      double m = std::numeric_limits<double>::quiet_NaN()
11586    ) SIP_SKIP;
11587    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
11588    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
11589    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
11590#ifndef SIP_RUN
11591  private:
11592    bool fuzzyHelper(
11593      double epsilon,
11594      const AbstractGeometry &other,
11595      bool is3DFlag,
11596      bool isMeasureFlag
11597    ) const
11598    {
11599      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
11600    }
11601#endif
11602};
11603class Ordinary : public Base { public: Ordinary(); };
11604class API_EXPORT Plain { public: Plain(); };
11605class API_EXPORT : public Base {};
11606class
11607PN_CPP_CLASS_EXTERN Sender : public Link {
11608    Sender();
11609};
11610class thread_ctx_t {};
11611class ctx_t ZMQ_FINAL : public thread_ctx_t {
11612    bool start();
11613};
11614"#;
11615        let mut parser = tree_sitter::Parser::new();
11616        parser
11617            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11618            .unwrap();
11619        let tree = parser.parse(source, None).unwrap();
11620        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
11621        let parsed = parse_cpp_file(&file, source, &tree);
11622        let declarations = parsed.declarations();
11623
11624        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
11625            assert!(
11626                declarations
11627                    .iter()
11628                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
11629                "missing recovered class {expected}: {declarations:#?}"
11630            );
11631        }
11632        let qgs_point = declarations
11633            .iter()
11634            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
11635            .expect("recovered QgsPoint class");
11636        assert_eq!(
11637            parsed.raw_supertypes.get(qgs_point),
11638            Some(&vec!["AbstractGeometry".to_string()]),
11639            "single-base export recovery must retain its displaced base"
11640        );
11641        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
11642        assert!(
11643            parsed
11644                .navigation_ranges
11645                .get(qgs_point)
11646                .is_some_and(|ranges| {
11647                    !ranges.is_empty()
11648                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
11649                }),
11650            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
11651            parsed.navigation_ranges.get(qgs_point)
11652        );
11653        let sender = declarations
11654            .iter()
11655            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
11656            .expect("recovered Sender class");
11657        assert_eq!(
11658            parsed.raw_supertypes.get(sender),
11659            Some(&vec!["Link".to_string()]),
11660            "post-declarator export recovery must retain its displaced base"
11661        );
11662        let ctx = declarations
11663            .iter()
11664            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
11665            .expect("recovered ctx_t class");
11666        assert_eq!(
11667            parsed.raw_supertypes.get(ctx),
11668            Some(&vec!["thread_ctx_t".to_string()]),
11669            "postfix export-macro recovery must retain its displaced base"
11670        );
11671        assert!(
11672            declarations.iter().any(|unit| {
11673                unit.is_function()
11674                    && unit.fq_name() == "QgsPoint.QgsPoint"
11675                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
11676            }),
11677            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
11678        );
11679        assert!(
11680            declarations.iter().all(|unit| {
11681                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
11682            }),
11683            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
11684        );
11685    }
11686
11687    #[test]
11688    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
11689        let positive_source =
11690            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
11691        let mut parser = tree_sitter::Parser::new();
11692        parser
11693            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11694            .unwrap();
11695        let positive_tree = parser.parse(positive_source, None).unwrap();
11696        assert!(cpp_reparsed_members_are_indexable(
11697            positive_tree.root_node(),
11698            positive_source
11699        ));
11700
11701        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
11702        let negative_tree = parser.parse(negative_source, None).unwrap();
11703        assert!(!cpp_reparsed_members_are_indexable(
11704            negative_tree.root_node(),
11705            negative_source
11706        ));
11707    }
11708
11709    #[test]
11710    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
11711        let copy_control_source = r#"
11712public:
11713    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
11714    explicit Token(const Token* tok);
11715    ~Token();
11716    Token* astOperand1() { return nullptr; }
11717"#;
11718        let constraint_source = r#"
11719private:
11720    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
11721    static T *tokAtImpl(T *tok, int index) {
11722        return tok;
11723    }
11724
11725    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
11726    static T *linkAtImpl(T *tok, int index) {
11727        return tok;
11728    }
11729
11730public:
11731    int late() const { return 1; }
11732"#;
11733        let mut parser = tree_sitter::Parser::new();
11734        parser
11735            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11736            .unwrap();
11737        let copy_control_tree = parser
11738            .parse(copy_control_source, None)
11739            .expect("parse copy-control fixture");
11740        assert!(
11741            copy_control_tree.root_node().has_error(),
11742            "fixture must exercise adjacent copy-control recovery"
11743        );
11744        assert!(
11745            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
11746            "a complete late getter must remain recoverable after adjacent copy-control declarations"
11747        );
11748        let mut cursor = copy_control_tree.root_node().walk();
11749        assert!(
11750            copy_control_tree
11751                .root_node()
11752                .named_children(&mut cursor)
11753                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
11754            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
11755            copy_control_tree.root_node().to_sexp()
11756        );
11757        let constraint_tree = parser
11758            .parse(constraint_source, None)
11759            .expect("parse constraint-macro fixture");
11760        assert!(constraint_tree.root_node().has_error());
11761        assert!(
11762            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
11763            "complete constraint-macro members must not hide a later ordinary member"
11764        );
11765        let mut cursor = constraint_tree.root_node().walk();
11766        assert!(
11767            constraint_tree
11768                .root_node()
11769                .named_children(&mut cursor)
11770                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
11771                    child,
11772                    constraint_source
11773                )),
11774            "fixture must retain the split constraint-macro prefix/function geometry"
11775        );
11776    }
11777
11778    #[test]
11779    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
11780        let source = r#"
11781struct Analyzer {
11782    struct Action {
11783        Action() = default;
11784        Action(const Action&) = default;
11785        Action& operator=(const Action& rhs) & = default;
11786
11787        template<class T,
11788                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
11789                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
11790        // NOLINTNEXTLINE(google-explicit-constructor)
11791        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
11792        {}
11793
11794        enum : std::uint16_t { None = 0, Read = (1 << 0) };
11795        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
11796
11797    private:
11798        unsigned int mFlag{};
11799    };
11800
11801    enum class Direction : unsigned char { Forward, Reverse };
11802    virtual Action analyze(Direction d) const = 0;
11803};
11804"#;
11805        let mut parser = tree_sitter::Parser::new();
11806        parser
11807            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11808            .unwrap();
11809        let tree = parser.parse(source, None).unwrap();
11810        assert!(tree.root_node().has_error());
11811        let root = tree.root_node();
11812        let outer = root
11813            .named_children(&mut root.walk())
11814            .find(|child| child.kind() == "ERROR")
11815            .expect("fragmented Analyzer prefix");
11816        let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
11817            .expect("structured Analyzer fragment boundary");
11818        assert_eq!(outer_name, "Analyzer");
11819        let outer_tree = cpp_reparse_fragmented_class_body(
11820            source,
11821            outer_fragment.reparse_start,
11822            outer_fragment.reparse_end,
11823        )
11824        .expect("reparse Analyzer body");
11825        let outer_root = outer_tree.root_node();
11826        let action_prefix = outer_root
11827            .named_children(&mut outer_root.walk())
11828            .find(|child| child.kind() == "ERROR")
11829            .expect("fragmented Action prefix");
11830        let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
11831            .expect("structured Action fragment boundary");
11832        assert_eq!(action_name, "Action");
11833        let action_tree = cpp_reparse_fragmented_class_body(
11834            source,
11835            action_fragment.reparse_start,
11836            action_fragment.reparse_end,
11837        )
11838        .expect("reparse Action body");
11839        let action_root = action_tree.root_node();
11840        let macro_prefix = action_root
11841            .named_children(&mut action_root.walk())
11842            .find(|child| child.kind() == "ERROR")
11843            .expect("constraint macro prefix");
11844        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
11845            .expect("structured template macro prefix");
11846        let macro_companion =
11847            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
11848        assert!(
11849            cpp_reparsed_template_macro_constructor_companion_is_indexable(
11850                macro_companion,
11851                macro_parameter,
11852                source,
11853            ),
11854            "split constrained constructor must be admitted: {}",
11855            macro_companion.to_sexp()
11856        );
11857        assert!(
11858            cpp_reparsed_members_are_indexable(action_root, source),
11859            "complete Action body must pass the recovery gate: {}",
11860            action_tree.root_node().to_sexp()
11861        );
11862        assert!(
11863            cpp_reparsed_members_are_indexable(outer_root, source),
11864            "complete Analyzer body must pass the recovery gate: {}",
11865            outer_tree.root_node().to_sexp()
11866        );
11867        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
11868        let parsed = parse_cpp_file(&file, source, &tree);
11869        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
11870            assert!(
11871                parsed
11872                    .declarations()
11873                    .iter()
11874                    .any(|unit| unit.fq_name() == expected),
11875                "missing recovered declaration {expected}: {:#?}",
11876                parsed.declarations()
11877            );
11878        }
11879        assert!(
11880            parsed
11881                .declarations()
11882                .iter()
11883                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
11884            "nested members must not remain flattened: {:#?}",
11885            parsed.declarations()
11886        );
11887    }
11888
11889    #[test]
11890    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
11891        let source = r#"
11892raw_hash_set& operator=(raw_hash_set&& that) {
11893  return move_assign(
11894      std::move(that),
11895      typename AllocTraits::propagate_on_container_move_assignment());
11896}
11897
11898iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
11899  return {};
11900}
11901
11902void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
11903
11904iterator insert(const_iterator hint, value_type&& value)
11905    ABSL_ATTRIBUTE_LIFETIME_BOUND {
11906  return {};
11907}
11908
11909friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
11910  return left.size() == right.size();
11911}
11912
11913static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
11914  return static_cast<slot_type*>(buffer);
11915}
11916
11917protected:
11918// Included-range recovery can attach this comment to the template prefix.
11919template <class K>
11920void AssertOnFind([[maybe_unused]] const K& key) {
11921  Check(key);
11922}
11923"#;
11924        let mut parser = tree_sitter::Parser::new();
11925        parser
11926            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11927            .unwrap();
11928        let tree = parser.parse(source, None).unwrap();
11929        assert!(
11930            tree.root_node().has_error(),
11931            "the fixture must exercise tree-sitter's errorful member shapes"
11932        );
11933        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
11934
11935        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
11936        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
11937        assert!(!cpp_reparsed_members_are_indexable(
11938            incomplete_tree.root_node(),
11939            incomplete_source
11940        ));
11941
11942        let outside_error_source = "int foo() stray_attribute {}\n";
11943        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
11944        assert!(outside_error_tree.root_node().has_error());
11945        assert!(!cpp_reparsed_members_are_indexable(
11946            outside_error_tree.root_node(),
11947            outside_error_source
11948        ));
11949
11950        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
11951        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
11952        assert!(!cpp_reparsed_members_are_indexable(
11953            variable_initializer_tree.root_node(),
11954            variable_initializer_source
11955        ));
11956    }
11957
11958    #[test]
11959    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
11960        let positive_source = r#"
11961std::pair<iterator, bool> insert(init_type&& value)
11962    ABSL_ATTRIBUTE_LIFETIME_BOUND
11963#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
11964  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
11965#endif
11966{
11967  return emplace(std::move(value));
11968}
11969"#;
11970        let mut parser = tree_sitter::Parser::new();
11971        parser
11972            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11973            .unwrap();
11974        let positive_tree = parser.parse(positive_source, None).unwrap();
11975        assert!(
11976            positive_tree.root_node().has_error(),
11977            "the fixture must exercise the split attribute/requires shape"
11978        );
11979        assert!(cpp_reparsed_members_are_indexable(
11980            positive_tree.root_node(),
11981            positive_source
11982        ));
11983
11984        let template_return_source = r#"
11985pair<int> insert(init_type&& value)
11986    ABSL_ATTRIBUTE_LIFETIME_BOUND
11987#if LANGUAGE_LEVEL >= 202002L
11988  requires(!Predicate<init_type>::value)
11989#endif
11990// Attributes and the function body may be separated by comments.
11991{
11992  return {};
11993}
11994"#;
11995        let template_return_tree = parser.parse(template_return_source, None).unwrap();
11996        assert!(
11997            cpp_reparsed_members_are_indexable(
11998                template_return_tree.root_node(),
11999                template_return_source
12000            ),
12001            "template-return attribute/requires tree: {}",
12002            template_return_tree.root_node().to_sexp()
12003        );
12004
12005        let no_body_source = r#"
12006std::pair<iterator, bool> insert(init_type&& value)
12007    ABSL_ATTRIBUTE_LIFETIME_BOUND
12008#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
12009  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
12010#endif
12011+ 0;
12012"#;
12013        let no_body_tree = parser.parse(no_body_source, None).unwrap();
12014        assert!(!cpp_reparsed_members_are_indexable(
12015            no_body_tree.root_node(),
12016            no_body_source
12017        ));
12018
12019        let extra_payload_source = r#"
12020pair<int> insert(init_type&& value)
12021    ABSL_ATTRIBUTE_LIFETIME_BOUND
12022#if LANGUAGE_LEVEL >= 202002L
12023  int unrelated;
12024  requires(Predicate<init_type>::value)
12025#endif
12026{
12027  return {};
12028}
12029"#;
12030        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
12031        assert!(!cpp_reparsed_members_are_indexable(
12032            extra_payload_tree.root_node(),
12033            extra_payload_source
12034        ));
12035
12036        let variable_initializer_source = r#"
12037int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
12038#if LANGUAGE_LEVEL >= 202002L
12039  requires(true)
12040#endif
12041{
12042  bad;
12043}
12044"#;
12045        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
12046        assert!(!cpp_reparsed_members_are_indexable(
12047            variable_initializer_tree.root_node(),
12048            variable_initializer_source
12049        ));
12050    }
12051
12052    #[test]
12053    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
12054        let source = r#"namespace absl {
12055ABSL_NAMESPACE_BEGIN namespace container_internal {
12056
12057class raw_hash_set : public Base {
12058 public:
12059  using value_type = int;
12060
12061  template <class U,
12062            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
12063  void insert(U value) { (void)value; }
12064
12065  struct InsertSlot {
12066    raw_hash_set& s;
12067  };
12068};
12069
12070}
12071ABSL_NAMESPACE_END
12072}"#;
12073        let mut parser = tree_sitter::Parser::new();
12074        parser
12075            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12076            .unwrap();
12077        let tree = parser.parse(source, None).unwrap();
12078        let root = tree.root_node();
12079        let outer_namespace = root
12080            .named_children(&mut root.walk())
12081            .find(|child| child.kind() == "namespace_definition")
12082            .expect("outer absl namespace");
12083        let declaration_list = outer_namespace
12084            .child_by_field_name("body")
12085            .expect("outer namespace body");
12086        let sentinel_function = declaration_list
12087            .named_children(&mut declaration_list.walk())
12088            .find(|child| child.kind() == "function_definition")
12089            .expect("malformed namespace sentinel function");
12090        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source)
12091            .expect("structured nested namespace sentinel");
12092        let fragmented =
12093            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source)
12094                .expect("fragmented raw_hash_set class");
12095        assert_eq!(fragmented.class_node.kind(), "ERROR");
12096        assert_eq!(fragmented.name, "raw_hash_set");
12097        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
12098
12099        let outer_scope =
12100            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
12101        let mut outer_siblings = Vec::new();
12102        push_cpp_sentinel_sibling_classes(
12103            &mut outer_siblings,
12104            declaration_list,
12105            sentinel.function,
12106            &outer_scope,
12107            source,
12108        );
12109        let [outer_shadow] = outer_siblings.as_slice() else {
12110            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
12111        };
12112        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
12113        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
12114
12115        let field = "    raw_hash_set& s;";
12116        let start = source.find(field).expect("InsertSlot field") + 4;
12117        let node = root
12118            .descendant_for_byte_range(start, start + "raw_hash_set".len())
12119            .expect("raw_hash_set type node");
12120        let recovered = cpp_sentinel_recovered_classes(root, source);
12121        let [deep_class] = recovered.as_slice() else {
12122            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
12123        };
12124        assert_eq!(
12125            deep_class.namespace_scope_components,
12126            vec!["absl", "container_internal"]
12127        );
12128        assert_eq!(
12129            deep_class.scope_components,
12130            vec!["absl", "container_internal", "raw_hash_set"]
12131        );
12132        assert!(
12133            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
12134                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
12135        );
12136
12137        assert_eq!(
12138            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
12139            Some(vec![
12140                "absl".to_string(),
12141                "container_internal".to_string(),
12142                "raw_hash_set".to_string(),
12143                "InsertSlot".to_string(),
12144            ])
12145        );
12146
12147        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
12148        let parsed = parse_cpp_file(&file, source, &tree);
12149        let raw_hash_set = parsed
12150            .declarations()
12151            .iter()
12152            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
12153            .expect("recovered raw_hash_set class");
12154        assert_eq!(
12155            raw_hash_set.fq_name(),
12156            "absl::container_internal.raw_hash_set",
12157            "the recovered declaration must publish under the deeper sentinel namespace"
12158        );
12159        assert_eq!(
12160            parsed.raw_supertypes.get(raw_hash_set),
12161            Some(&vec!["Base".to_string()]),
12162            "the structured base clause on the fragmented ERROR prefix must survive publication"
12163        );
12164        assert!(
12165            parsed.materialization_records.iter().any(|record| matches!(
12166                record,
12167                MaterializationRecord::RecoveredDeclaration { recovery, unit }
12168                    if unit == raw_hash_set && *recovery == deep_class.class_range
12169            )),
12170            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
12171            parsed.materialization_records
12172        );
12173    }
12174
12175    #[test]
12176    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
12177        const DISTINCT_PER_KIND: usize = 64;
12178        let mut source = String::new();
12179        for index in 0..DISTINCT_PER_KIND {
12180            writeln!(source, "typedef int Alias{index};").unwrap();
12181        }
12182        writeln!(source, "typedef long Alias0;").unwrap();
12183        for index in 0..DISTINCT_PER_KIND {
12184            writeln!(source, "#define MACRO_{index} {index}").unwrap();
12185        }
12186        writeln!(source, "#define MACRO_0 duplicate").unwrap();
12187        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
12188
12189        let mut parser = tree_sitter::Parser::new();
12190        parser
12191            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12192            .unwrap();
12193        let tree = parser.parse(&source, None).unwrap();
12194        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
12195
12196        start_declaration_identity_comparison_probe();
12197        let parsed = parse_cpp_file(&file, &source, &tree);
12198        let comparisons = finish_declaration_identity_comparison_probe();
12199
12200        assert_eq!(
12201            DISTINCT_PER_KIND + 1,
12202            parsed
12203                .declarations()
12204                .iter()
12205                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
12206                .count(),
12207            "every physical typedef alias declaration must be retained so \
12208             conditional branch guards stay available to the resolver"
12209        );
12210        assert_eq!(
12211            DISTINCT_PER_KIND,
12212            parsed
12213                .declarations()
12214                .iter()
12215                .filter(|unit| {
12216                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
12217                })
12218                .count(),
12219            "macros should retain semantic-identity deduplication"
12220        );
12221        assert_eq!(
12222            2,
12223            parsed
12224                .declarations()
12225                .iter()
12226                .filter(|unit| {
12227                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
12228                })
12229                .count(),
12230            "function overloads must remain distinct"
12231        );
12232
12233        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
12234        assert!(
12235            comparisons <= dedup_inputs * 4,
12236            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
12237        );
12238    }
12239
12240    #[test]
12241    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
12242        let source = r#"namespace absl {
12243ABSL_NAMESPACE_BEGIN namespace container_internal {
12244template <typename T>
12245class broken {
12246 public:
12247  using value_type = T;
12248  T operator->() const { return &operator*(); }
12249  using alias = value_type;
12250};
12251}
12252}
12253"#;
12254        let mut parser = tree_sitter::Parser::new();
12255        parser
12256            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12257            .unwrap();
12258        let tree = parser.parse(source, None).unwrap();
12259        let broken = find_class_named(tree.root_node(), source, "broken")
12260            .expect("the positive fixture must expose the broken class node");
12261        assert!(
12262            broken.has_error(),
12263            "the positive fixture must retain an internal parser error"
12264        );
12265        assert!(
12266            cpp_complete_class_body_close(broken).is_some(),
12267            "the positive fixture must expose a real class body close"
12268        );
12269        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12270        assert!(
12271            recovered.iter().any(|class| {
12272                class.scope_components == ["absl", "container_internal", "broken"]
12273            }),
12274            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
12275        );
12276    }
12277
12278    #[test]
12279    fn sentinel_recovery_keeps_members_after_nested_body_close() {
12280        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
12281NLOHMANN_BASIC_JSON_TPL_DECLARATION
12282class basic_json {
12283 private:
12284  union storage {
12285    int value;
12286  } data;
12287 public:
12288  using late_alias = int;
12289  late_alias value() const;
12290};
12291NLOHMANN_JSON_NAMESPACE_END
12292"#;
12293        let mut parser = tree_sitter::Parser::new();
12294        parser
12295            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12296            .unwrap();
12297        let tree = parser.parse(source, None).unwrap();
12298        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12299        let basic_json = recovered
12300            .iter()
12301            .find(|class| {
12302                class
12303                    .scope_components
12304                    .last()
12305                    .is_some_and(|name| name == "basic_json")
12306            })
12307            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
12308        let late_alias = source
12309            .find("late_alias value")
12310            .expect("late alias reference");
12311        assert!(
12312            basic_json.class_range.start_byte < late_alias
12313                && late_alias < basic_json.class_range.end_byte,
12314            "the recovered class range must include members after a nested close: {basic_json:#?}"
12315        );
12316    }
12317
12318    #[test]
12319    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
12320        let source = r#"namespace absl {
12321ABSL_NAMESPACE_BEGIN namespace container_internal {
12322template <typename T>
12323class broken {
12324 public:
12325  using value_type = T;
12326  T operator->() const { return &operator*(); }
12327}
12328}
12329"#;
12330        let mut parser = tree_sitter::Parser::new();
12331        parser
12332            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12333            .unwrap();
12334        let tree = parser.parse(source, None).unwrap();
12335        let broken = find_class_named(tree.root_node(), source, "broken")
12336            .expect("the negative fixture must expose the malformed class node");
12337        assert!(
12338            broken.has_error(),
12339            "the negative fixture must retain a parser error"
12340        );
12341        assert!(
12342            cpp_complete_class_body_close(broken).is_none(),
12343            "the malformed class must not expose a real body close"
12344        );
12345        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12346        assert!(
12347            recovered
12348                .iter()
12349                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
12350            "an incomplete class must not borrow the namespace close: {recovered:#?}"
12351        );
12352    }
12353
12354    #[test]
12355    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
12356        let source = r#"namespace absl {
12357ABSL_NAMESPACE_BEGIN namespace container_internal {
12358template <typename T>
12359struct broken {
12360  using value_type = T;
12361};
12362}
12363
12364#ifdef OWNER_DEF
12365template <typename T>
12366typename broken<T>::value_type broken<T>::method() {
12367  value_type value{};
12368  return value;
12369}
12370#endif
12371
12372namespace sibling {
12373template <typename T>
12374typename broken<T>::value_type broken<T>::other() {
12375  value_type value{};
12376  return value;
12377}
12378}
12379
12380ABSL_NAMESPACE_END
12381}
12382"#;
12383        let mut parser = tree_sitter::Parser::new();
12384        parser
12385            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12386            .unwrap();
12387        let tree = parser.parse(source, None).unwrap();
12388        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12389        let broken = recovered
12390            .iter()
12391            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
12392            .expect("the sentinel class must be recovered");
12393        let method_start = source
12394            .find("typename broken<T>::value_type broken<T>::method()")
12395            .expect("guarded sibling owner");
12396        let method_end = source[method_start..]
12397            .find("\n}")
12398            .map(|offset| method_start + offset + 2)
12399            .expect("guarded sibling owner close");
12400        assert!(
12401            broken
12402                .owner_ranges
12403                .iter()
12404                .any(|owner| owner.range.start_byte <= method_start
12405                    && method_end <= owner.range.end_byte),
12406            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
12407        );
12408        let sibling_start = source
12409            .find("typename broken<T>::value_type broken<T>::other()")
12410            .expect("nested namespace sibling owner");
12411        assert!(
12412            broken
12413                .owner_ranges
12414                .iter()
12415                .all(|owner| owner.range.start_byte > sibling_start
12416                    || owner.range.end_byte <= sibling_start),
12417            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
12418        );
12419    }
12420
12421    #[test]
12422    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
12423        let source = r#"#ifdef OUTER
12424namespace absl {
12425ABSL_NAMESPACE_BEGIN namespace container_internal {
12426template <typename T>
12427struct broken {
12428  using value_type = T;
12429};
12430}
12431}
12432
12433#ifdef OWNER_DEF
12434template <typename T>
12435typename broken<T>::value_type broken<T>::method() {
12436  value_type value{};
12437  return value;
12438}
12439#endif
12440#endif
12441"#;
12442        let mut parser = tree_sitter::Parser::new();
12443        parser
12444            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12445            .unwrap();
12446        let tree = parser.parse(source, None).unwrap();
12447        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12448        let broken = recovered
12449            .iter()
12450            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
12451            .expect("the sentinel class must be recovered");
12452        let method_start = source
12453            .find("typename broken<T>::value_type broken<T>::method()")
12454            .expect("outer sibling owner");
12455        assert!(
12456            broken
12457                .owner_ranges
12458                .iter()
12459                .all(|owner| owner.range.start_byte > method_start
12460                    || owner.range.end_byte <= method_start),
12461            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
12462        );
12463    }
12464
12465    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
12466    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
12467        let mut signatures = parsed
12468            .declarations()
12469            .iter()
12470            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
12471            .filter_map(|unit| unit.signature().map(str::to_string))
12472            .collect::<Vec<_>>();
12473        signatures.sort();
12474        signatures.dedup();
12475        signatures
12476    }
12477
12478    #[test]
12479    fn trailing_qualifiers_survive_parameter_list_whitespace() {
12480        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
12481        // declarator's structure, so an out-of-line definition that spells its
12482        // parameter list with different whitespace than the declaration must
12483        // still carry it.
12484        let source = r#"
12485struct Widget {
12486  bool multiline(int settings, int supprs) const;
12487  bool doublespace(int settings, int supprs) const;
12488  bool noexcept_multiline(int settings, int supprs) noexcept;
12489  bool ref_multiline(int settings, int supprs) &&;
12490};
12491bool
12492Widget::multiline (int settings,
12493                   int supprs) const
12494{ return settings + supprs > 0; }
12495bool Widget::doublespace(int settings,  int supprs) const { return true; }
12496bool Widget::noexcept_multiline(int settings,
12497                                int supprs) noexcept { return true; }
12498bool Widget::ref_multiline(int settings,
12499                           int supprs) && { return true; }
12500"#;
12501        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
12502        assert_eq!(
12503            vec!["(int, int) const".to_string()],
12504            identity_signatures(&parsed, "Widget.multiline")
12505        );
12506        assert_eq!(
12507            vec!["(int, int) const".to_string()],
12508            identity_signatures(&parsed, "Widget.doublespace")
12509        );
12510        assert_eq!(
12511            vec!["(int, int) noexcept".to_string()],
12512            identity_signatures(&parsed, "Widget.noexcept_multiline")
12513        );
12514        assert_eq!(
12515            vec!["(int, int) &&".to_string()],
12516            identity_signatures(&parsed, "Widget.ref_multiline")
12517        );
12518    }
12519
12520    #[test]
12521    fn macro_fragmented_plain_class_keeps_following_member_signature() {
12522        let source = r#"
12523struct CString {};
12524class CMessage {
12525public:
12526  CString GetParams(unsigned int index, unsigned int length = -1) const
12527      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
12528    return GetParamsColon(index, length);
12529  }
12530  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
12531};
12532CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
12533  return {};
12534}
12535"#;
12536        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
12537        assert_eq!(
12538            vec!["(unsigned int, unsigned int) const".to_string()],
12539            identity_signatures(&parsed, "CMessage.GetParamsColon")
12540        );
12541    }
12542
12543    #[test]
12544    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
12545        let source = r#"
12546#pragma once
12547#define DEMO_DEPRECATED(message)
12548namespace demo {
12549struct Base {
12550    static int aligned(int value) { return value; }
12551    int legacy(int value) const
12552        DEMO_DEPRECATED("use replacement()") { return value; }
12553    int replacement() const;
12554    void run(int value);
12555};
12556struct OtherBase {
12557    void run(int value);
12558    static int aligned(int value) { return value; }
12559};
12560struct Derived : Base {};
12561struct Override : Base {
12562    void run(int value);
12563    static int aligned(int value) { return value; }
12564};
12565struct RecoveredOverride : Base {
12566    int legacy(int value) const
12567        DEMO_DEPRECATED("use replacement()") { return value; }
12568    void run(int value);
12569};
12570struct Hidden : Base {
12571    void run(int first, int second);
12572    static int aligned(int first, int second) { return first + second; }
12573};
12574struct Ambiguous : Base, OtherBase {};
12575}
12576struct Global {};
12577"#;
12578        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
12579        let declarations = parsed.declarations();
12580        let fq_names = declarations
12581            .iter()
12582            .map(|unit| unit.fq_name())
12583            .collect::<std::collections::BTreeSet<_>>();
12584
12585        for expected in [
12586            "demo.Base",
12587            "demo.Base.aligned",
12588            "demo.Base.legacy",
12589            "demo.Base.replacement",
12590            "demo.Base.run",
12591            "demo.Derived",
12592            "demo.OtherBase",
12593            "demo.Override",
12594            "demo.RecoveredOverride",
12595            "demo.Hidden",
12596            "demo.Ambiguous",
12597            "Global",
12598        ] {
12599            assert!(
12600                fq_names.contains(expected),
12601                "missing {expected} from namespaced macro fragment: {declarations:#?}"
12602            );
12603        }
12604        assert!(
12605            !fq_names.contains("Derived"),
12606            "following class escaped its namespace: {declarations:#?}"
12607        );
12608        assert!(
12609            !fq_names.contains("demo.Global"),
12610            "global class crossed the recovered namespace boundary: {declarations:#?}"
12611        );
12612    }
12613
12614    #[test]
12615    fn trailing_qualifiers_still_separate_genuine_overloads() {
12616        // The qualifier must keep distinguishing the real C++ overload sets it
12617        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
12618        let source = r#"
12619struct Widget {
12620  int* slot(int index);
12621  const int* slot(int index) const;
12622  int log(int severity) &;
12623  int log(int severity) &&;
12624};
12625"#;
12626        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
12627        assert_eq!(
12628            vec!["(int)".to_string(), "(int) const".to_string()],
12629            identity_signatures(&parsed, "Widget.slot")
12630        );
12631        assert_eq!(
12632            vec!["(int) &".to_string(), "(int) &&".to_string()],
12633            identity_signatures(&parsed, "Widget.log")
12634        );
12635    }
12636
12637    #[test]
12638    fn virtual_specifier_is_not_part_of_the_identity_signature() {
12639        // `override` never appears on the out-of-line definition, and C++ does
12640        // not make it part of the signature, so it must not split the identity.
12641        let source = r#"
12642struct Base {
12643  virtual void run(int value) const;
12644};
12645struct Widget : Base {
12646  void run(int value) const override;
12647};
12648void Widget::run(int value) const {}
12649"#;
12650        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
12651        assert_eq!(
12652            vec!["(int) const".to_string()],
12653            identity_signatures(&parsed, "Widget.run")
12654        );
12655    }
12656
12657    #[test]
12658    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
12659        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
12660        // the function type, so a declaration that spells `const int` and a
12661        // definition that spells `int` are one entity.
12662        let source = r#"
12663struct Widget {
12664  bool value_params(const int settings, const int supprs);
12665  void pointee_const(const int* p);
12666  void pointer_const(int* const p);
12667  void both_const(const int* const p);
12668  void reference_const(const int& p);
12669  void array_const(const int values[4]);
12670};
12671bool Widget::value_params(int settings, int supprs) { return true; }
12672void Widget::pointer_const(int* p) {}
12673void Widget::both_const(const int* p) {}
12674"#;
12675        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
12676        assert_eq!(
12677            vec!["(int, int)".to_string()],
12678            identity_signatures(&parsed, "Widget.value_params")
12679        );
12680        assert_eq!(
12681            vec!["(int *)".to_string()],
12682            identity_signatures(&parsed, "Widget.pointer_const")
12683        );
12684        assert_eq!(
12685            vec!["(const int *)".to_string()],
12686            identity_signatures(&parsed, "Widget.both_const")
12687        );
12688        // The const that is not top-level still distinguishes the type.
12689        assert_eq!(
12690            vec!["(const int *)".to_string()],
12691            identity_signatures(&parsed, "Widget.pointee_const")
12692        );
12693        assert_eq!(
12694            vec!["(const int &)".to_string()],
12695            identity_signatures(&parsed, "Widget.reference_const")
12696        );
12697        assert_eq!(
12698            vec!["(const int [4])".to_string()],
12699            identity_signatures(&parsed, "Widget.array_const")
12700        );
12701    }
12702
12703    #[test]
12704    fn top_level_parameter_const_still_separates_pointee_overloads() {
12705        let source = r#"
12706struct Widget {
12707  void take(const int* p);
12708  void take(int* p);
12709};
12710"#;
12711        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
12712        assert_eq!(
12713            vec!["(const int *)".to_string(), "(int *)".to_string()],
12714            identity_signatures(&parsed, "Widget.take")
12715        );
12716    }
12717}