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    parent: Node<'tree>,
230    next_index: usize,
231    /// Named-child index to stop before (`usize::MAX` = drain the parent).
232    /// Bounded ranges re-own a swallowed region's head and tail with
233    /// different scopes (issue #1524).
234    end_index: usize,
235    scope: ScopeInfo,
236}
237
238enum CppWork<'tree> {
239    Container(CppContainer<'tree>),
240    Node(CppNodeWork<'tree>),
241    Siblings(CppSiblingsWork<'tree>),
242}
243
244fn class_like_name(node: Node<'_>, source: &str) -> Option<String> {
245    let best = class_like_name_from_children(node, source);
246    if let Some(parent) = node.parent()
247        && matches!(
248            parent.kind(),
249            "declaration" | "field_declaration" | "function_definition"
250        )
251        && node
252            .child_by_field_name("name")
253            .map(|name_node| {
254                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
255            })
256            .unwrap_or(false)
257        && let Some(recovered) = exported_class_name_from_node(parent, source)
258        && best.as_deref() != Some(recovered.as_str())
259    {
260        return Some(recovered);
261    }
262    best.or_else(|| {
263        node.child_by_field_name("name")
264            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
265            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
266    })
267}
268
269fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
270    let mut grammar_name = None;
271    if let Some(name_node) = node.child_by_field_name("name") {
272        let name = normalize_cpp_whitespace(node_text(name_node, source));
273        if name.is_empty() {
274            return None;
275        }
276        if !cpp_export_macro_token(&name) {
277            return Some(name);
278        }
279        grammar_name = Some(name);
280    }
281
282    let mut best = None;
283    let mut cursor = node.walk();
284    let mut stack = Vec::new();
285    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
286        if matches!(
287            child.kind(),
288            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
289        ) {
290            break;
291        }
292        stack.push(child);
293    }
294
295    while let Some(current) = stack.pop() {
296        if matches!(current.kind(), "type_identifier" | "identifier") {
297            let name = normalize_cpp_whitespace(node_text(current, source));
298            if !name.is_empty() && !cpp_export_macro_token(&name) {
299                best = Some(name);
300            }
301            continue;
302        }
303
304        for index in (0..current.named_child_count()).rev() {
305            if let Some(child) = current.named_child(index) {
306                stack.push(child);
307            }
308        }
309    }
310    best.or(grammar_name)
311}
312
313pub fn cpp_export_macro_token(token: &str) -> bool {
314    token
315        .chars()
316        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
317}
318
319struct RecoveredExportedClass<'tree> {
320    declaration_node: Node<'tree>,
321    name: String,
322    body: Option<Node<'tree>>,
323    raw_supertypes: Option<Vec<String>>,
324    uses_initializer_body: bool,
325    /// Present only for the fragmented multiple-base export shape (issue #938).
326    /// Carries the true class-body byte region -- the members tree-sitter scattered
327    /// out of the recovered node -- so they can be reparsed and re-owned as members
328    /// rather than lost inside the truncated `initializer_list` stand-in.
329    fragmented_body: Option<FragmentedExportBody>,
330}
331
332/// The recovered class-body geometry for a fragmented multiple-base export class.
333/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
334/// verbatim for a region reparse (issue #941 machinery) so every recovered member
335/// keeps its exact original byte/line position. `class_range` is the full class
336/// navigation range spanning to the displaced closing brace.
337struct FragmentedExportBody {
338    reparse_start: usize,
339    reparse_end: usize,
340    class_range: Range,
341}
342
343/// Result of validating a reparsed fragmented class body.  A complete tree can
344/// safely consume the whole region.  A partial tree may contain only the exact
345/// class-named constructor that tree-sitter merged into an access label; its
346/// remaining siblings must stay on the ordinary outer walk.
347enum FragmentedExportMembers {
348    Complete(Tree),
349    ConditionalConstructor(Tree),
350}
351
352#[derive(Clone, Copy)]
353struct DisplacedMacroClassTail {
354    split_index: usize,
355    class_range: Range,
356}
357
358fn recover_exported_class_declaration<'tree>(
359    node: Node<'tree>,
360    source: &str,
361) -> Option<RecoveredExportedClass<'tree>> {
362    if let Some(recovered) = recover_malformed_exported_multiple_base_class(node, source) {
363        return Some(recovered);
364    }
365
366    let class_node = first_class_like_child(node)?;
367    if let Some(name_node) = class_node.child_by_field_name("name") {
368        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
369        if cpp_export_macro_token(&class_name) {
370            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
371            // Name declarator. Only a bare declarator can be the displaced class name;
372            // wrappers describe an object whose type merely happens to look macro-like.
373            let mut cursor = node.walk();
374            if node
375                .children_by_field_name("declarator", &mut cursor)
376                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
377            {
378                return None;
379            }
380        } else if has_direct_cpp_declarator(node) {
381            return None;
382        }
383    }
384    let name = exported_class_name_from_node(class_node, source)?;
385    Some(RecoveredExportedClass {
386        declaration_node: class_node,
387        name,
388        body: cpp_body_node(class_node),
389        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
390            .then(|| extract_cpp_supertypes(class_node, source)),
391        uses_initializer_body: false,
392        fragmented_body: None,
393    })
394}
395
396fn recover_malformed_exported_multiple_base_class<'tree>(
397    node: Node<'tree>,
398    source: &str,
399) -> Option<RecoveredExportedClass<'tree>> {
400    if node.kind() != "declaration" {
401        return None;
402    }
403    let class_node = node.child_by_field_name("type")?;
404    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
405        return None;
406    }
407    let macro_name = class_node
408        .child_by_field_name("name")
409        .and_then(|name| direct_identifier_name(name, source))?;
410    if !cpp_export_macro_token(&macro_name) {
411        return None;
412    }
413
414    let mut named_cursor = node.walk();
415    let mut named = node.named_children(&mut named_cursor);
416    if named
417        .next()
418        .is_none_or(|child| !same_node(child, class_node))
419    {
420        return None;
421    }
422    let displaced = named.next()?;
423    if displaced.kind() != "ERROR" {
424        return None;
425    }
426    let name = displaced_exported_class_name(displaced, source)?;
427
428    let remaining = named.collect::<Vec<_>>();
429    let init = *remaining.last()?;
430    if init.kind() != "init_declarator" {
431        return None;
432    }
433    let final_base = init
434        .child_by_field_name("declarator")
435        .and_then(|base| recovered_malformed_base_name(base, source))?;
436    let body = init.child_by_field_name("value")?;
437    // A complete reduction has a real closing brace here. In Chromium's Widget
438    // declaration, tree-sitter instead emits the same direct `}` slot as a
439    // zero-width missing node where the first body macro truncates the prefix.
440    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
441        return None;
442    }
443
444    let mut declarator_cursor = node.walk();
445    let direct_declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
446    if direct_declarators.count() < 2 {
447        return None;
448    }
449    if remaining[..remaining.len() - 1]
450        .iter()
451        .any(|child| match child.kind() {
452            "qualified_identifier"
453            | "scoped_type_identifier"
454            | "type_identifier"
455            | "identifier" => false,
456            "ERROR" => !is_malformed_inheritance_access(*child, source),
457            _ => true,
458        })
459    {
460        return None;
461    }
462
463    let mut raw_supertypes = Vec::new();
464    for base in &remaining[..remaining.len() - 1] {
465        if base.kind() == "ERROR" {
466            continue;
467        }
468        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
469    }
470    raw_supertypes.push(final_base);
471
472    Some(RecoveredExportedClass {
473        declaration_node: node,
474        name,
475        body: Some(body),
476        raw_supertypes: Some(raw_supertypes),
477        uses_initializer_body: true,
478        fragmented_body: fragmented_export_body_region(node, body, source),
479    })
480}
481
482/// Locate the true class-body region for a fragmented multiple-base export class.
483///
484/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
485/// emits in place of the real class body. Tree-sitter reduces that body in one of
486/// two shapes, both of which lose the members from the recovered node:
487///
488/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
489///   a real closing brace and holds the whole body text inline. The interior between
490///   the braces reparses to the members directly.
491/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
492///   first member with a zero-width MISSING `}`; every later member -- and the real
493///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
494///   siblings. The interior runs from the opening brace to that displaced `}`.
495///
496/// Returns the interior byte range to reparse plus the full class navigation range.
497fn fragmented_export_body_region(
498    node: Node<'_>,
499    body: Node<'_>,
500    source: &str,
501) -> Option<FragmentedExportBody> {
502    let reparse_start = body.start_byte() + 1;
503    let close = direct_close_brace(body)?;
504    if close.end_byte() > close.start_byte() {
505        return Some(FragmentedExportBody {
506            reparse_start,
507            reparse_end: close.start_byte(),
508            class_range: cpp_declaration_range(node),
509        });
510    }
511    // The closing brace was displaced past the recovered node. A balanced nested
512    // class keeps its own braces, so the first lone-`}` sibling is this class's.
513    let mut sibling = node.next_named_sibling();
514    let displaced_close = loop {
515        let Some(current) = sibling else {
516            break displaced_fragment_close_at_namespace_boundary(node, body, source)?;
517        };
518        if cpp_is_stray_close_brace(current, source) {
519            break current;
520        }
521        sibling = current.next_named_sibling();
522    };
523    Some(FragmentedExportBody {
524        reparse_start,
525        reparse_end: displaced_close.start_byte(),
526        class_range: Range {
527            start_byte: node.start_byte(),
528            end_byte: displaced_close.end_byte(),
529            start_line: node.start_position().row + 1,
530            end_line: displaced_close.end_position().row + 1,
531        },
532    })
533}
534
535/// Locate the true class-body region for the export-macro class shape that
536/// tree-sitter promotes to a `function_definition`.
537///
538/// In this shape the synthetic function body closes at the first inline
539/// method, while the class's real members continue as root-level siblings until
540/// a stray `}` followed by the displaced class `;`. Reparse the complete
541/// interior so those siblings are visited with the recovered class scope.
542fn fragmented_export_function_body_region(
543    node: Node<'_>,
544    body: Node<'_>,
545    source: &str,
546) -> Option<FragmentedExportBody> {
547    let reparse_start = body.start_byte().checked_add(1)?;
548    let siblings = cpp_following_named_siblings(node, source);
549    let boundary = fragmented_export_sibling_class_boundary(node, source);
550    let boundary_index = boundary.and_then(|boundary| {
551        siblings
552            .iter()
553            .position(|candidate| same_node(*candidate, boundary))
554    });
555    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
556    let mut sibling_index = 0;
557    // A complete recovered class's synthetic wrapper is immediately followed
558    // by its displaced semicolon (comments may sit between the body and that
559    // semicolon). Only scan for a later stray close when real member siblings
560    // intervene; otherwise every earlier complete class would borrow the next
561    // malformed class's close and claim its members.
562    while let Some(current) = siblings.get(sibling_index).copied() {
563        if current.kind() == "comment" {
564            sibling_index += 1;
565            continue;
566        }
567        if cpp_is_stray_semicolon(current, source) {
568            return None;
569        }
570        break;
571    }
572    while let Some(current) = siblings.get(sibling_index).copied() {
573        let next = siblings.get(sibling_index + 1).copied();
574        if cpp_is_stray_close_brace(current, source)
575            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
576        {
577            let semicolon = next.expect("checked above");
578            return Some(FragmentedExportBody {
579                reparse_start,
580                reparse_end: current.start_byte(),
581                class_range: Range {
582                    start_byte: node.start_byte(),
583                    end_byte: semicolon.end_byte(),
584                    start_line: node.start_position().row + 1,
585                    end_line: semicolon.end_position().row + 1,
586                },
587            });
588        }
589        // When the final access label keeps the class close in its malformed
590        // declaration body, tree-sitter nests the lone `}` ERROR below the
591        // label instead of exposing it as a direct sibling. Search only the
592        // scattered siblings after the synthetic wrapper. The first such
593        // close is the class terminator because nested class bodies retain
594        // their own balanced class_specifier nodes.
595        if current.start_byte() >= body.end_byte()
596            && let Some(close) = cpp_nested_stray_close_brace(current, source)
597        {
598            return Some(FragmentedExportBody {
599                reparse_start,
600                reparse_end: close.start_byte(),
601                class_range: Range {
602                    start_byte: node.start_byte(),
603                    end_byte: current.end_byte(),
604                    start_line: node.start_position().row + 1,
605                    end_line: current.end_position().row + 1,
606                },
607            });
608        }
609        sibling_index += 1;
610    }
611    boundary.map(|boundary| FragmentedExportBody {
612        reparse_start,
613        reparse_end: boundary.start_byte(),
614        class_range: Range {
615            start_byte: node.start_byte(),
616            end_byte: boundary.start_byte(),
617            start_line: node.start_position().row + 1,
618            end_line: boundary.start_position().row + 1,
619        },
620    })
621}
622
623/// Find a later macro-export class that tree-sitter lifted through an enclosing
624/// preprocessor container. A class that is still a direct sibling can be a
625/// nested member of the current fragmented class, so only a changed parent is
626/// a proven boundary between the two recovered class envelopes.
627fn fragmented_export_sibling_class_boundary<'tree>(
628    node: Node<'tree>,
629    source: &str,
630) -> Option<Node<'tree>> {
631    let node_parent = node.parent()?;
632    cpp_following_named_siblings(node, source)
633        .into_iter()
634        .find(|candidate| {
635            recover_exported_class_function_definition(*candidate, source).is_some()
636                && candidate
637                    .parent()
638                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
639        })
640}
641
642/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
643/// export-class wrapper can place the class close inside an access-label node,
644/// so direct-sibling checks alone miss the boundary.  Walk named CST children
645/// only; the helper does not inspect source text beyond the existing structured
646/// stray-brace predicate.
647fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
648    let mut stack = vec![node];
649    while let Some(current) = stack.pop() {
650        if cpp_is_stray_close_brace(current, source) {
651            return Some(current);
652        }
653        let mut cursor = current.walk();
654        stack.extend(current.named_children(&mut cursor));
655    }
656    None
657}
658
659/// Return named siblings that follow `node`, including siblings that tree-sitter
660/// attached to an enclosing container after malformed recovery split the local
661/// declaration list. Stop at the first structurally visible class close so a
662/// later namespace or exported class cannot supply the recovery boundary.
663fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
664    let mut siblings = Vec::new();
665    let mut anchor = node;
666    while let Some(parent) = anchor.parent() {
667        let at_translation_unit = parent.kind() == "translation_unit";
668        let mut sibling = anchor.next_named_sibling();
669        while let Some(current) = sibling {
670            if at_translation_unit
671                && (current.kind() == "namespace_definition"
672                    || (current.kind() == "function_definition"
673                        && first_class_like_child(current).is_some()))
674            {
675                return siblings;
676            }
677            siblings.push(current);
678            if cpp_is_stray_close_brace(current, source) {
679                if let Some(semicolon) = current
680                    .next_named_sibling()
681                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
682                {
683                    siblings.push(semicolon);
684                }
685                return siblings;
686            }
687            if current.start_byte() >= node.end_byte()
688                && matches!(current.kind(), "ERROR" | "labeled_statement")
689                && cpp_nested_stray_close_brace(current, source).is_some()
690            {
691                return siblings;
692            }
693            sibling = current.next_named_sibling();
694        }
695        anchor = parent;
696    }
697    siblings
698}
699
700fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
701    if node.start_byte() >= class_end {
702        return false;
703    }
704    node.end_byte() <= class_end
705        || cpp_nested_stray_close_brace(node, source)
706            .is_some_and(|close| close.start_byte() == class_end)
707}
708
709/// Recover a plain class whose opening prefix is retained in one ERROR node
710/// while one or more nested class closes and the outer close are displaced to
711/// sibling `}`/`;` nodes. This is the non-export counterpart to the fragmented
712/// export-class recovery above. All boundaries come from tree-sitter nodes: the
713/// direct class tokens establish nesting depth and the displaced close nodes
714/// terminate it.
715fn fragmented_plain_class_body(
716    node: Node<'_>,
717    source: &str,
718) -> Option<(String, FragmentedExportBody)> {
719    if node.kind() != "ERROR" {
720        return None;
721    }
722    let mut cursor = node.walk();
723    let children = node.children(&mut cursor).collect::<Vec<_>>();
724    let keyword = children.first()?;
725    if !matches!(keyword.kind(), "class" | "struct" | "union") {
726        return None;
727    }
728    let name_node = children
729        .iter()
730        .copied()
731        .skip(1)
732        .find(|child| child.is_named())?;
733    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
734        return None;
735    }
736    let name = normalize_cpp_whitespace(node_text(name_node, source));
737    if name.is_empty() || cpp_export_macro_token(&name) {
738        return None;
739    }
740    let open_index = children.iter().position(|child| child.kind() == "{")?;
741    let open = children[open_index];
742    let nested_class_opens = children[open_index + 1..]
743        .iter()
744        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
745        .count();
746    let mut closes_remaining = 1 + nested_class_opens;
747    let mut sibling = node.next_named_sibling();
748    while let Some(candidate) = sibling {
749        let next = candidate.next_named_sibling();
750        if cpp_is_stray_close_brace(candidate, source) {
751            closes_remaining -= 1;
752            if closes_remaining == 0 {
753                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
754                if open.end_byte() >= candidate.start_byte() {
755                    return None;
756                }
757                return Some((
758                    name,
759                    FragmentedExportBody {
760                        reparse_start: open.end_byte(),
761                        reparse_end: candidate.start_byte(),
762                        class_range: Range {
763                            start_byte: node.start_byte(),
764                            end_byte: semicolon.end_byte(),
765                            start_line: node.start_position().row + 1,
766                            end_line: semicolon.end_position().row + 1,
767                        },
768                    },
769                ));
770            }
771        }
772        sibling = next;
773    }
774    None
775}
776
777fn displaced_fragment_close_at_namespace_boundary<'tree>(
778    declaration: Node<'tree>,
779    body: Node<'tree>,
780    source: &str,
781) -> Option<Node<'tree>> {
782    let declaration_list = declaration.parent()?;
783    if declaration_list.kind() != "declaration_list" {
784        return None;
785    }
786    let namespace = declaration_list.parent()?;
787    if namespace.kind() != "namespace_definition"
788        || namespace.child_by_field_name("body") != Some(declaration_list)
789    {
790        return None;
791    }
792    let class_close = direct_close_brace(declaration_list)?;
793    let trailing_semicolon = namespace.next_named_sibling()?;
794    if trailing_semicolon.kind() != "expression_statement"
795        || trailing_semicolon.named_child_count() != 0
796    {
797        return None;
798    }
799    let displaced_namespace_close = trailing_semicolon.next_named_sibling()?;
800    if !cpp_is_stray_close_brace(displaced_namespace_close, source) {
801        return None;
802    }
803    let reparse_start = body.start_byte() + 1;
804    let tree = cpp_reparse_region_items(source, reparse_start, class_close.start_byte())?;
805    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(class_close)
806}
807
808/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
809fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
810    (0..node.child_count())
811        .filter_map(|index| node.child(index))
812        .find(|child| !child.is_named() && child.kind() == "}")
813}
814
815/// A displaced lone closing brace: the class close that the fragmented multiple-base
816/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
817fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
818    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
819}
820
821/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
822/// text while skipping line/block comments and string/char literals. The
823/// exported-class recovery needs this when tree-sitter's bogus
824/// `function_definition` body runs past the class's true closing brace and
825/// swallows following siblings (issue #1524): the grammar tree carries no
826/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
827/// close is located textually. Returns `None` when the text is unbalanced or
828/// contains a construct the scanner deliberately does not interpret (raw
829/// strings) -- callers treat that as "cannot partition" and keep the
830/// un-split recovery.
831fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
832    let bytes = source.as_bytes();
833    if bytes.get(open_byte) != Some(&b'{') {
834        return None;
835    }
836    let mut depth = 0usize;
837    let mut i = open_byte;
838    while i < bytes.len() {
839        match bytes[i] {
840            b'{' => depth += 1,
841            b'}' => {
842                depth = depth.checked_sub(1)?;
843                if depth == 0 {
844                    return Some(i);
845                }
846            }
847            b'/' if bytes.get(i + 1) == Some(&b'/') => {
848                while i < bytes.len() && bytes[i] != b'\n' {
849                    i += 1;
850                }
851                continue;
852            }
853            b'/' if bytes.get(i + 1) == Some(&b'*') => {
854                i += 2;
855                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
856                    i += 1;
857                }
858                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
859                continue;
860            }
861            quote @ (b'"' | b'\'') => {
862                // Raw strings (R"(...)") can hold unescaped quotes and braces;
863                // bail out rather than mis-count.
864                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
865                    return None;
866                }
867                i += 1;
868                while i < bytes.len() && bytes[i] != quote {
869                    i += if bytes[i] == b'\\' { 2 } else { 1 };
870                }
871                if i >= bytes.len() {
872                    return None;
873                }
874            }
875            _ => {}
876        }
877        i += 1;
878    }
879    None
880}
881
882fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
883    let mut name = None;
884    let mut colon_count = 0;
885    let mut access_count = 0;
886    for index in 0..node.child_count() {
887        let child = node.child(index)?;
888        match child.kind() {
889            "identifier" | "type_identifier" if child.is_named() => {
890                if name.is_some() {
891                    return None;
892                }
893                let candidate = normalize_cpp_whitespace(node_text(child, source));
894                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
895                    return None;
896                }
897                name = Some(candidate);
898            }
899            ":" if !child.is_named() => colon_count += 1,
900            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
901            _ => return None,
902        }
903    }
904    (colon_count == 1 && access_count == 1)
905        .then_some(name)
906        .flatten()
907}
908
909fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
910    if node.kind() != "ERROR" || node.named_child_count() != 1 {
911        return false;
912    }
913    node.named_child(0)
914        .and_then(|child| direct_identifier_name(child, source))
915        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
916}
917
918fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
919    (0..node.child_count()).any(|index| {
920        node.child(index)
921            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
922    })
923}
924
925fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
926    match node.kind() {
927        "type_identifier" | "identifier" | "namespace_identifier" => {
928            recovered_base_atom(node, source)
929        }
930        "template_type" | "template_function" => node
931            .child_by_field_name("name")
932            .and_then(|name| recovered_malformed_base_name(name, source)),
933        "ERROR" => None,
934        "qualified_identifier" | "scoped_type_identifier" => {
935            let suffix = node
936                .child_by_field_name("name")
937                .and_then(|name| recovered_malformed_base_name(name, source))?;
938            let scope = node
939                .child_by_field_name("scope")
940                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
941            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
942                malformed_qualified_prefix(node, source)?
943            } else {
944                if malformed_qualified_prefix(node, source).is_some() {
945                    return None;
946                }
947                scope
948            };
949            Some(format!("{prefix}::{suffix}"))
950        }
951        _ => None,
952    }
953}
954
955fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
956    if !matches!(
957        node.kind(),
958        "identifier" | "type_identifier" | "namespace_identifier"
959    ) {
960        return None;
961    }
962    let name = normalize_cpp_whitespace(node_text(node, source));
963    (!name.is_empty()).then_some(name)
964}
965
966fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
967    let mut prefix = None;
968    let mut cursor = node.walk();
969    for error in node
970        .named_children(&mut cursor)
971        .filter(|child| child.kind() == "ERROR")
972    {
973        if error.named_child_count() != 1 || prefix.is_some() {
974            return None;
975        }
976        prefix = error
977            .named_child(0)
978            .and_then(|child| recovered_base_atom(child, source));
979        prefix.as_ref()?;
980    }
981    prefix
982}
983
984fn recover_exported_class_function_definition<'tree>(
985    node: Node<'tree>,
986    source: &str,
987) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
988    if node.kind() != "function_definition" {
989        return None;
990    }
991    let type_node = node.child_by_field_name("type")?;
992    let declarator = node.child_by_field_name("declarator")?;
993
994    if matches!(
995        type_node.kind(),
996        "class_specifier" | "struct_specifier" | "union_specifier"
997    ) {
998        let type_name = type_node
999            .child_by_field_name("name")
1000            .and_then(|name| direct_identifier_name(name, source));
1001        let exported_macro_type = type_name
1002            .as_ref()
1003            .is_some_and(|name| cpp_export_macro_token(name));
1004        if exported_macro_type {
1005            let mut cursor = node.walk();
1006            let errors_before_declarator = node
1007                .named_children(&mut cursor)
1008                .filter(|child| {
1009                    child.kind() == "ERROR"
1010                        && child.start_byte() >= type_node.end_byte()
1011                        && child.end_byte() <= declarator.start_byte()
1012                })
1013                .collect::<Vec<_>>();
1014            if let Some(name) = errors_before_declarator
1015                .iter()
1016                .find_map(|error| displaced_exported_class_name(*error, source))
1017            {
1018                let raw_supertypes = errors_before_declarator
1019                    .iter()
1020                    .any(|error| malformed_inheritance_syntax(*error))
1021                    .then(|| recovered_malformed_base_name(declarator, source))
1022                    .flatten()
1023                    .map(|base| vec![base]);
1024                return Some((node, name, raw_supertypes));
1025            }
1026            if errors_before_declarator
1027                .iter()
1028                .any(|error| malformed_inheritance_syntax(*error))
1029            {
1030                return None;
1031            }
1032        }
1033        if !exported_macro_type
1034            && let Some(name) = type_name
1035            && !cpp_export_macro_token(&name)
1036            && let Some(base) =
1037                recovered_postfix_export_macro_base(node, type_node, declarator, source)
1038        {
1039            return Some((node, name, Some(vec![base])));
1040        }
1041        if let Some(name) = direct_identifier_name(declarator, source)
1042            && exported_macro_type
1043            && !cpp_export_macro_token(&name)
1044        {
1045            let raw_supertypes = exported_macro_type
1046                .then(|| recovered_single_base_after_declarator(node, declarator, source))
1047                .flatten()
1048                .map(|base| vec![base]);
1049            return Some((node, name, raw_supertypes));
1050        }
1051        if declarator.kind() == "parenthesized_declarator"
1052            && type_node
1053                .child_by_field_name("name")
1054                .and_then(|name| direct_identifier_name(name, source))
1055                .is_some_and(|name| cpp_export_macro_token(&name))
1056        {
1057            let body_start = node
1058                .child_by_field_name("body")
1059                .map(|body| body.start_byte())
1060                .unwrap_or(node.end_byte());
1061            let mut cursor = node.walk();
1062            if let Some(name) = node
1063                .named_children(&mut cursor)
1064                .filter(|child| {
1065                    child.kind() == "ERROR"
1066                        && child.start_byte() >= declarator.end_byte()
1067                        && child.end_byte() <= body_start
1068                })
1069                .find_map(|error| declarator_name_from_node(error, source))
1070            {
1071                return Some((node, name, None));
1072            }
1073        }
1074    }
1075
1076    let declarator_text = direct_identifier_name(declarator, source)?;
1077    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1078        return None;
1079    }
1080    class_identifier_before_body(node, source).map(|name| (node, name, None))
1081}
1082
1083/// Recover the class item from a region reparse that still carries the
1084/// sentinel's synthetic function envelope.  An unknown class attribute can
1085/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
1086/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
1087/// then nested below that function, so direct class-child lookup is not enough.
1088struct CppSentinelReparsedClass<'tree> {
1089    declaration_node: Node<'tree>,
1090    name: String,
1091    body: Node<'tree>,
1092    raw_supertypes: Option<Vec<String>>,
1093}
1094
1095fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1096    let mut cursor = root.walk();
1097    root.named_children(&mut cursor)
1098        .find(|child| child.kind() != "comment")
1099        .filter(|child| child.kind() == "template_declaration")
1100}
1101
1102fn cpp_sentinel_reparsed_class<'tree>(
1103    root: Node<'tree>,
1104    template_node: Option<Node<'tree>>,
1105    source: &str,
1106) -> Option<CppSentinelReparsedClass<'tree>> {
1107    let container = template_node.unwrap_or(root);
1108    let mut cursor = container.walk();
1109    for child in container.named_children(&mut cursor) {
1110        if matches!(
1111            child.kind(),
1112            "class_specifier" | "struct_specifier" | "union_specifier"
1113        ) {
1114            let name = class_like_name(child, source)?;
1115            let body = cpp_body_node(child)?;
1116            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1117                .then(|| extract_cpp_supertypes(child, source));
1118            return Some(CppSentinelReparsedClass {
1119                declaration_node: child,
1120                name,
1121                body,
1122                raw_supertypes,
1123            });
1124        }
1125        if child.kind() == "declaration"
1126            && let Some(class_node) = first_class_like_child(child)
1127        {
1128            let name = class_like_name(class_node, source)?;
1129            let body = cpp_body_node(class_node)?;
1130            let raw_supertypes =
1131                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1132                    .then(|| extract_cpp_supertypes(class_node, source));
1133            return Some(CppSentinelReparsedClass {
1134                declaration_node: class_node,
1135                name,
1136                body,
1137                raw_supertypes,
1138            });
1139        }
1140        // Only when the nested class item carries its own body. A bodyless
1141        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
1142        // a function definition -- is the export-macro shape recovered by the
1143        // next arm, and must fall through to it rather than abort the search.
1144        if child.kind() == "function_definition"
1145            && let Some(class_node) = first_class_like_child(child)
1146            && let Some(body) = cpp_body_node(class_node)
1147            && let Some(name) = class_like_name(class_node, source)
1148        {
1149            let raw_supertypes =
1150                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1151                    .then(|| extract_cpp_supertypes(class_node, source));
1152            return Some(CppSentinelReparsedClass {
1153                declaration_node: class_node,
1154                name,
1155                body,
1156                raw_supertypes,
1157            });
1158        }
1159        if child.kind() == "function_definition"
1160            && let Some((_, name, raw_supertypes)) =
1161                recover_exported_class_function_definition(child, source)
1162        {
1163            let body = cpp_body_node(child)?;
1164            return Some(CppSentinelReparsedClass {
1165                declaration_node: child,
1166                name,
1167                body,
1168                raw_supertypes,
1169            });
1170        }
1171    }
1172    None
1173}
1174
1175fn recovered_postfix_export_macro_base(
1176    node: Node<'_>,
1177    type_node: Node<'_>,
1178    declarator: Node<'_>,
1179    source: &str,
1180) -> Option<String> {
1181    let mut cursor = node.walk();
1182    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
1183        child.kind() == "ERROR"
1184            && child.start_byte() >= type_node.end_byte()
1185            && child.end_byte() <= declarator.start_byte()
1186            && postfix_export_macro_inheritance(*child, source)
1187    });
1188    malformed_clauses.next()?;
1189    if malformed_clauses.next().is_some() {
1190        return None;
1191    }
1192    recovered_malformed_base_name(declarator, source)
1193}
1194
1195fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
1196    let mut macro_count = 0;
1197    let mut colon_count = 0;
1198    let mut access_count = 0;
1199    for index in 0..node.child_count() {
1200        let Some(child) = node.child(index) else {
1201            return false;
1202        };
1203        match child.kind() {
1204            "identifier" | "type_identifier" if child.is_named() => {
1205                let candidate = normalize_cpp_whitespace(node_text(child, source));
1206                if !cpp_export_macro_token(&candidate) {
1207                    return false;
1208                }
1209                macro_count += 1;
1210            }
1211            ":" if !child.is_named() => colon_count += 1,
1212            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1213            _ => return false,
1214        }
1215    }
1216    macro_count == 1 && colon_count == 1 && access_count == 1
1217}
1218
1219fn recovered_single_base_after_declarator(
1220    node: Node<'_>,
1221    declarator: Node<'_>,
1222    source: &str,
1223) -> Option<String> {
1224    let body_start = node
1225        .child_by_field_name("body")
1226        .map(|body| body.start_byte())
1227        .unwrap_or(node.end_byte());
1228    let mut cursor = node.walk();
1229    let mut bases = node
1230        .named_children(&mut cursor)
1231        .filter(|child| {
1232            child.kind() == "ERROR"
1233                && child.start_byte() >= declarator.end_byte()
1234                && child.end_byte() <= body_start
1235        })
1236        .filter_map(|error| displaced_exported_class_name(error, source));
1237    let base = bases.next()?;
1238    bases.next().is_none().then_some(base)
1239}
1240
1241fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
1242    (0..node.child_count()).any(|index| {
1243        node.child(index)
1244            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
1245    })
1246}
1247
1248pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
1249    recover_exported_class_function_definition(node, source).is_some()
1250}
1251
1252fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
1253    matches!(
1254        kind,
1255        "ERROR"
1256            | "preproc_if"
1257            | "preproc_ifdef"
1258            | "preproc_ifndef"
1259            | "preproc_else"
1260            | "preproc_elif"
1261    ) || (kind == "labeled_statement" && in_class_scope)
1262}
1263
1264pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
1265    if node.kind() != "declaration" {
1266        return false;
1267    }
1268    let mut ancestor = node.parent();
1269    while let Some(container) = ancestor {
1270        match container.kind() {
1271            "compound_statement" => {
1272                return container.parent().is_some_and(|class_container| {
1273                    is_recovered_exported_class_container(class_container, source)
1274                });
1275            }
1276            // These containers preserve ScopeInfo in visit_node. declaration_list is
1277            // the body container selected for a linkage specification.
1278            "template_declaration" | "linkage_specification" | "declaration_list" => {}
1279            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
1280            _ => return false,
1281        }
1282        ancestor = container.parent();
1283    }
1284    false
1285}
1286
1287pub fn recovered_exported_class_has_body(
1288    node: Node<'_>,
1289    source: &str,
1290    expected_name: &str,
1291) -> Option<bool> {
1292    match node.kind() {
1293        "function_definition" => {
1294            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
1295            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
1296        }
1297        "declaration" | "field_declaration" => {
1298            let recovered = recover_exported_class_declaration(node, source)?;
1299            (recovered.name == expected_name).then(|| recovered.body.is_some())
1300        }
1301        _ => None,
1302    }
1303}
1304
1305fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
1306    let body_start = node
1307        .child_by_field_name("body")
1308        .map(|body| body.start_byte())
1309        .unwrap_or(node.end_byte());
1310    let mut stack = Vec::new();
1311    for index in (0..node.named_child_count()).rev() {
1312        let Some(child) = node.named_child(index) else {
1313            continue;
1314        };
1315        if child.start_byte() >= body_start {
1316            continue;
1317        }
1318        stack.push(child);
1319    }
1320
1321    let mut best = None;
1322    while let Some(current) = stack.pop() {
1323        if matches!(current.kind(), "identifier" | "type_identifier") {
1324            let name = normalize_cpp_whitespace(node_text(current, source));
1325            if !name.is_empty()
1326                && !cpp_export_macro_token(&name)
1327                && !matches!(name.as_str(), "class" | "struct" | "union")
1328            {
1329                best = Some(name);
1330            }
1331            continue;
1332        }
1333
1334        for index in (0..current.named_child_count()).rev() {
1335            if let Some(child) = current.named_child(index)
1336                && child.start_byte() < body_start
1337            {
1338                stack.push(child);
1339            }
1340        }
1341    }
1342    best
1343}
1344
1345fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1346    if node.kind() == "declaration"
1347        && node
1348            .child_by_field_name("type")
1349            .or_else(|| first_class_like_child(node))
1350            .is_some_and(|type_node| {
1351                matches!(
1352                    type_node.kind(),
1353                    "class_specifier" | "struct_specifier" | "union_specifier"
1354                )
1355            })
1356        && let Some(name) = node
1357            .child_by_field_name("declarator")
1358            .and_then(|declarator| declarator_name_from_node(declarator, source))
1359        && !cpp_export_macro_token(&name)
1360    {
1361        return Some(name);
1362    }
1363
1364    if node.kind() == "function_definition"
1365        && node.child_by_field_name("type").is_some_and(|type_node| {
1366            matches!(
1367                type_node.kind(),
1368                "class_specifier" | "struct_specifier" | "union_specifier"
1369            )
1370        })
1371        && let Some(name) = node
1372            .child_by_field_name("declarator")
1373            .and_then(|declarator| direct_identifier_name(declarator, source))
1374        && !cpp_export_macro_token(&name)
1375    {
1376        return Some(name);
1377    }
1378
1379    let class_node = if matches!(
1380        node.kind(),
1381        "class_specifier" | "struct_specifier" | "union_specifier"
1382    ) {
1383        node
1384    } else {
1385        first_class_like_child(node)?
1386    };
1387    class_like_name_from_children(class_node, source)
1388}
1389
1390fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
1391    if !matches!(
1392        node.kind(),
1393        "identifier" | "field_identifier" | "type_identifier"
1394    ) {
1395        return None;
1396    }
1397    let name = normalize_cpp_whitespace(node_text(node, source));
1398    (!name.is_empty()).then_some(name)
1399}
1400
1401fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1402    match node.kind() {
1403        "identifier" | "field_identifier" | "type_identifier" => {
1404            let name = normalize_cpp_whitespace(node_text(node, source));
1405            (!name.is_empty()).then_some(name)
1406        }
1407        _ => {
1408            let mut cursor = node.walk();
1409            node.named_children(&mut cursor)
1410                .find_map(|child| declarator_name_from_node(child, source))
1411        }
1412    }
1413}
1414
1415fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
1416    let mut cursor = node.walk();
1417    node.named_children(&mut cursor).find(|child| {
1418        matches!(
1419            child.kind(),
1420            "class_specifier" | "struct_specifier" | "union_specifier"
1421        )
1422    })
1423}
1424
1425/// Push a container's children as a `Siblings` cursor rather than snapshotting
1426/// them all with one shared scope: children are visited one at a time so a
1427/// `using namespace X;` sibling can affect the scope threaded to the siblings
1428/// that textually follow it (issue #1093).
1429fn push_cpp_container_work<'tree>(
1430    node: Node<'tree>,
1431    scope: ScopeInfo,
1432    stack: &mut Vec<CppWork<'tree>>,
1433) {
1434    stack.push(CppWork::Siblings(CppSiblingsWork {
1435        parent: node,
1436        next_index: 0,
1437        end_index: usize::MAX,
1438        scope,
1439    }));
1440}
1441
1442/// Advance a `Siblings` cursor by one child: dispatch the current child under
1443/// the scope accumulated from its *earlier* siblings, then push a
1444/// continuation for the remaining siblings carrying the scope updated for
1445/// *this* child (only `using namespace X;` directives change it). Pushing the
1446/// continuation before the current child's own node work means the current
1447/// child's subtree fully drains (LIFO) before the next sibling is visited,
1448/// preserving left-to-right order.
1449fn advance_cpp_siblings<'tree>(
1450    siblings: CppSiblingsWork<'tree>,
1451    source: &str,
1452    stack: &mut Vec<CppWork<'tree>>,
1453) {
1454    if siblings.next_index >= siblings.end_index {
1455        return;
1456    }
1457    let Some(child) = siblings.parent.named_child(siblings.next_index) else {
1458        return;
1459    };
1460    let current_scope = siblings.scope.clone();
1461    let mut next_scope = siblings.scope;
1462    if let Some(namespace) = cpp_using_namespace_target(child, source) {
1463        next_scope.visible_using_namespaces.push(namespace);
1464    }
1465    stack.push(CppWork::Siblings(CppSiblingsWork {
1466        parent: siblings.parent,
1467        next_index: siblings.next_index + 1,
1468        end_index: siblings.end_index,
1469        scope: next_scope,
1470    }));
1471    stack.push(CppWork::Node(CppNodeWork {
1472        node: child,
1473        scope: current_scope,
1474    }));
1475}
1476
1477/// The namespace target of a `using namespace X;` directive, or `None` for
1478/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
1479/// kind. Distinguished structurally by the presence of the grammar's literal
1480/// `namespace` keyword token among the node's children -- not by inspecting
1481/// source text -- so it never misreads a member-importing using-declaration
1482/// as a namespace directive.
1483fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
1484    if node.kind() != "using_declaration" {
1485        return None;
1486    }
1487    let mut cursor = node.walk();
1488    let is_namespace_directive = node
1489        .children(&mut cursor)
1490        .any(|child| child.kind() == "namespace");
1491    if !is_namespace_directive {
1492        return None;
1493    }
1494    let target = node.named_child(0)?;
1495    let text = normalize_cpp_whitespace(node_text(target, source));
1496    (!text.is_empty()).then_some(text)
1497}
1498
1499/// Every `using namespace X;` directive target in a file, in source order, for
1500/// resolution-time consumers that need the file's using-directives without the
1501/// per-position scope threading extraction does. Parses `source` fresh and
1502/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
1503/// keys on the grammar's `namespace` keyword token, not source text), so it
1504/// never misreads a member-importing `using X::Y;` as a namespace directive.
1505///
1506/// This is a whole-file over-approximation of what is in scope at any one point
1507/// (a directive nested inside a `namespace {}` block or a function body is still
1508/// reported), which is exactly what the #1134 identity reconciler wants: extra
1509/// candidate namespaces that no visible class confirms are harmless, and two
1510/// that both confirm are treated as a genuine ambiguity by the reconciler.
1511pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
1512    let mut parser = Parser::new();
1513    if parser
1514        .set_language(&tree_sitter_cpp::LANGUAGE.into())
1515        .is_err()
1516    {
1517        return Vec::new();
1518    }
1519    let Some(tree) = parser.parse(source, None) else {
1520        return Vec::new();
1521    };
1522    let mut namespaces = Vec::new();
1523    let mut seen = std::collections::HashSet::new();
1524    let mut stack = vec![tree.root_node()];
1525    while let Some(node) = stack.pop() {
1526        if let Some(namespace) = cpp_using_namespace_target(node, source)
1527            && seen.insert(namespace.clone())
1528        {
1529            namespaces.push(namespace);
1530        }
1531        let mut cursor = node.walk();
1532        stack.extend(node.named_children(&mut cursor));
1533    }
1534    namespaces
1535}
1536
1537pub struct CppVisitor<'a> {
1538    pub file: &'a ProjectFile,
1539    pub source: &'a str,
1540    pub parsed: &'a mut ParsedFile,
1541    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
1542    /// Byte regions whose contents were re-owned by a fragmented export-class
1543    /// recovery (#938): the scattered members between the fragmented
1544    /// declaration and its displaced closing brace are indexed as members of
1545    /// the recovered class by the region reparse, so the ordinary sibling walk
1546    /// must not ALSO index them as top-level declarations (that double-indexing
1547    /// made a scattered nested class ambiguous between `Inner` and
1548    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
1549    /// linear scan at visit time is fine.
1550    pub consumed_fragment_regions: Vec<(usize, usize)>,
1551}
1552
1553impl<'a> CppVisitor<'a> {
1554    #[allow(clippy::too_many_arguments)]
1555    pub fn visit_container(
1556        &mut self,
1557        node: Node<'_>,
1558        package_name: &str,
1559        module: Option<CodeUnit>,
1560        class_unit: Option<CodeUnit>,
1561        template_signature: Option<String>,
1562        visible_using_namespaces: Vec<String>,
1563    ) {
1564        let scope = ScopeInfo {
1565            package_name: package_name.to_string(),
1566            module,
1567            class_unit,
1568            template_signature,
1569            template_metadata: None,
1570            declarations_are_fields: false,
1571            recovered_specialization_member_scope: false,
1572            visible_using_namespaces,
1573        };
1574        self.run_container_work(node, scope);
1575    }
1576
1577    /// Whether a work node lies entirely inside a byte region consumed by a
1578    /// fragmented export-class recovery (#938); such nodes were already indexed
1579    /// as members of the recovered class by the region reparse.
1580    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
1581        self.consumed_fragment_regions
1582            .iter()
1583            .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
1584    }
1585
1586    /// Drive the container work loop from an explicit seed scope to completion. The
1587    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
1588    /// stays alive for the whole traversal.
1589    fn run_container_work<'tree>(&mut self, node: Node<'tree>, scope: ScopeInfo) {
1590        let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
1591        while let Some(work) = stack.pop() {
1592            match work {
1593                CppWork::Container(container) => {
1594                    push_cpp_container_work(container.node, container.scope, &mut stack);
1595                }
1596                CppWork::Siblings(siblings) => {
1597                    advance_cpp_siblings(siblings, self.source, &mut stack);
1598                }
1599                CppWork::Node(work) => {
1600                    if self.node_is_inside_consumed_fragment(work.node) {
1601                        continue;
1602                    }
1603                    self.visit_node(work.node, &work.scope, &mut stack);
1604                }
1605            }
1606        }
1607    }
1608
1609    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
1610    /// it only when the entire region is member-shaped. This validation must happen
1611    /// before registering the recovered class because a rejected speculative range
1612    /// must not leak into the ordinary recovery path.
1613    fn reparse_fragmented_export_class_members(
1614        &self,
1615        fragmented: &FragmentedExportBody,
1616        class_name: &str,
1617    ) -> Option<FragmentedExportMembers> {
1618        if fragmented.reparse_start >= fragmented.reparse_end {
1619            return None;
1620        }
1621        let tree = cpp_reparse_fragmented_class_body(
1622            self.source,
1623            fragmented.reparse_start,
1624            fragmented.reparse_end,
1625        )?;
1626        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
1627            return Some(FragmentedExportMembers::Complete(tree));
1628        }
1629        let has_conditional_constructor = {
1630            let root = tree.root_node();
1631            let mut cursor = root.walk();
1632            root.named_children(&mut cursor).any(|child| {
1633                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
1634            })
1635        };
1636        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
1637    }
1638
1639    /// Index an already validated fragmented body as members of `class_unit`. The
1640    /// region reparse keeps each member's exact original byte and line positions.
1641    fn visit_fragmented_export_class_members(
1642        &mut self,
1643        outcome: FragmentedExportMembers,
1644        class_unit: CodeUnit,
1645        scope: &ScopeInfo,
1646    ) -> bool {
1647        let (tree, complete) = match outcome {
1648            FragmentedExportMembers::Complete(tree) => (tree, true),
1649            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
1650        };
1651        let root = tree.root_node();
1652        let class_name = class_unit.identifier().to_string();
1653        let member_scope = ScopeInfo {
1654            // A recovered export-macro class may borrow its namespace from an
1655            // earlier forward declaration even when the malformed node itself
1656            // sits at file scope. Use the recovered class identity as the
1657            // authoritative package for reparsed members as well.
1658            package_name: class_unit.package_name().to_string(),
1659            module: scope.module.clone(),
1660            class_unit: Some(class_unit),
1661            template_signature: scope.template_signature.clone(),
1662            template_metadata: None,
1663            declarations_are_fields: true,
1664            recovered_specialization_member_scope: false,
1665            visible_using_namespaces: scope.visible_using_namespaces.clone(),
1666        };
1667        if !complete {
1668            // A conditional beginning immediately after an access label can
1669            // fragment one constructor declaration while leaving the rest of
1670            // the class body as unsafe statement soup. Recover only that
1671            // structurally proven constructor and leave the outer-tree
1672            // siblings unconsumed for their ordinary walk.
1673            let mut cursor = root.walk();
1674            let constructors = root
1675                .named_children(&mut cursor)
1676                .filter_map(|child| {
1677                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
1678                })
1679                .collect::<Vec<_>>();
1680            for constructor in constructors {
1681                let mut stack = Vec::new();
1682                self.visit_node(constructor, &member_scope, &mut stack);
1683                while let Some(work) = stack.pop() {
1684                    match work {
1685                        CppWork::Container(container) => {
1686                            push_cpp_container_work(container.node, container.scope, &mut stack);
1687                        }
1688                        CppWork::Siblings(siblings) => {
1689                            advance_cpp_siblings(siblings, self.source, &mut stack);
1690                        }
1691                        CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
1692                    }
1693                }
1694            }
1695            return false;
1696        }
1697        self.run_container_work(root, member_scope);
1698        true
1699    }
1700
1701    fn visit_recovered_fragment_constructor(
1702        &mut self,
1703        range: std::ops::Range<usize>,
1704        constructor_body: Node<'_>,
1705        class_declaration: Node<'_>,
1706        class_unit: &CodeUnit,
1707        scope: &ScopeInfo,
1708    ) {
1709        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
1710            return;
1711        };
1712        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
1713            tree.root_node(),
1714            range.start,
1715            class_unit.identifier(),
1716            self.source,
1717        ) else {
1718            return;
1719        };
1720        let member_scope = ScopeInfo {
1721            package_name: class_unit.package_name().to_string(),
1722            module: scope.module.clone(),
1723            class_unit: Some(class_unit.clone()),
1724            template_signature: scope.template_signature.clone(),
1725            template_metadata: None,
1726            declarations_are_fields: true,
1727            recovered_specialization_member_scope: false,
1728            visible_using_namespaces: scope.visible_using_namespaces.clone(),
1729        };
1730        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
1731        else {
1732            return;
1733        };
1734        debug_assert_eq!(function.name, class_unit.identifier());
1735        let code_unit = function.code_unit(self.file.clone());
1736        self.parsed.add_code_unit_with_range(
1737            code_unit.clone(),
1738            Range {
1739                start_byte: function_declarator.start_byte(),
1740                end_byte: constructor_body.end_byte(),
1741                start_line: function_declarator.start_position().row + 1,
1742                end_line: constructor_body.end_position().row + 1,
1743            },
1744            None,
1745            None,
1746        );
1747        self.parsed.add_signature_with_metadata(
1748            code_unit.clone(),
1749            cpp_signature_metadata(
1750                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
1751                function_declarator,
1752                self.source,
1753            )
1754            .with_declaration_only(false)
1755            .with_callable_linkage(cpp_callable_linkage(class_declaration, self.source)),
1756        );
1757        self.parsed.add_child(class_unit.clone(), code_unit);
1758    }
1759
1760    fn visit_recovered_fragment_prefix_members(
1761        &mut self,
1762        root: Node<'_>,
1763        constructor_start: usize,
1764        class_unit: &CodeUnit,
1765        scope: &ScopeInfo,
1766    ) {
1767        let member_scope = ScopeInfo {
1768            package_name: class_unit.package_name().to_string(),
1769            module: scope.module.clone(),
1770            class_unit: Some(class_unit.clone()),
1771            template_signature: scope.template_signature.clone(),
1772            template_metadata: None,
1773            declarations_are_fields: true,
1774            recovered_specialization_member_scope: false,
1775            visible_using_namespaces: scope.visible_using_namespaces.clone(),
1776        };
1777        let mut stack = vec![root];
1778        while let Some(current) = stack.pop() {
1779            if current.kind() == "comment" || current.start_byte() >= constructor_start {
1780                continue;
1781            }
1782            if current.end_byte() <= constructor_start
1783                && current.kind() != "translation_unit"
1784                && current.kind() != "labeled_statement"
1785                && current.kind() != "ERROR"
1786            {
1787                let mut work_stack = Vec::new();
1788                self.visit_node(current, &member_scope, &mut work_stack);
1789                while let Some(work) = work_stack.pop() {
1790                    match work {
1791                        CppWork::Container(container) => {
1792                            push_cpp_container_work(
1793                                container.node,
1794                                container.scope,
1795                                &mut work_stack,
1796                            );
1797                        }
1798                        CppWork::Siblings(siblings) => {
1799                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
1800                        }
1801                        CppWork::Node(work) => {
1802                            self.visit_node(work.node, &work.scope, &mut work_stack)
1803                        }
1804                    }
1805                }
1806                continue;
1807            }
1808            if matches!(
1809                current.kind(),
1810                "translation_unit" | "labeled_statement" | "ERROR"
1811            ) {
1812                let mut cursor = current.walk();
1813                stack.extend(current.named_children(&mut cursor));
1814            }
1815        }
1816    }
1817
1818    fn visit_node<'tree>(
1819        &mut self,
1820        node: Node<'tree>,
1821        scope: &ScopeInfo,
1822        stack: &mut Vec<CppWork<'tree>>,
1823    ) {
1824        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
1825            self.visit_node(node, &recovered_scope, stack);
1826            return;
1827        }
1828        if let Some((name, fragmented)) = fragmented_plain_class_body(node, self.source) {
1829            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
1830            let mut class_stack = Vec::new();
1831            let class_unit = self.visit_named_class_like_shape(
1832                node,
1833                name,
1834                None,
1835                true,
1836                Some(fragmented.class_range),
1837                Some(extract_cpp_supertypes(node, self.source)),
1838                scope,
1839                &mut class_stack,
1840            );
1841            let member_scope = ScopeInfo {
1842                package_name: class_unit.package_name().to_string(),
1843                module: scope.module.clone(),
1844                class_unit: Some(class_unit.clone()),
1845                template_signature: scope.template_signature.clone(),
1846                template_metadata: None,
1847                declarations_are_fields: true,
1848                recovered_specialization_member_scope: false,
1849                visible_using_namespaces: scope.visible_using_namespaces.clone(),
1850            };
1851            let complete = outcome.is_some_and(|outcome| {
1852                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
1853            });
1854            if complete {
1855                self.consumed_fragment_regions
1856                    .push((node.start_byte(), fragmented.class_range.end_byte));
1857            } else {
1858                // A macro-constrained member can make the full body reparse
1859                // unsafe while tree-sitter still exposes later class members
1860                // as bounded siblings up to the displaced `}`/`;`. Keep the
1861                // structurally proven class/base declaration and re-own those
1862                // sibling nodes under it. They retain their original parser
1863                // nodes and exact ranges; the close boundary comes solely from
1864                // `fragmented_plain_class_body`.
1865                let mut sibling = node.next_named_sibling();
1866                while let Some(candidate) = sibling {
1867                    if candidate.start_byte() >= fragmented.reparse_end {
1868                        break;
1869                    }
1870                    if cpp_fragment_sibling_is_class_member(
1871                        candidate,
1872                        fragmented.reparse_end,
1873                        self.source,
1874                    ) {
1875                        self.recovered_class_sibling_scopes
1876                            .insert(candidate.id(), member_scope.clone());
1877                    }
1878                    sibling = candidate.next_named_sibling();
1879                }
1880            }
1881            stack.extend(class_stack);
1882            return;
1883        }
1884        match node.kind() {
1885            "template_declaration" => {
1886                if let Some(recovered) = recover_fragmented_preprocessor_class(node, self.source) {
1887                    let mut template_scope = scope.clone();
1888                    template_scope.template_signature =
1889                        cpp_template_signature(node, recovered.declaration_node, self.source);
1890                    template_scope.template_metadata =
1891                        cpp_template_metadata(node, recovered.class_node, self.source);
1892                    let raw_supertypes =
1893                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
1894                    let mut class_stack = Vec::new();
1895                    let class_unit = self.visit_named_class_like_shape(
1896                        recovered.class_node,
1897                        recovered.name,
1898                        Some(recovered.body),
1899                        true,
1900                        Some(recovered.range),
1901                        raw_supertypes,
1902                        &template_scope,
1903                        &mut class_stack,
1904                    );
1905                    self.parsed.record_materialization(
1906                        MaterializationRecord::RecoveredDeclaration {
1907                            recovery: recovered.range,
1908                            unit: class_unit.clone(),
1909                        },
1910                    );
1911                    let member_scope = ScopeInfo {
1912                        package_name: template_scope.package_name.clone(),
1913                        module: template_scope.module.clone(),
1914                        class_unit: Some(class_unit.clone()),
1915                        template_signature: template_scope.template_signature.clone(),
1916                        template_metadata: None,
1917                        declarations_are_fields: true,
1918                        recovered_specialization_member_scope: recovered
1919                            .class_node
1920                            .child_by_field_name("name")
1921                            .is_some_and(|name| name.kind() == "template_type"),
1922                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
1923                    };
1924                    for tail_member in recovered.tail_members.into_iter().rev() {
1925                        stack.push(CppWork::Node(CppNodeWork {
1926                            node: tail_member,
1927                            scope: member_scope.clone(),
1928                        }));
1929                    }
1930                    stack.extend(class_stack);
1931                    for sibling in recovered.member_siblings {
1932                        self.recovered_class_sibling_scopes
1933                            .insert(sibling.id(), member_scope.clone());
1934                    }
1935                    return;
1936                }
1937                for index in (0..node.named_child_count()).rev() {
1938                    let Some(child) = node.named_child(index) else {
1939                        continue;
1940                    };
1941                    if matches!(
1942                        child.kind(),
1943                        "class_specifier"
1944                            | "struct_specifier"
1945                            | "union_specifier"
1946                            | "enum_specifier"
1947                            | "function_definition"
1948                            | "declaration"
1949                            | "field_declaration"
1950                            | "alias_declaration"
1951                            | "namespace_definition"
1952                    ) {
1953                        let mut template_scope = scope.clone();
1954                        template_scope.template_signature =
1955                            cpp_template_signature(node, child, self.source);
1956                        template_scope.template_metadata =
1957                            cpp_template_metadata(node, child, self.source);
1958                        if let Some(recovered) =
1959                            recover_fragmented_partial_specialization(node, child, self.source)
1960                        {
1961                            let code_unit = self.visit_named_class_like_shape(
1962                                recovered.declaration_node,
1963                                recovered.name,
1964                                None,
1965                                true,
1966                                Some(recovered.range),
1967                                None,
1968                                &template_scope,
1969                                stack,
1970                            );
1971                            let mut member_scope = template_scope.clone();
1972                            member_scope.class_unit = Some(code_unit);
1973                            member_scope.declarations_are_fields = true;
1974                            member_scope.recovered_specialization_member_scope = true;
1975                            for prefix_member in recovered.prefix_members.into_iter().rev() {
1976                                stack.push(CppWork::Node(CppNodeWork {
1977                                    node: prefix_member,
1978                                    scope: member_scope.clone(),
1979                                }));
1980                            }
1981                            for sibling in recovered.member_siblings {
1982                                self.recovered_class_sibling_scopes
1983                                    .insert(sibling.id(), member_scope.clone());
1984                            }
1985                            for following in recovered.following_declarations.into_iter().rev() {
1986                                stack.push(CppWork::Node(CppNodeWork {
1987                                    node: following,
1988                                    scope: scope.clone(),
1989                                }));
1990                            }
1991                            return;
1992                        }
1993                        stack.push(CppWork::Node(CppNodeWork {
1994                            node: child,
1995                            scope: template_scope,
1996                        }));
1997                    }
1998                }
1999            }
2000            "namespace_definition" => self.visit_namespace(node, scope, stack),
2001            "linkage_specification" => {
2002                if let Some(body) = cpp_body_node(node) {
2003                    stack.push(CppWork::Container(CppContainer {
2004                        node: body,
2005                        scope: scope.clone(),
2006                    }));
2007                } else {
2008                    stack.push(CppWork::Container(CppContainer {
2009                        node,
2010                        scope: scope.clone(),
2011                    }));
2012                }
2013            }
2014            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
2015                self.visit_class_like(node, scope, stack)
2016            }
2017            "function_definition" => self.visit_function_definition(node, scope, stack),
2018            // A bare namespace-begin sentinel can make tree-sitter promote the
2019            // wrapped declaration to an ERROR node instead of the usual bogus
2020            // function_definition envelope. Keep the recovery entry point on
2021            // the same structured path for both shapes; ordinary ERROR nodes
2022            // retain their declaration-preserving wrapper traversal when the
2023            // sentinel predicate does not match.
2024            "ERROR" => {
2025                if !self.visit_sentinel_macro_region(node, scope, stack) {
2026                    stack.push(CppWork::Container(CppContainer {
2027                        node,
2028                        scope: scope.clone(),
2029                    }));
2030                }
2031            }
2032            "declaration" => {
2033                if scope.class_unit.is_some()
2034                    && scope.declarations_are_fields
2035                    && scope.recovered_specialization_member_scope
2036                    && let Some(alias_name) =
2037                        recovered_using_declaration_alias_name(node, self.source)
2038                {
2039                    self.add_type_aliases(node, scope, vec![alias_name]);
2040                } else {
2041                    self.visit_declaration(node, scope, scope.declarations_are_fields, stack)
2042                }
2043            }
2044            "field_declaration" => self.visit_declaration(node, scope, true, stack),
2045            "type_definition" | "alias_declaration" => {
2046                self.visit_type_declaration(node, scope, stack)
2047            }
2048            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
2049            "preproc_include" => self.visit_include(node),
2050            kind if preserves_declaration_scope_through_wrapper(
2051                kind,
2052                scope.class_unit.is_some(),
2053            ) =>
2054            {
2055                // A preprocessor conditional gates every declaration inside it
2056                // on a configuration this analyzer never evaluates; record the
2057                // interval so declaration state can say so (issue #1476). The
2058                // else/elif branches are children of the `preproc_if` node, so
2059                // recording the openers covers every branch.
2060                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
2061                    self.parsed.record_materialization(
2062                        MaterializationRecord::ConfigurationConditional {
2063                            range: cpp_declaration_range(node),
2064                        },
2065                    );
2066                }
2067                stack.push(CppWork::Container(CppContainer {
2068                    node,
2069                    scope: scope.clone(),
2070                }))
2071            }
2072            _ => {}
2073        }
2074    }
2075
2076    fn visit_namespace<'tree>(
2077        &mut self,
2078        node: Node<'tree>,
2079        scope: &ScopeInfo,
2080        stack: &mut Vec<CppWork<'tree>>,
2081    ) {
2082        let name_node = node.child_by_field_name("name");
2083        let Some(name_node) = name_node else {
2084            if let Some(body) = cpp_body_node(node) {
2085                stack.push(CppWork::Container(CppContainer {
2086                    node: body,
2087                    scope: scope.clone(),
2088                }));
2089            }
2090            return;
2091        };
2092        // Diagnostic corpora contain deliberately ill-formed global namespace
2093        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
2094        // the leading global `::` as the first anonymous child. Honor that AST
2095        // boundary instead of appending the name to the lexical namespace;
2096        // appending produced legacy names such as `outer::::outer::inner`, which
2097        // could not round-trip through the structured FqName boundary.
2098        let explicitly_global = name_node
2099            .child(0)
2100            .is_some_and(|child| !child.is_named() && child.kind() == "::");
2101        let components = cpp_namespace_name_components(name_node, self.source);
2102        if components.is_empty() {
2103            return;
2104        }
2105        // One Module per namespace level. C++17's `namespace a::b { ... }` is
2106        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
2107        // shorthand must declare `a` as well as `a::b` -- extracting only the
2108        // innermost level left the enclosing namespace undeclared and made the
2109        // two spellings of one construct disagree (issue #1878).
2110        let mut package_name = if explicitly_global {
2111            String::new()
2112        } else {
2113            scope.package_name.clone()
2114        };
2115        let mut module = None;
2116        for component in components {
2117            let full_name = if package_name.is_empty() {
2118                component
2119            } else {
2120                format!("{package_name}::{component}")
2121            };
2122            let level = CodeUnit::new_fq(
2123                self.file.clone(),
2124                CodeUnitType::Module,
2125                "",
2126                full_name.clone(),
2127                cpp_namespace_fq(&full_name),
2128            );
2129            if !self.parsed.contains_declaration(&level) {
2130                self.parsed
2131                    .add_code_unit(level.clone(), node, self.source, None, None);
2132            }
2133            package_name = full_name;
2134            module = Some(level);
2135        }
2136
2137        let namespace_scope = ScopeInfo {
2138            package_name,
2139            module,
2140            class_unit: scope.class_unit.clone(),
2141            template_signature: scope.template_signature.clone(),
2142            template_metadata: scope.template_metadata.clone(),
2143            declarations_are_fields: false,
2144            recovered_specialization_member_scope: false,
2145            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2146        };
2147        let container = cpp_body_node(node).unwrap_or(node);
2148        stack.push(CppWork::Container(CppContainer {
2149            node: container,
2150            scope: namespace_scope,
2151        }));
2152    }
2153
2154    fn visit_class_like<'tree>(
2155        &mut self,
2156        node: Node<'tree>,
2157        scope: &ScopeInfo,
2158        stack: &mut Vec<CppWork<'tree>>,
2159    ) {
2160        let Some(name) = class_like_name(node, self.source) else {
2161            return;
2162        };
2163        self.visit_named_class_like(node, name, scope, stack);
2164    }
2165
2166    fn visit_named_class_like<'tree>(
2167        &mut self,
2168        node: Node<'tree>,
2169        name: String,
2170        scope: &ScopeInfo,
2171        stack: &mut Vec<CppWork<'tree>>,
2172    ) {
2173        let body = cpp_body_node(node);
2174        let definition_body_present = body.is_some();
2175        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
2176            .then(|| extract_cpp_supertypes(node, self.source));
2177        self.visit_named_class_like_shape(
2178            node,
2179            name,
2180            body,
2181            definition_body_present,
2182            None,
2183            raw_supertypes,
2184            scope,
2185            stack,
2186        );
2187    }
2188
2189    #[allow(clippy::too_many_arguments)]
2190    fn visit_named_class_like_shape<'tree>(
2191        &mut self,
2192        declaration_node: Node<'tree>,
2193        name: String,
2194        body: Option<Node<'tree>>,
2195        definition_body_present: bool,
2196        explicit_range: Option<Range>,
2197        raw_supertypes: Option<Vec<String>>,
2198        scope: &ScopeInfo,
2199        stack: &mut Vec<CppWork<'tree>>,
2200    ) -> CodeUnit {
2201        let displaced_macro_tail = if explicit_range.is_none() {
2202            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
2203        } else {
2204            None
2205        };
2206        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
2207        let recovered_scope = self.scope_for_recovered_exported_class(
2208            declaration_node,
2209            &name,
2210            definition_body_present,
2211            scope,
2212        );
2213        let scope = &recovered_scope;
2214        let short_name = if let Some(parent) = &scope.class_unit {
2215            format!("{}${name}", parent.short_name())
2216        } else {
2217            name
2218        };
2219        let fq = cpp_class_fq(&scope.package_name, &short_name);
2220        let code_unit = CodeUnit::with_signature_and_fq(
2221            self.file.clone(),
2222            CodeUnitType::Class,
2223            scope.package_name.clone(),
2224            short_name,
2225            scope.template_signature.clone(),
2226            false,
2227            fq,
2228        );
2229        let has_body = definition_body_present;
2230        if !has_body && self.parsed.contains_declaration(&code_unit) {
2231            self.parsed.record_navigation_range(
2232                code_unit.clone(),
2233                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
2234            );
2235            return code_unit;
2236        }
2237        if has_body {
2238            if let Some(range) = explicit_range {
2239                self.parsed
2240                    .replace_code_unit_with_range(code_unit.clone(), range, None, None);
2241            } else {
2242                self.parsed.replace_code_unit(
2243                    code_unit.clone(),
2244                    declaration_node,
2245                    self.source,
2246                    None,
2247                    None,
2248                );
2249            }
2250        } else {
2251            self.parsed
2252                .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
2253        }
2254        if let Some(raw_supertypes) = raw_supertypes {
2255            self.parsed
2256                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
2257        }
2258        self.parsed.add_signature(
2259            code_unit.clone(),
2260            render_cpp_type_signature(
2261                declaration_node,
2262                self.source,
2263                scope.template_signature.as_deref(),
2264            ),
2265        );
2266        if let Some(metadata) = &scope.template_metadata {
2267            let primary_short_name = if let Some(parent) = &scope.class_unit {
2268                format!("{}${}", parent.short_name(), metadata.primary_name)
2269            } else {
2270                metadata.primary_name.clone()
2271            };
2272            let primary_fq_name = CodeUnit::new(
2273                self.file.clone(),
2274                CodeUnitType::Class,
2275                scope.package_name.clone(),
2276                primary_short_name,
2277            )
2278            .fq_name();
2279            let mut metadata = metadata.clone();
2280            metadata.primary_fq_name = primary_fq_name;
2281            self.parsed
2282                .set_cpp_template_metadata(code_unit.clone(), metadata);
2283        }
2284        if let Some(parent) = &scope.class_unit {
2285            self.parsed.add_child(parent.clone(), code_unit.clone());
2286        } else if let Some(module) = &scope.module {
2287            self.parsed.add_child(module.clone(), code_unit.clone());
2288        }
2289
2290        if let Some(body) = body {
2291            let mut nested_scope = scope.clone();
2292            nested_scope.class_unit = Some(code_unit.clone());
2293            nested_scope.template_signature = scope.template_signature.clone();
2294            // Template metadata describes the class just created. It must not
2295            // leak into ordinary nested declarations in that class's body.
2296            // Recovered export-macro specializations carry a separate scope bit
2297            // for their declaration-shaped body members.
2298            nested_scope.template_metadata = None;
2299            // Export-macro class bodies recovered from a function_definition use
2300            // compound_statement children, whose direct fields are declarations.
2301            nested_scope.recovered_specialization_member_scope =
2302                scope.template_metadata.as_ref().is_some_and(|metadata| {
2303                    declaration_node.kind() == "function_definition"
2304                        && !metadata.specialization_arguments.is_empty()
2305                });
2306            nested_scope.declarations_are_fields =
2307                is_recovered_exported_class_container(declaration_node, self.source)
2308                    || nested_scope.recovered_specialization_member_scope;
2309            if let Some(displaced) = displaced_macro_tail {
2310                // A macro-shaped field without a source semicolon can make
2311                // tree-sitter consume the real class terminator as an ERROR
2312                // inside that field, then retain following namespace items as
2313                // later field-list children. Drain the proven class prefix
2314                // first and re-own only the structured tail with the outer
2315                // scope. The tail is pushed first because the work stack is
2316                // LIFO.
2317                stack.push(CppWork::Siblings(CppSiblingsWork {
2318                    parent: body,
2319                    next_index: displaced.split_index,
2320                    end_index: usize::MAX,
2321                    scope: scope.clone(),
2322                }));
2323                stack.push(CppWork::Siblings(CppSiblingsWork {
2324                    parent: body,
2325                    next_index: 0,
2326                    end_index: displaced.split_index,
2327                    scope: nested_scope,
2328                }));
2329            } else {
2330                stack.push(CppWork::Container(CppContainer {
2331                    node: body,
2332                    scope: nested_scope,
2333                }));
2334            }
2335        }
2336        if declaration_node.kind() == "enum_specifier" {
2337            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
2338            if !self.has_enum_enumerator_units(&code_unit) {
2339                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
2340            }
2341        }
2342        code_unit
2343    }
2344
2345    fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
2346        let prefix = format!("{}.", parent.short_name());
2347        self.parsed.declarations().iter().any(|unit| {
2348            unit.kind() == CodeUnitType::Field
2349                && unit.source() == parent.source()
2350                && unit.package_name() == parent.package_name()
2351                && unit.short_name().starts_with(&prefix)
2352        })
2353    }
2354
2355    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
2356        walk_named_tree_preorder(node, false, |child| {
2357            if child.kind() != "enumerator" {
2358                return WalkControl::Continue;
2359            }
2360            let Some(name_node) = child.child_by_field_name("name") else {
2361                return WalkControl::Continue;
2362            };
2363            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
2364            if name.is_empty() {
2365                return WalkControl::Continue;
2366            }
2367            let code_unit = CodeUnit::new_fq(
2368                self.file.clone(),
2369                CodeUnitType::Field,
2370                scope.package_name.clone(),
2371                format!("{}.{}", parent.short_name(), name),
2372                parent
2373                    .fq()
2374                    .clone()
2375                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
2376            );
2377            if self.parsed.contains_declaration(&code_unit) {
2378                return WalkControl::Continue;
2379            }
2380            self.parsed.add_code_unit(
2381                code_unit.clone(),
2382                child,
2383                self.source,
2384                Some(parent.clone()),
2385                None,
2386            );
2387            self.parsed.add_signature(
2388                code_unit,
2389                normalize_cpp_whitespace(node_text(child, self.source)),
2390            );
2391            WalkControl::Continue
2392        });
2393    }
2394
2395    fn visit_enum_enumerators_from_text(
2396        &mut self,
2397        node: Node<'_>,
2398        scope: &ScopeInfo,
2399        parent: &CodeUnit,
2400    ) {
2401        let text = node_text(node, self.source);
2402        let Some((_, body)) = text.split_once('{') else {
2403            return;
2404        };
2405        let Some((body, _)) = body.rsplit_once('}') else {
2406            return;
2407        };
2408        for entry in body.split(',') {
2409            let trimmed = entry.trim();
2410            let name = trimmed
2411                .split('=')
2412                .next()
2413                .unwrap_or("")
2414                .split_whitespace()
2415                .next()
2416                .unwrap_or("");
2417            if name.is_empty() {
2418                continue;
2419            }
2420            let code_unit = CodeUnit::new_fq(
2421                self.file.clone(),
2422                CodeUnitType::Field,
2423                scope.package_name.clone(),
2424                format!("{}.{}", parent.short_name(), name),
2425                parent
2426                    .fq()
2427                    .clone()
2428                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
2429            );
2430            if self.parsed.contains_declaration(&code_unit) {
2431                continue;
2432            }
2433            self.parsed.add_code_unit(
2434                code_unit.clone(),
2435                node,
2436                self.source,
2437                Some(parent.clone()),
2438                None,
2439            );
2440            self.parsed.add_signature(code_unit, trimmed.to_string());
2441        }
2442    }
2443
2444    fn visit_function_definition<'tree>(
2445        &mut self,
2446        node: Node<'tree>,
2447        scope: &ScopeInfo,
2448        stack: &mut Vec<CppWork<'tree>>,
2449    ) {
2450        // A file-scope object-like macro sentinel the parser cannot see (issue
2451        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
2452        // prefixes as a bogus `function_definition` that swallows real namespaces,
2453        // classes, and members. Reparse the swallowed interior as C++ items so the
2454        // ordinary declaration visitors index it with byte/line-exact ownership.
2455        if self.visit_sentinel_macro_region(node, scope, stack) {
2456            return;
2457        }
2458        if let Some((class_node, name, raw_supertypes)) =
2459            recover_exported_class_function_definition(node, self.source)
2460        {
2461            let body = cpp_body_node(class_node);
2462            let fragmented = cpp_body_node(node)
2463                .and_then(|body| fragmented_export_function_body_region(node, body, self.source));
2464            // The recovery tuple's first node is the class-like type when the
2465            // parser exposes one, but the synthetic wrapper owns the compound
2466            // statement that contains the truncated class body. Use the
2467            // wrapper body for fragmented-member detection; retain the
2468            // class-node body for the ordinary (non-fragmented) path below.
2469            if let Some(fragmented) = fragmented {
2470                // The lifted sibling no longer sits below the parser-visible
2471                // namespace node. Restore the current parent scope when the
2472                // ordinary work walk reaches that class.
2473                if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
2474                    .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
2475                {
2476                    let mut boundary_scope = scope.clone();
2477                    for sibling in cpp_following_named_siblings(node, self.source) {
2478                        if sibling.start_byte() >= boundary.start_byte() {
2479                            break;
2480                        }
2481                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
2482                            boundary_scope.visible_using_namespaces.push(namespace);
2483                        }
2484                    }
2485                    self.recovered_class_sibling_scopes
2486                        .insert(boundary.id(), boundary_scope);
2487                }
2488                let mut recovered_constructor = None;
2489                let mut recovered_prefix_tree = None;
2490                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
2491                {
2492                    Some(FragmentedExportMembers::Complete(tree)) => {
2493                        if let Some(body) = body
2494                            && let Some(range) =
2495                                cpp_reparsed_synthetic_initializer_constructor_range(
2496                                    tree.root_node(),
2497                                    &name,
2498                                    self.source,
2499                                    body.end_byte(),
2500                                )
2501                        {
2502                            recovered_constructor = Some(range);
2503                            recovered_prefix_tree = Some(tree);
2504                            None
2505                        } else {
2506                            Some(FragmentedExportMembers::Complete(tree))
2507                        }
2508                    }
2509                    outcome => outcome,
2510                };
2511                let mut class_stack = Vec::new();
2512                let class_unit = self.visit_named_class_like_shape(
2513                    class_node,
2514                    name,
2515                    None,
2516                    true,
2517                    Some(fragmented.class_range),
2518                    raw_supertypes,
2519                    scope,
2520                    &mut class_stack,
2521                );
2522                self.parsed
2523                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
2524                        recovery: fragmented.class_range,
2525                        unit: class_unit.clone(),
2526                    });
2527                let complete = outcome.is_some_and(|outcome| {
2528                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
2529                });
2530                if complete {
2531                    self.consumed_fragment_regions
2532                        .push((node.start_byte(), fragmented.class_range.end_byte));
2533                } else {
2534                    // The reparse can fail when the first constructor or a
2535                    // method body is split into statement-shaped siblings.
2536                    // Keep the recovered class envelope, but do not visit the
2537                    // synthetic wrapper body: its initializer expressions can
2538                    // look like same-named member functions (for example
2539                    // `Token.location(loc)`). Re-own only the original sibling
2540                    // nodes that fall inside the proven class range. Their CST
2541                    // shapes retain the real field/function kinds and ranges.
2542                    let member_scope = ScopeInfo {
2543                        package_name: class_unit.package_name().to_string(),
2544                        module: scope.module.clone(),
2545                        class_unit: Some(class_unit.clone()),
2546                        template_signature: scope.template_signature.clone(),
2547                        template_metadata: None,
2548                        declarations_are_fields: true,
2549                        recovered_specialization_member_scope: false,
2550                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
2551                    };
2552                    for candidate in cpp_following_named_siblings(node, self.source) {
2553                        if candidate.start_byte() >= fragmented.reparse_end {
2554                            break;
2555                        }
2556                        if cpp_fragment_sibling_is_class_member(
2557                            candidate,
2558                            fragmented.reparse_end,
2559                            self.source,
2560                        ) {
2561                            self.recovered_class_sibling_scopes
2562                                .insert(candidate.id(), member_scope.clone());
2563                        }
2564                    }
2565                    if let Some(range) = recovered_constructor
2566                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
2567                    {
2568                        self.visit_recovered_fragment_prefix_members(
2569                            prefix_tree.root_node(),
2570                            range.start,
2571                            &class_unit,
2572                            scope,
2573                        );
2574                        self.visit_recovered_fragment_constructor(
2575                            range,
2576                            body,
2577                            class_node,
2578                            &class_unit,
2579                            scope,
2580                        );
2581                    }
2582                }
2583                stack.extend(class_stack);
2584                return;
2585            }
2586            let mut stack = Vec::new();
2587            let class_unit = self.visit_named_class_like_shape(
2588                class_node,
2589                name,
2590                body,
2591                body.is_some(),
2592                None,
2593                raw_supertypes,
2594                scope,
2595                &mut stack,
2596            );
2597            self.parsed
2598                .record_materialization(MaterializationRecord::RecoveredDeclaration {
2599                    recovery: cpp_declaration_range(node),
2600                    unit: class_unit,
2601                });
2602            // Issue #1524: the bogus `function_definition` body can run past
2603            // the class's true closing brace (the parse ends it with a
2604            // zero-width `MISSING "}"`), swallowing following namespace-scope
2605            // siblings -- they would index as members of the recovered class.
2606            // When the body's text-balanced close lands before the body's own
2607            // end, re-own the swallowed tail with the outer scope instead.
2608            if let Some(body) = body
2609                && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
2610                && class_close < body.end_byte()
2611            {
2612                let split = {
2613                    let mut cursor = body.walk();
2614                    body.named_children(&mut cursor)
2615                        .position(|child| child.start_byte() > class_close)
2616                };
2617                if let Some(split) = split {
2618                    // The seeded work is a single Container over the whole
2619                    // body with the class scope; replace it with the bounded
2620                    // head (class scope) plus the swallowed tail (outer
2621                    // scope). Push tail first so the head drains first.
2622                    let seeded = stack.pop();
2623                    match seeded {
2624                        Some(CppWork::Container(container)) => {
2625                            stack.push(CppWork::Siblings(CppSiblingsWork {
2626                                parent: body,
2627                                next_index: split,
2628                                end_index: usize::MAX,
2629                                scope: scope.clone(),
2630                            }));
2631                            stack.push(CppWork::Siblings(CppSiblingsWork {
2632                                parent: body,
2633                                next_index: 0,
2634                                end_index: split,
2635                                scope: container.scope,
2636                            }));
2637                        }
2638                        // visit_named_class_like_shape always seeds exactly
2639                        // one Container when a body is present.
2640                        _ => unreachable!("exported-class seed is always one Container"),
2641                    }
2642                }
2643            }
2644            while let Some(work) = stack.pop() {
2645                match work {
2646                    CppWork::Container(container) => {
2647                        push_cpp_container_work(container.node, container.scope, &mut stack);
2648                    }
2649                    CppWork::Siblings(siblings) => {
2650                        advance_cpp_siblings(siblings, self.source, &mut stack);
2651                    }
2652                    CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
2653                }
2654            }
2655            return;
2656        }
2657        let recovered_constraint_constructor =
2658            cpp_recovered_template_macro_constructor(node, self.source);
2659        let declarator = recovered_constraint_constructor
2660            .map(|(declarator, _)| declarator)
2661            .or_else(|| node.child_by_field_name("declarator"));
2662        let Some(declarator) = declarator else {
2663            self.visit_malformed_function_definition_container(node, scope, stack);
2664            return;
2665        };
2666        let Some(function_declarator) = extract_function_declarator(declarator) else {
2667            self.visit_malformed_function_definition_container(node, scope, stack);
2668            return;
2669        };
2670        let Some(mut function) = extract_function_info(function_declarator, self.source, scope)
2671        else {
2672            self.visit_malformed_function_definition_container(node, scope, stack);
2673            return;
2674        };
2675        if let Some((_, template_parameter)) = recovered_constraint_constructor {
2676            function.signature = format!(
2677                "template <{}>{}",
2678                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
2679                function.signature
2680            );
2681        }
2682        let code_unit = function.code_unit(self.file.clone());
2683        // Keep an earlier same-file prototype as another physical occurrence
2684        // of this callable. `CodeUnit` already identifies the role-neutral
2685        // overload, while ranges and signature metadata describe its
2686        // declaration/definition occurrences.
2687        self.parsed
2688            .add_code_unit(code_unit.clone(), node, self.source, None, None);
2689        let signature = if recovered_constraint_constructor.is_some() {
2690            normalize_cpp_whitespace(node_text(function_declarator, self.source))
2691        } else {
2692            render_cpp_function_display_signature_from_node(
2693                node,
2694                self.source,
2695                scope.template_signature.as_deref(),
2696                true,
2697            )
2698        };
2699        self.parsed.add_signature_with_metadata(
2700            code_unit.clone(),
2701            cpp_signature_metadata(signature, function_declarator, self.source)
2702                .with_declaration_only(false)
2703                .with_callable_linkage(cpp_callable_linkage(node, self.source)),
2704        );
2705        if let Some(parent) = &scope.class_unit {
2706            self.parsed.add_child(parent.clone(), code_unit);
2707        } else if let Some(module) = &scope.module {
2708            self.parsed.add_child(module.clone(), code_unit);
2709        }
2710    }
2711
2712    /// Recover the namespace lost when tree-sitter promotes an export-macro
2713    /// class definition to a root-level `function_definition`.  Only a
2714    /// body-bearing, top-level recovery may borrow a namespace, and only when
2715    /// one earlier namespace-scope forward declaration proves the identity.
2716    fn scope_for_recovered_exported_class(
2717        &self,
2718        node: Node<'_>,
2719        name: &str,
2720        definition_body_present: bool,
2721        scope: &ScopeInfo,
2722    ) -> ScopeInfo {
2723        if !definition_body_present
2724            || !scope.package_name.is_empty()
2725            || scope.class_unit.is_some()
2726            || !(is_recovered_exported_class_container(node, self.source)
2727                || matches!(node.kind(), "declaration" | "field_declaration")
2728                    && recover_exported_class_declaration(node, self.source).is_some()
2729                || matches!(
2730                    node.kind(),
2731                    "class_specifier" | "struct_specifier" | "union_specifier"
2732                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
2733                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
2734                        name_node,
2735                        self.source,
2736                    )))
2737                }) || node.parent().is_some_and(|parent| {
2738                    matches!(parent.kind(), "declaration" | "field_declaration")
2739                        && recover_exported_class_declaration(parent, self.source).is_some()
2740                        || is_recovered_exported_class_container(parent, self.source)
2741                })) && class_like_name(node, self.source).as_deref() == Some(name))
2742        {
2743            return scope.clone();
2744        }
2745        let Some(package_name) = unique_earlier_cpp_namespace_forward(node, name, self.source)
2746        else {
2747            return scope.clone();
2748        };
2749
2750        let module = CodeUnit::new_fq(
2751            self.file.clone(),
2752            CodeUnitType::Module,
2753            "",
2754            package_name.clone(),
2755            cpp_namespace_fq(&package_name),
2756        );
2757        let mut recovered = scope.clone();
2758        recovered.package_name = package_name;
2759        recovered.module = Some(module);
2760        recovered
2761    }
2762
2763    fn visit_malformed_function_definition_container<'tree>(
2764        &mut self,
2765        node: Node<'tree>,
2766        scope: &ScopeInfo,
2767        stack: &mut Vec<CppWork<'tree>>,
2768    ) {
2769        let Some(body) = cpp_body_node(node) else {
2770            return;
2771        };
2772        if !cpp_contains_namespace_definition(body) {
2773            return;
2774        }
2775        stack.push(CppWork::Container(CppContainer {
2776            node: body,
2777            scope: scope.clone(),
2778        }));
2779    }
2780
2781    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
2782    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
2783    /// emits for a sentinel-prefixed region, reparse the interior after the
2784    /// sentinel identifier as real C++ items -- confined to the region so
2785    /// every reparsed node keeps its original byte/line position -- and run the
2786    /// ordinary container visitation over the result. Returns `true` when it fired
2787    /// (the caller must then skip normal function processing). Nested sentinel
2788    /// regions recover recursively: the reparsed interior is walked through the
2789    /// same `visit_function_definition` path, so a sentinel inside the region hits
2790    /// this recovery again.
2791    fn visit_sentinel_macro_region<'tree>(
2792        &mut self,
2793        node: Node<'tree>,
2794        scope: &ScopeInfo,
2795        stack: &mut Vec<CppWork<'tree>>,
2796    ) -> bool {
2797        if self.visit_nested_namespace_sentinel(node, scope) {
2798            return true;
2799        }
2800        if let Some((
2801            reparse_start,
2802            class_start,
2803            body_start,
2804            class_close_start,
2805            class_close_end,
2806            class_close_line,
2807        )) = cpp_sentinel_macro_class_region(node, self.source)
2808        {
2809            let Some(class_tree) =
2810                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
2811            else {
2812                return false;
2813            };
2814            let class_root = class_tree.root_node();
2815            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
2816            let Some(reparsed_class) =
2817                cpp_sentinel_reparsed_class(class_root, template_node, self.source)
2818            else {
2819                return false;
2820            };
2821            let class_node = reparsed_class.declaration_node;
2822            let name = reparsed_class.name;
2823            let mut class_scope = scope.clone();
2824            if let Some(template_node) = template_node {
2825                class_scope.template_signature =
2826                    cpp_template_signature(template_node, class_node, self.source);
2827                class_scope.template_metadata =
2828                    cpp_template_metadata(template_node, class_node, self.source);
2829            }
2830            let Some(body_tree) =
2831                cpp_reparse_region_items(self.source, body_start, class_close_start)
2832            else {
2833                return false;
2834            };
2835            let raw_supertypes = reparsed_class.raw_supertypes;
2836            let class_range = Range {
2837                start_byte: class_start,
2838                end_byte: class_close_end,
2839                start_line: class_node.start_position().row + 1,
2840                end_line: class_close_line,
2841            };
2842            let class_scope =
2843                self.scope_for_recovered_exported_class(class_node, &name, true, &class_scope);
2844            let mut class_stack = Vec::new();
2845            let class_unit = self.visit_named_class_like_shape(
2846                class_node,
2847                name,
2848                None,
2849                true,
2850                Some(class_range),
2851                raw_supertypes,
2852                &class_scope,
2853                &mut class_stack,
2854            );
2855            let member_scope = ScopeInfo {
2856                package_name: class_scope.package_name.clone(),
2857                module: class_scope.module.clone(),
2858                class_unit: Some(class_unit),
2859                template_signature: class_scope.template_signature.clone(),
2860                template_metadata: None,
2861                declarations_are_fields: true,
2862                recovered_specialization_member_scope: false,
2863                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
2864            };
2865            self.run_container_work(body_tree.root_node(), member_scope);
2866            // Register only after the padded body reparse: its nodes deliberately
2867            // retain offsets inside the consumed region and must be visited first.
2868            self.consumed_fragment_regions
2869                .push((node.start_byte(), class_close_end));
2870            // An ERROR envelope can hold real sibling declarations after the
2871            // recovered class's close (the suffix-reparse boundary in
2872            // `cpp_sentinel_macro_class_region` partitions, it does not
2873            // consume). Walk the envelope's remaining children normally; the
2874            // consumed region above keeps the recovered class from being
2875            // indexed twice.
2876            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
2877                stack.push(CppWork::Container(CppContainer {
2878                    node,
2879                    scope: scope.clone(),
2880                }));
2881            }
2882            return true;
2883        }
2884        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
2885            return false;
2886        };
2887        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
2888            return false;
2889        };
2890        let root = tree.root_node();
2891        if !cpp_reparsed_items_are_indexable(root, self.source) {
2892            return false;
2893        }
2894        self.visit_container(
2895            root,
2896            &scope.package_name,
2897            scope.module.clone(),
2898            scope.class_unit.clone(),
2899            scope.template_signature.clone(),
2900            scope.visible_using_namespaces.clone(),
2901        );
2902        if end > node.end_byte() {
2903            self.consumed_fragment_regions
2904                .push((node.start_byte(), end));
2905        } else if node.kind() == "ERROR" && node.end_byte() > end {
2906            // The sentinel region ended at the first recovered class-like item
2907            // but the ERROR envelope keeps real sibling declarations after it
2908            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
2909            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
2910            // envelope's remaining children normally; the consumed region
2911            // keeps the reparsed prefix from being indexed twice.
2912            self.consumed_fragment_regions
2913                .push((node.start_byte(), end));
2914            stack.push(CppWork::Container(CppContainer {
2915                node,
2916                scope: scope.clone(),
2917            }));
2918        }
2919        true
2920    }
2921
2922    /// Re-own complete class declarations from the structured Abseil
2923    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
2924    /// its direct CST children already prove both namespace components and the
2925    /// class bodies, so the ordinary class/member visitor can retain ownership
2926    /// and exact source ranges without admitting unrelated callable bodies.
2927    fn visit_nested_namespace_sentinel(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
2928        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source) else {
2929            return false;
2930        };
2931
2932        let mut package_name = scope.package_name.clone();
2933        let mut module = scope.module.clone();
2934        for component in recovered.namespace_components {
2935            package_name = if package_name.is_empty() {
2936                component
2937            } else {
2938                format!("{package_name}::{component}")
2939            };
2940            let namespace_module = CodeUnit::new_fq(
2941                self.file.clone(),
2942                CodeUnitType::Module,
2943                "",
2944                package_name.clone(),
2945                cpp_namespace_fq(&package_name),
2946            );
2947            if !self.parsed.contains_declaration(&namespace_module) {
2948                self.parsed.add_code_unit(
2949                    namespace_module.clone(),
2950                    recovered.function,
2951                    self.source,
2952                    None,
2953                    None,
2954                );
2955            }
2956            module = Some(namespace_module);
2957        }
2958
2959        let recovered_scope = ScopeInfo {
2960            package_name,
2961            module,
2962            class_unit: scope.class_unit.clone(),
2963            template_signature: scope.template_signature.clone(),
2964            template_metadata: scope.template_metadata.clone(),
2965            declarations_are_fields: false,
2966            recovered_specialization_member_scope: false,
2967            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2968        };
2969        if let Some(fragmented) =
2970            cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, self.source)
2971        {
2972            let mut class_scope = recovered_scope.clone();
2973            if let Some(template_node) = fragmented.template_node {
2974                class_scope.template_signature =
2975                    cpp_template_signature(template_node, fragmented.class_node, self.source);
2976                class_scope.template_metadata =
2977                    cpp_template_metadata(template_node, fragmented.class_node, self.source);
2978            }
2979            let raw_supertypes = matches!(
2980                fragmented.class_node.kind(),
2981                "class_specifier" | "struct_specifier"
2982            )
2983            .then(|| extract_cpp_supertypes(fragmented.class_node, self.source));
2984            if let Some(outcome) = self
2985                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
2986            {
2987                let mut class_stack = Vec::new();
2988                let class_unit = self.visit_named_class_like_shape(
2989                    fragmented.class_node,
2990                    fragmented.name.clone(),
2991                    None,
2992                    true,
2993                    Some(fragmented.fragmented.class_range),
2994                    raw_supertypes,
2995                    &class_scope,
2996                    &mut class_stack,
2997                );
2998                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
2999                    self.consumed_fragment_regions.push((
3000                        fragmented.consumed_start,
3001                        fragmented.fragmented.class_range.end_byte,
3002                    ));
3003                }
3004            }
3005        }
3006        // The class requirement above is the admission gate; once admitted,
3007        // traverse the whole proven inner namespace body so sibling aliases,
3008        // functions, and variables are not silently discarded.
3009        self.run_container_work(recovered.body, recovered_scope);
3010        true
3011    }
3012
3013    fn visit_declaration<'tree>(
3014        &mut self,
3015        node: Node<'tree>,
3016        scope: &ScopeInfo,
3017        in_class_body: bool,
3018        stack: &mut Vec<CppWork<'tree>>,
3019    ) {
3020        if self.visit_sentinel_macro_region(node, scope, stack) {
3021            return;
3022        }
3023        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
3024            !cpp_active_template_type_parameter(
3025                node,
3026                node_text(declarator, self.source),
3027                self.source,
3028            )
3029        }) {
3030            return;
3031        }
3032        if in_class_body
3033            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
3034        {
3035            self.visit_recovered_macro_qualified_function_declaration(node, call, scope);
3036            return;
3037        }
3038        if in_class_body
3039            && let Some(declarators) =
3040                recovered_macro_qualified_field_declarators(node, self.source)
3041        {
3042            for declarator in declarators {
3043                self.visit_variable_declaration(node, declarator, scope, true);
3044            }
3045            return;
3046        }
3047        let recovered_alias_names = recovered_type_alias_names(node, self.source);
3048        if !recovered_alias_names.is_empty() {
3049            self.add_type_aliases(node, scope, recovered_alias_names);
3050            return;
3051        }
3052
3053        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
3054            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
3055                // Issue #938: the members tree-sitter scattered out of the fragmented
3056                // multiple-base export node are reparsed from their true body region
3057                // and re-owned as members of the recovered class, with an explicit
3058                // navigation range spanning to the displaced closing brace.
3059                if let Some(outcome) =
3060                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
3061                {
3062                    let consumed_region = (
3063                        recovered.declaration_node.end_byte(),
3064                        fragmented.class_range.end_byte,
3065                    );
3066                    let code_unit = self.visit_named_class_like_shape(
3067                        recovered.declaration_node,
3068                        recovered.name,
3069                        None,
3070                        true,
3071                        Some(fragmented.class_range),
3072                        recovered.raw_supertypes,
3073                        scope,
3074                        stack,
3075                    );
3076                    let consume_fragment =
3077                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
3078                    // Everything between the fragmented declaration and its displaced
3079                    // closing brace now belongs to the recovered class; keep the
3080                    // ordinary walk from re-indexing those scattered siblings at top
3081                    // level. Register the consumed region only after indexing because
3082                    // the reparsed nodes retain byte offsets inside that same region.
3083                    if consume_fragment {
3084                        self.consumed_fragment_regions.push(consumed_region);
3085                    }
3086                    return;
3087                }
3088            }
3089            let uses_initializer_body = recovered.uses_initializer_body;
3090            let definition_body_present = recovered.body.is_some();
3091            self.visit_named_class_like_shape(
3092                recovered.declaration_node,
3093                recovered.name,
3094                recovered.body,
3095                definition_body_present,
3096                None,
3097                recovered.raw_supertypes,
3098                scope,
3099                stack,
3100            );
3101            if uses_initializer_body {
3102                return;
3103            }
3104        }
3105
3106        let mut handled_function = false;
3107        let mut handled_declarator = false;
3108        let mut cursor = node.walk();
3109        for child in node.named_children(&mut cursor) {
3110            if matches!(
3111                child.kind(),
3112                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3113            ) {
3114                // A named class-like definition remains a declaration even when
3115                // the same statement also declares an object, for example
3116                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
3117                // declaration's type and `kind` as its declarator.  Dropping the
3118                // type here loses both its nested owner and every later lexical
3119                // reference to it.  A body is the structured proof that this is
3120                // a definition rather than an elaborated type use such as
3121                // `class Kind value;`.
3122                if cpp_body_node(child).is_some() {
3123                    self.visit_class_like(child, scope, stack);
3124                }
3125                continue;
3126            }
3127        }
3128
3129        let mut cursor = node.walk();
3130        for child in node.children_by_field_name("declarator", &mut cursor) {
3131            if crate::structural::is_recovered_designator_init_declarator(child) {
3132                handled_declarator = true;
3133                continue;
3134            }
3135            if let Some(kind) = classify_declarator(child) {
3136                handled_declarator = true;
3137                match kind {
3138                    DeclaratorKind::Function(function_declarator) => {
3139                        handled_function = true;
3140                        self.visit_function_declaration(node, function_declarator, scope);
3141                    }
3142                    DeclaratorKind::Variable(variable_declarator) => {
3143                        self.visit_variable_declaration(
3144                            node,
3145                            variable_declarator,
3146                            scope,
3147                            in_class_body,
3148                        );
3149                    }
3150                }
3151            }
3152        }
3153
3154        if !handled_declarator {
3155            let mut cursor = node.walk();
3156            for child in node.named_children(&mut cursor) {
3157                if crate::structural::is_recovered_designator_init_declarator(child) {
3158                    handled_declarator = true;
3159                    continue;
3160                }
3161                if !is_unfielded_declarator_candidate(child) {
3162                    continue;
3163                }
3164                let Some(kind) = classify_declarator(child) else {
3165                    continue;
3166                };
3167                handled_declarator = true;
3168                match kind {
3169                    DeclaratorKind::Function(function_declarator) => {
3170                        handled_function = true;
3171                        self.visit_function_declaration(node, function_declarator, scope);
3172                    }
3173                    DeclaratorKind::Variable(variable_declarator) => {
3174                        self.visit_variable_declaration(
3175                            node,
3176                            variable_declarator,
3177                            scope,
3178                            in_class_body,
3179                        );
3180                    }
3181                }
3182            }
3183        }
3184
3185        if handled_function {
3186            return;
3187        }
3188
3189        if !handled_declarator {
3190            if in_class_body {
3191                self.visit_class_members_from_declaration(node, scope);
3192            } else {
3193                self.visit_global_variables_from_declaration(node, scope);
3194            }
3195        }
3196    }
3197
3198    fn visit_function_declaration(
3199        &mut self,
3200        declaration_node: Node<'_>,
3201        declarator: Node<'_>,
3202        scope: &ScopeInfo,
3203    ) {
3204        let Some(function) = extract_function_info(declarator, self.source, scope) else {
3205            return;
3206        };
3207        let code_unit =
3208            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
3209        if self.parsed.contains_declaration(&code_unit) {
3210            self.parsed
3211                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3212            return;
3213        }
3214        self.parsed
3215            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3216        let signature = render_cpp_function_display_signature_from_node(
3217            declaration_node,
3218            self.source,
3219            scope.template_signature.as_deref(),
3220            false,
3221        );
3222        self.parsed.add_signature_with_metadata(
3223            code_unit.clone(),
3224            cpp_signature_metadata(signature, declarator, self.source)
3225                .with_declaration_only(true)
3226                .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source)),
3227        );
3228        if let Some(parent) = &scope.class_unit {
3229            self.parsed.add_child(parent.clone(), code_unit);
3230        } else if let Some(module) = &scope.module {
3231            self.parsed.add_child(module.clone(), code_unit);
3232        }
3233    }
3234
3235    fn visit_recovered_macro_qualified_function_declaration(
3236        &mut self,
3237        declaration_node: Node<'_>,
3238        call: Node<'_>,
3239        scope: &ScopeInfo,
3240    ) {
3241        let Some(parent) = &scope.class_unit else {
3242            return;
3243        };
3244        let Some(name_node) = call.child_by_field_name("function") else {
3245            return;
3246        };
3247        let Some(arguments) = call.child_by_field_name("arguments") else {
3248            return;
3249        };
3250        let Some((signature, parameter_labels)) =
3251            recovered_macro_qualified_function_parameters(arguments, self.source)
3252        else {
3253            return;
3254        };
3255        let arity = parameter_labels.len();
3256        let function = FunctionInfo {
3257            package_name: scope.package_name.clone(),
3258            owner_path: Some(parent.short_name().to_string()),
3259            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
3260            signature,
3261        };
3262        if function.name.is_empty() {
3263            return;
3264        }
3265        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3266        if self.parsed.contains_declaration(&code_unit) {
3267            self.parsed
3268                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3269            return;
3270        }
3271        self.parsed
3272            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3273        let signature_label = render_cpp_function_display_signature_from_node(
3274            declaration_node,
3275            self.source,
3276            scope.template_signature.as_deref(),
3277            false,
3278        );
3279        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3280            .with_declaration_only(true)
3281            .with_callable_arity(CallableArity::exact(arity))
3282            .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3283        self.parsed
3284            .add_signature_with_metadata(code_unit.clone(), metadata);
3285        self.parsed.add_child(parent.clone(), code_unit);
3286    }
3287
3288    fn visit_variable_declaration(
3289        &mut self,
3290        declaration_node: Node<'_>,
3291        declarator: Node<'_>,
3292        scope: &ScopeInfo,
3293        in_class_body: bool,
3294    ) {
3295        let Some(name) = extract_variable_name(declarator, self.source) else {
3296            return;
3297        };
3298        let short_name = if in_class_body {
3299            let Some(parent) = &scope.class_unit else {
3300                return;
3301            };
3302            format!("{}.{}", parent.short_name(), name)
3303        } else {
3304            name
3305        };
3306        let fq = cpp_member_fq(&scope.package_name, &short_name);
3307        let code_unit = CodeUnit::new_fq(
3308            self.file.clone(),
3309            CodeUnitType::Field,
3310            scope.package_name.clone(),
3311            short_name,
3312            fq,
3313        );
3314        if self.parsed.contains_declaration(&code_unit) {
3315            return;
3316        }
3317        self.parsed
3318            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3319        self.parsed.add_signature_with_metadata(
3320            code_unit.clone(),
3321            SignatureMetadata::new(
3322                render_cpp_field_signature(declaration_node, declarator, self.source),
3323                Vec::new(),
3324            )
3325            .with_cpp_field_linkage(cpp_field_declaration_linkage(declaration_node, self.source)),
3326        );
3327        if let Some(parent) = &scope.class_unit {
3328            self.parsed.add_child(parent.clone(), code_unit);
3329        } else if let Some(module) = &scope.module {
3330            self.parsed.add_child(module.clone(), code_unit);
3331        }
3332    }
3333
3334    fn visit_class_members_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
3335        let mut cursor = node.walk();
3336        for child in node.named_children(&mut cursor) {
3337            if child.kind() == "init_declarator"
3338                && let Some(inner) = child.child_by_field_name("declarator")
3339            {
3340                self.visit_variable_declaration(node, inner, scope, true);
3341            } else if matches!(
3342                child.kind(),
3343                "identifier"
3344                    | "field_identifier"
3345                    | "pointer_declarator"
3346                    | "reference_declarator"
3347                    | "array_declarator"
3348                    | "parenthesized_declarator"
3349            ) {
3350                self.visit_variable_declaration(node, child, scope, true);
3351            }
3352        }
3353    }
3354
3355    fn visit_global_variables_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
3356        let mut cursor = node.walk();
3357        for child in node.named_children(&mut cursor) {
3358            if child.kind() == "init_declarator"
3359                && let Some(inner) = child.child_by_field_name("declarator")
3360            {
3361                self.visit_variable_declaration(node, inner, scope, false);
3362            } else if matches!(
3363                child.kind(),
3364                "identifier"
3365                    | "field_identifier"
3366                    | "pointer_declarator"
3367                    | "reference_declarator"
3368                    | "array_declarator"
3369                    | "parenthesized_declarator"
3370            ) {
3371                self.visit_variable_declaration(node, child, scope, false);
3372            }
3373        }
3374    }
3375
3376    fn visit_include(&mut self, node: Node<'_>) {
3377        let raw = normalize_cpp_whitespace(node_text(node, self.source));
3378        self.parsed.imports.push(ImportInfo {
3379            raw_snippet: raw,
3380            is_wildcard: false,
3381            is_global: false,
3382            identifier: None,
3383            alias: None,
3384            path: None,
3385            binder_span: None,
3386        });
3387    }
3388
3389    fn visit_type_declaration<'tree>(
3390        &mut self,
3391        node: Node<'tree>,
3392        scope: &ScopeInfo,
3393        stack: &mut Vec<CppWork<'tree>>,
3394    ) {
3395        if let Some(type_node) = node.child_by_field_name("type")
3396            && matches!(
3397                type_node.kind(),
3398                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3399            )
3400        {
3401            self.visit_class_like(type_node, scope, stack);
3402        }
3403
3404        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
3405            let range = Range {
3406                start_byte: node.start_byte(),
3407                end_byte: recovered.end_node.end_byte(),
3408                start_line: node.start_position().row + 1,
3409                end_line: recovered.end_node.end_position().row + 1,
3410            };
3411            let signature = self
3412                .source
3413                .get(range.start_byte..range.end_byte)
3414                .map(normalize_cpp_whitespace)
3415                .unwrap_or_default();
3416            self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
3417            return;
3418        }
3419
3420        let alias_names = match node.kind() {
3421            "alias_declaration" => extract_alias_declaration_name(node, self.source)
3422                .into_iter()
3423                .collect::<Vec<_>>(),
3424            "type_definition" => extract_typedef_alias_names(node, self.source),
3425            _ => Vec::new(),
3426        };
3427        self.add_type_aliases(node, scope, alias_names);
3428    }
3429
3430    fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
3431        let signature = normalize_cpp_whitespace(node_text(node, self.source));
3432        self.record_type_aliases(
3433            node,
3434            scope,
3435            alias_names,
3436            signature,
3437            cpp_declaration_range(node),
3438        );
3439    }
3440
3441    fn record_type_aliases(
3442        &mut self,
3443        node: Node<'_>,
3444        scope: &ScopeInfo,
3445        alias_names: Vec<String>,
3446        signature: String,
3447        range: Range,
3448    ) {
3449        if signature.is_empty() {
3450            return;
3451        }
3452        let type_name = node
3453            .child_by_field_name("type")
3454            .and_then(|type_node| type_node.child_by_field_name("name"))
3455            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
3456        for alias_name in alias_names {
3457            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
3458                continue;
3459            }
3460            let short_name = if let Some(parent) = &scope.class_unit {
3461                format!("{}${alias_name}", parent.short_name())
3462            } else {
3463                alias_name
3464            };
3465            let fq = cpp_class_fq(&scope.package_name, &short_name);
3466            let code_unit = CodeUnit::with_signature_and_fq(
3467                self.file.clone(),
3468                CodeUnitType::Class,
3469                scope.package_name.clone(),
3470                short_name,
3471                Some(signature.clone()),
3472                false,
3473                fq,
3474            );
3475            // Declaration identity does not include the alias signature. Keep
3476            // each physical range so conditional aliases retain their guards.
3477            self.parsed
3478                .add_code_unit_with_range(code_unit.clone(), range, None, None);
3479            self.parsed
3480                .add_signature(code_unit.clone(), signature.clone());
3481            if let Some(metadata) = &scope.template_metadata {
3482                let mut metadata = metadata.clone();
3483                metadata.primary_fq_name = code_unit.fq_name();
3484                self.parsed
3485                    .set_cpp_template_metadata(code_unit.clone(), metadata);
3486            }
3487            if let Some(parent) = &scope.class_unit {
3488                self.parsed.add_child(parent.clone(), code_unit.clone());
3489            } else if let Some(module) = &scope.module {
3490                self.parsed.add_child(module.clone(), code_unit.clone());
3491            }
3492            self.parsed.mark_type_alias(code_unit);
3493        }
3494    }
3495
3496    fn visit_macro(&mut self, node: Node<'_>) {
3497        let Some(name) = extract_macro_name(node, self.source) else {
3498            return;
3499        };
3500        let signature = node_text(node, self.source).trim_end().to_string();
3501        if signature.is_empty() {
3502            return;
3503        }
3504        let fq = cpp_member_fq("", &name);
3505        let code_unit = CodeUnit::new_fq(self.file.clone(), CodeUnitType::Macro, "", name, fq);
3506        if self.parsed.contains_declaration_identity(&code_unit) {
3507            return;
3508        }
3509        self.parsed
3510            .add_code_unit(code_unit.clone(), node, self.source, None, None);
3511        let name_range = node
3512            .child_by_field_name("name")
3513            .map(cpp_declaration_range)
3514            .unwrap_or_else(|| cpp_declaration_range(node));
3515        self.parsed
3516            .record_materialization(MaterializationRecord::GeneratedDeclaration {
3517                site: cpp_declaration_range(node),
3518                argument: name_range,
3519                kind: GenerationKind::PreprocessorDefinition,
3520                unit: code_unit.clone(),
3521            });
3522        self.parsed.add_signature(code_unit, signature);
3523    }
3524}
3525
3526/// Classify a C++ field while its declaration syntax is already available.
3527///
3528/// The persisted result lets later visibility queries avoid reparsing the
3529/// complete source file only to recover linkage.
3530pub fn cpp_field_declaration_linkage(declaration: Node<'_>, source: &str) -> CppFieldLinkage {
3531    let mut current = declaration.parent();
3532    let mut enclosed_by_class = false;
3533    while let Some(node) = current {
3534        if node.kind() == "namespace_definition"
3535            && node
3536                .child_by_field_name("name")
3537                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
3538        {
3539            return CppFieldLinkage::Internal;
3540        }
3541        if matches!(
3542            node.kind(),
3543            "class_specifier" | "struct_specifier" | "union_specifier"
3544        ) && node
3545            .child_by_field_name("name")
3546            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
3547        {
3548            return CppFieldLinkage::Internal;
3549        }
3550        if matches!(
3551            node.kind(),
3552            "class_specifier" | "struct_specifier" | "union_specifier"
3553        ) {
3554            enclosed_by_class = true;
3555        }
3556        if matches!(node.kind(), "function_definition" | "lambda_expression") {
3557            return CppFieldLinkage::Internal;
3558        }
3559        current = node.parent();
3560    }
3561    if enclosed_by_class {
3562        return CppFieldLinkage::External;
3563    }
3564    let mut cursor = declaration.walk();
3565    let mut has_static = false;
3566    let mut has_extern = false;
3567    let mut has_inline = false;
3568    let mut has_const = false;
3569    let mut has_constexpr = false;
3570    for child in declaration.named_children(&mut cursor) {
3571        let text = normalize_cpp_whitespace(node_text(child, source));
3572        match (child.kind(), text.as_str()) {
3573            ("storage_class_specifier", "static") => has_static = true,
3574            ("storage_class_specifier", "extern") => has_extern = true,
3575            ("storage_class_specifier", "inline") => has_inline = true,
3576            ("storage_class_specifier", "constexpr") => has_constexpr = true,
3577            ("type_qualifier", "const") => has_const = true,
3578            ("type_qualifier", "constexpr") => has_constexpr = true,
3579            _ => {}
3580        }
3581    }
3582    if has_static {
3583        CppFieldLinkage::Internal
3584    } else if has_extern || has_inline {
3585        CppFieldLinkage::External
3586    } else if has_const || has_constexpr {
3587        CppFieldLinkage::InternalUnlessExternalPeer
3588    } else {
3589        CppFieldLinkage::External
3590    }
3591}
3592
3593fn cpp_declaration_range(node: Node<'_>) -> Range {
3594    Range {
3595        start_byte: node.start_byte(),
3596        end_byte: node.end_byte(),
3597        start_line: node.start_position().row + 1,
3598        end_line: node.end_position().row + 1,
3599    }
3600}
3601
3602pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
3603    let mut in_block_comment = false;
3604    for line in source.lines() {
3605        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
3606        let trimmed = stripped.trim();
3607        if !looks_like_quoted_include_line(trimmed) {
3608            continue;
3609        }
3610
3611        let raw = normalize_cpp_whitespace(trimmed);
3612        // The tree-sitter walk already recorded every `#include` it could see;
3613        // this line scan only recovers the ones a parse error hid, so skip a
3614        // snippet that is already an import binding.
3615        if parsed
3616            .imports
3617            .iter()
3618            .any(|import| import.raw_snippet == raw)
3619        {
3620            continue;
3621        }
3622
3623        parsed.imports.push(ImportInfo {
3624            raw_snippet: raw,
3625            is_wildcard: false,
3626            is_global: false,
3627            identifier: None,
3628            alias: None,
3629            path: None,
3630            binder_span: None,
3631        });
3632    }
3633}
3634
3635fn looks_like_quoted_include_line(line: &str) -> bool {
3636    let Some(rest) = line.trim_start().strip_prefix('#') else {
3637        return false;
3638    };
3639    let Some(rest) = rest.trim_start().strip_prefix("include") else {
3640        return false;
3641    };
3642    rest.trim_start().starts_with('"')
3643}
3644
3645fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
3646    let mut raw = Vec::new();
3647    let mut cursor = node.walk();
3648    for child in node.named_children(&mut cursor) {
3649        if child.kind() == "base_class_clause" {
3650            collect_cpp_base_nodes(child, source, &mut raw);
3651        }
3652    }
3653    raw
3654}
3655
3656fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
3657    walk_named_tree_preorder(node, false, |child| match child.kind() {
3658        "type_identifier" | "qualified_identifier" | "template_type" => {
3659            let text = normalize_cpp_whitespace(node_text(child, source));
3660            if !text.is_empty() {
3661                raw.push(text);
3662            }
3663            WalkControl::SkipChildren
3664        }
3665        _ => WalkControl::Continue,
3666    });
3667}
3668
3669fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
3670    let mut out = String::new();
3671    let chars: Vec<char> = line.chars().collect();
3672    let mut index = 0;
3673    let mut in_string = false;
3674    let mut in_char = false;
3675    let mut escape = false;
3676
3677    while index < chars.len() {
3678        let ch = chars[index];
3679        let next = chars.get(index + 1).copied();
3680
3681        if *in_block_comment {
3682            if ch == '*' && next == Some('/') {
3683                *in_block_comment = false;
3684                index += 2;
3685            } else {
3686                index += 1;
3687            }
3688            continue;
3689        }
3690
3691        if in_string {
3692            out.push(ch);
3693            if escape {
3694                escape = false;
3695            } else if ch == '\\' {
3696                escape = true;
3697            } else if ch == '"' {
3698                in_string = false;
3699            }
3700            index += 1;
3701            continue;
3702        }
3703
3704        if in_char {
3705            out.push(ch);
3706            if escape {
3707                escape = false;
3708            } else if ch == '\\' {
3709                escape = true;
3710            } else if ch == '\'' {
3711                in_char = false;
3712            }
3713            index += 1;
3714            continue;
3715        }
3716
3717        if ch == '/' && next == Some('/') {
3718            break;
3719        }
3720        if ch == '/' && next == Some('*') {
3721            *in_block_comment = true;
3722            index += 2;
3723            continue;
3724        }
3725        if ch == '"' {
3726            in_string = true;
3727            out.push(ch);
3728            index += 1;
3729            continue;
3730        }
3731        if ch == '\'' {
3732            in_char = true;
3733            out.push(ch);
3734            index += 1;
3735            continue;
3736        }
3737
3738        out.push(ch);
3739        index += 1;
3740    }
3741
3742    out
3743}
3744
3745#[derive(Clone)]
3746struct FunctionInfo {
3747    package_name: String,
3748    owner_path: Option<String>,
3749    name: String,
3750    signature: String,
3751}
3752
3753enum DeclaratorKind<'a> {
3754    Function(Node<'a>),
3755    Variable(Node<'a>),
3756}
3757
3758impl FunctionInfo {
3759    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
3760        self.code_unit_with_synthetic(file, false)
3761    }
3762
3763    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
3764        let short_name = if let Some(owner) = &self.owner_path {
3765            format!("{owner}.{}", self.name)
3766        } else {
3767            self.name.clone()
3768        };
3769        let fq = cpp_member_fq(&self.package_name, &short_name);
3770        CodeUnit::with_signature_and_fq(
3771            file,
3772            CodeUnitType::Function,
3773            self.package_name.clone(),
3774            short_name,
3775            Some(self.signature.clone()),
3776            synthetic,
3777            fq,
3778        )
3779    }
3780}
3781
3782fn extract_function_info(
3783    declarator: Node<'_>,
3784    source: &str,
3785    scope: &ScopeInfo,
3786) -> Option<FunctionInfo> {
3787    let parameters_node = declarator.child_by_field_name("parameters")?;
3788    let parameters_text = cpp_parameter_signature(parameters_node, source);
3789    let declarator_name_node = declarator
3790        .child_by_field_name("declarator")
3791        .or_else(|| parameters_node.prev_named_sibling())?;
3792    let recovered_specialization_member = scope
3793        .recovered_specialization_member_scope
3794        .then(|| {
3795            let terminal = declarator_name_node
3796                .child_by_field_name("name")
3797                .unwrap_or(declarator_name_node);
3798            let name = canonical_cpp_qualified_component(terminal, source)?.name;
3799            let owner = scope.class_unit.as_ref()?;
3800            Some((
3801                Some(owner.short_name().to_string()),
3802                name,
3803                scope.package_name.clone(),
3804            ))
3805        })
3806        .flatten();
3807    let (owner_path, name, package_name) = if let Some(parts) = recovered_specialization_member {
3808        parts
3809    } else if let Some(parts) =
3810        split_structured_templated_cpp_name(declarator_name_node, source, scope)
3811    {
3812        parts
3813    } else {
3814        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
3815            declarator_name_node,
3816            source,
3817        )?);
3818        if raw_name.is_empty() {
3819            return None;
3820        }
3821        split_cpp_name(&raw_name, scope)
3822    };
3823    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
3824    let mut signature = if suffix.is_empty() {
3825        parameters_text
3826    } else {
3827        format!("{parameters_text} {suffix}")
3828    };
3829    if let Some(template_signature) = &scope.template_signature {
3830        signature = format!("{template_signature}{signature}");
3831    }
3832
3833    Some(FunctionInfo {
3834        package_name,
3835        owner_path,
3836        name,
3837        signature,
3838    })
3839}
3840
3841/// The part of a `function_declarator` after its parameter list that belongs to
3842/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
3843/// specification, a trailing return type and a trailing requires-clause.
3844///
3845/// The grammar makes each of these a distinct sibling of the `parameters`
3846/// field, so they are read from the tree. Splitting the declarator's text on
3847/// the parameter list instead silently dropped every qualifier whenever the
3848/// parameter list was spelled with whitespace that normalization rewrote - a
3849/// line break or a double space was enough to make a `const` member definition
3850/// a different logical symbol from its declaration (#1827).
3851///
3852/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
3853/// are deliberately excluded. C++ does not make them part of the signature and
3854/// an out-of-line definition never repeats them, so including them would split
3855/// a declaration from its own definition.
3856fn cpp_declarator_identity_suffix(
3857    declarator: Node<'_>,
3858    parameters_node: Node<'_>,
3859    source: &str,
3860) -> String {
3861    let mut cursor = declarator.walk();
3862    let parts = declarator
3863        .named_children(&mut cursor)
3864        .filter(|child| child.start_byte() >= parameters_node.end_byte())
3865        .filter(|child| {
3866            matches!(
3867                child.kind(),
3868                "type_qualifier"
3869                    | "ref_qualifier"
3870                    | "noexcept"
3871                    | "throw_specifier"
3872                    | "trailing_return_type"
3873                    | "requires_clause"
3874            )
3875        })
3876        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
3877        .filter(|text| !text.is_empty())
3878        .collect::<Vec<_>>();
3879    normalize_cpp_qualifier_suffix(&parts.join(" "))
3880}
3881
3882fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
3883    match classify_declarator(node)? {
3884        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
3885        DeclaratorKind::Variable(_) => None,
3886    }
3887}
3888
3889fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
3890    match node.kind() {
3891        "function_declarator" => {
3892            let inner = node
3893                .child_by_field_name("declarator")
3894                .or_else(|| node.child_by_field_name("name"))
3895                .or_else(|| last_named_child(node));
3896            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
3897                Some(DeclaratorKind::Variable(node))
3898            } else {
3899                Some(DeclaratorKind::Function(node))
3900            }
3901        }
3902        "init_declarator"
3903        | "pointer_declarator"
3904        | "reference_declarator"
3905        | "parenthesized_declarator"
3906        | "array_declarator"
3907        | "attributed_declarator"
3908        | "template_function" => node
3909            .child_by_field_name("declarator")
3910            .or_else(|| node.child_by_field_name("name"))
3911            .or_else(|| last_named_child(node))
3912            .and_then(classify_declarator),
3913        "identifier" | "field_identifier" | "qualified_identifier" => {
3914            Some(DeclaratorKind::Variable(node))
3915        }
3916        _ => node
3917            .child_by_field_name("declarator")
3918            .or_else(|| node.child_by_field_name("name"))
3919            .or_else(|| last_named_child(node))
3920            .and_then(classify_declarator),
3921    }
3922}
3923
3924fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
3925    matches!(
3926        node.kind(),
3927        "function_declarator"
3928            | "init_declarator"
3929            | "pointer_declarator"
3930            | "reference_declarator"
3931            | "parenthesized_declarator"
3932            | "array_declarator"
3933            | "attributed_declarator"
3934            | "template_function"
3935            | "identifier"
3936            | "field_identifier"
3937            | "qualified_identifier"
3938    )
3939}
3940
3941fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
3942    let class_like = first_class_like_child(node);
3943    let mut cursor = node.walk();
3944    node.named_children(&mut cursor).any(|child| {
3945        matches!(
3946            child.kind(),
3947            "init_declarator"
3948                | "pointer_declarator"
3949                | "reference_declarator"
3950                | "array_declarator"
3951                | "function_declarator"
3952                | "parenthesized_declarator"
3953                | "attributed_declarator"
3954        ) || matches!(
3955            child.kind(),
3956            "identifier" | "field_identifier" | "qualified_identifier"
3957        ) && class_like.is_none_or(|class_node| {
3958            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
3959        })
3960    })
3961}
3962
3963/// Find the unique namespace-scope forward declaration that precedes a
3964/// recovered export-macro class definition.  Tree-sitter can close a malformed
3965/// class at the enclosing namespace's closing brace, leaving the later class
3966/// definitions as root-level recovered `function_definition` nodes.  A
3967/// preceding `class Name;` in the same namespace is the only structured identity
3968/// signal available in that shape.
3969///
3970/// The search is deliberately conservative: it only accepts a body-less class
3971/// specifier whose declaration has no declarator and is not nested in a function
3972/// or class body.  More than one matching namespace forward declaration is
3973/// ambiguous and returns `None` rather than guessing.
3974fn unique_earlier_cpp_namespace_forward(
3975    recovered_node: Node<'_>,
3976    name: &str,
3977    source: &str,
3978) -> Option<String> {
3979    let mut root = recovered_node;
3980    while let Some(parent) = root.parent() {
3981        root = parent;
3982    }
3983
3984    let mut candidates = Vec::new();
3985    let mut stack = vec![root];
3986    while let Some(current) = stack.pop() {
3987        if current.start_byte() < recovered_node.start_byte()
3988            && matches!(
3989                current.kind(),
3990                "class_specifier" | "struct_specifier" | "union_specifier"
3991            )
3992            && cpp_body_node(current).is_none()
3993            && current.parent().is_some_and(|parent| {
3994                parent.kind() == "declaration_list"
3995                    || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
3996            })
3997            && class_like_name(current, source).as_deref() == Some(name)
3998            && cpp_namespace_definition_for_forward(current).is_some_and(|namespace| {
3999                // Borrowing is only justified by the parser-recovery shape we
4000                // are repairing: the namespace that held the forward must
4001                // itself contain a syntax error and must have closed before
4002                // the root-level recovered class. A clean, unrelated
4003                // namespace forward is not an identity proof.
4004                namespace.has_error()
4005                    && namespace.end_byte() < recovered_node.start_byte()
4006                    && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
4007            })
4008            && let Some(package_name) = cpp_namespace_name_for_forward(current, source)
4009        {
4010            candidates.push(package_name);
4011        }
4012
4013        let mut cursor = current.walk();
4014        for child in current.named_children(&mut cursor) {
4015            if child.start_byte() < recovered_node.start_byte() {
4016                stack.push(child);
4017            }
4018        }
4019    }
4020
4021    if candidates.len() == 1 {
4022        candidates.pop()
4023    } else {
4024        None
4025    }
4026}
4027
4028fn malformed_namespace_is_nearest_recovery_region(
4029    namespace: Node<'_>,
4030    recovered_node: Node<'_>,
4031) -> bool {
4032    let mut root = recovered_node;
4033    while let Some(parent) = root.parent() {
4034        root = parent;
4035    }
4036    let mut cursor = root.walk();
4037    root.named_children(&mut cursor)
4038        .filter(|sibling| {
4039            namespace.end_byte() <= sibling.start_byte()
4040                && sibling.end_byte() <= recovered_node.start_byte()
4041        })
4042        .all(is_malformed_namespace_recovery_trivia)
4043}
4044
4045fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
4046    matches!(node.kind(), "ERROR" | "comment")
4047        || node.kind().starts_with("preproc_")
4048        || node.kind() == "expression_statement" && node.named_child_count() == 0
4049}
4050
4051/// Return the namespace path for a forward class only when the declaration is
4052/// at namespace scope.  A declaration nested in a function/class body may share
4053/// the same namespace ancestor but cannot identify a top-level class definition.
4054fn cpp_namespace_name_for_forward(node: Node<'_>, source: &str) -> Option<String> {
4055    cpp_namespace_definition_for_forward(node)?;
4056    cpp_lexical_namespace_name(node, source)
4057}
4058
4059fn cpp_namespace_definition_for_forward(node: Node<'_>) -> Option<Node<'_>> {
4060    let declaration = node.parent()?;
4061    let mut ancestor = declaration.parent();
4062    while let Some(current) = ancestor {
4063        if matches!(
4064            current.kind(),
4065            "compound_statement"
4066                | "field_declaration_list"
4067                | "class_specifier"
4068                | "struct_specifier"
4069                | "union_specifier"
4070                | "function_definition"
4071                | "lambda_expression"
4072        ) {
4073            return None;
4074        }
4075        if current.kind() == "namespace_definition" {
4076            return Some(current);
4077        }
4078        ancestor = current.parent();
4079    }
4080    None
4081}
4082
4083fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
4084    match node.kind() {
4085        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4086        "parenthesized_declarator" => node
4087            .child_by_field_name("declarator")
4088            .or_else(|| last_named_child(node))
4089            .is_some_and(is_pointer_wrapper_declarator),
4090        "template_function" => node
4091            .child_by_field_name("name")
4092            .is_some_and(is_function_pointer_like_inner_declarator),
4093        _ => false,
4094    }
4095}
4096
4097fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
4098    match node.kind() {
4099        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4100        "parenthesized_declarator" => node
4101            .child_by_field_name("declarator")
4102            .or_else(|| last_named_child(node))
4103            .is_some_and(is_pointer_wrapper_declarator),
4104        _ => false,
4105    }
4106}
4107
4108fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<String>, String, String) {
4109    let cleaned = raw_name.trim_start_matches("template ").trim();
4110    // A leading `::` is the explicit-global marker, not an empty owner segment.
4111    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
4112    // erroneous macro envelope swallowing the first identifier of an
4113    // out-of-line `X::X` constructor, chromium #1573); without this strip the
4114    // split below yields owner_parts `[""]`, constructing a unit with an empty
4115    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
4116    let cleaned = cleaned.trim_start_matches("::");
4117    // Parser recovery can preserve two adjacent scope operators around a
4118    // missing component (for example `X::/**/::method` in compiler diagnostic
4119    // fixtures). Empty components are syntax-recovery artifacts, never C++
4120    // owners. Keeping one as the final owner constructed `short_name=".method"`
4121    // and violated the structured package/short boundary during a large LLVM
4122    // workspace build. This is the same legacy-string-to-FqName bridge as the
4123    // ordinary split above; discard only components that the delimiter itself
4124    // proves empty.
4125    let parts: Vec<_> = cleaned
4126        .split("::")
4127        .filter(|component| !component.is_empty())
4128        .collect();
4129    if parts.is_empty() {
4130        return (None, cleaned.to_string(), scope.package_name.clone());
4131    }
4132    if parts.len() > 1 {
4133        let name = parts.last().unwrap_or(&cleaned).to_string();
4134        let owner_parts = &parts[..parts.len() - 1];
4135        if let Some(class_unit) = &scope.class_unit {
4136            // Lexically inside a class body: the owner is that class, whatever
4137            // the declarator re-qualifies it as.
4138            return (
4139                Some(class_unit.short_name().to_string()),
4140                name,
4141                scope.package_name.clone(),
4142            );
4143        }
4144        if !scope.package_name.is_empty() {
4145            // Out-of-line member definition written *inside* an enclosing
4146            // `namespace {}` block (scope package is that namespace). Every
4147            // owner segment before the terminal member is a class-nesting step
4148            // -- an out-of-line nested-class member `Outer::Inner::method` in
4149            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
4150            // namespace path: `using namespace` never brings nested-class
4151            // access into unqualified scope, so C++ always writes the full
4152            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
4153            // that redundantly re-states the enclosing namespace it already
4154            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
4155            // strip that re-qualifying prefix (which duplicates a suffix of the
4156            // enclosing package path) before treating what remains as the
4157            // nested-class chain, so the redundant spelling still lands on the
4158            // same `log4cxx.Foo.method` identity as its header declaration.
4159            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
4160            let owner_path = (!nested.is_empty()).then(|| nested.join("$"));
4161            return (owner_path, name, scope.package_name.clone());
4162        }
4163        // File scope (no enclosing `namespace {}` block, scope package empty).
4164        let (owner_path, package_name) = if owner_parts.len() > 1 {
4165            // A multi-segment qualifier at file scope with no enclosing
4166            // namespace: treat all but the last owner segment as the namespace
4167            // path and the last as the owning class (`ns1::ns2::Class::method`
4168            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
4169            // is really a namespace or an outer class cannot be told from the
4170            // declarator text alone here, and no enclosing namespace or
4171            // in-index owner is available at per-file extraction to confirm the
4172            // class reading, so the far-more-common namespace interpretation is
4173            // kept rather than guessed away (the nested-class-at-file-scope and
4174            // using-directive-qualified nested-class shapes remain on this
4175            // behavior; see #1121).
4176            (
4177                Some(owner_parts.last().unwrap_or(&"").to_string()),
4178                owner_parts[..owner_parts.len() - 1].join("::"),
4179            )
4180        } else {
4181            // A bare `Class::member` qualifier at file scope carries no
4182            // namespace segment of its own. The declarator alone cannot say
4183            // which namespace owns `Class` -- but a `using namespace X;`
4184            // directive already in effect at this point in the file (#1093,
4185            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
4186            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
4187            // is the remaining structural signal for it, so fall back to it
4188            // rather than leaving the definition's package empty while its
4189            // header declaration (parsed inside the `namespace {}` block) keeps
4190            // the real one -- an identity split that made the same member
4191            // unresolvable under its own displayed spelling.
4192            (
4193                Some(owner_parts[0].to_string()),
4194                cpp_using_directive_namespace_for_bare_owner(scope),
4195            )
4196        };
4197        return (owner_path, name, package_name);
4198    }
4199
4200    let package_name = scope.package_name.clone();
4201    let owner_path = scope
4202        .class_unit
4203        .as_ref()
4204        .map(|parent| parent.short_name().to_string());
4205    (owner_path, cleaned.to_string(), package_name)
4206}
4207
4208/// Drop the leading owner segments of an out-of-line member qualifier that
4209/// merely re-state the enclosing namespace the definition already sits in, so
4210/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
4211/// definition may redundantly write `a::b::Outer::Inner::method` (or the
4212/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
4213/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
4214/// noise, not class-nesting steps. Returns the owner segments with the longest
4215/// such re-qualifying prefix removed (possibly all of them, when the qualifier
4216/// names only the enclosing namespace before the terminal member -- a
4217/// re-qualified free function). `package_name` is the enclosing namespace path
4218/// in its stored `::`-joined form; both sides are split on the same delimiter
4219/// the namespace walker joined them with, so this compares namespace *segments*
4220/// rather than scanning text.
4221fn strip_redundant_namespace_prefix<'a>(
4222    owner_parts: &'a [&'a str],
4223    package_name: &str,
4224) -> &'a [&'a str] {
4225    if package_name.is_empty() {
4226        return owner_parts;
4227    }
4228    let package_segments: Vec<&str> = package_name.split("::").collect();
4229    let max_prefix = owner_parts.len().min(package_segments.len());
4230    for prefix_len in (1..=max_prefix).rev() {
4231        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
4232        if &owner_parts[..prefix_len] == package_suffix {
4233            return &owner_parts[prefix_len..];
4234        }
4235    }
4236    owner_parts
4237}
4238
4239/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
4240/// class name at file/namespace scope, from the `using namespace` directives
4241/// visible at this point in the file. Several may be in scope at once (a
4242/// primary `using namespace NS;` alongside deeper conveniences like `using
4243/// namespace NS::helpers;`); since the declarator gives no way to tell which
4244/// one actually declares the owner class, prefer the shallowest (fewest
4245/// `::`-separated segments) as the file's most likely "home" namespace,
4246/// breaking ties by declaration order. Returns an empty string (leaving the
4247/// caller's package unqualified, as before) when no using-namespace directive
4248/// is in scope.
4249fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
4250    scope
4251        .visible_using_namespaces
4252        .iter()
4253        .min_by_key(|namespace| namespace.split("::").count())
4254        .cloned()
4255        .unwrap_or_default()
4256}
4257
4258struct CppQualifiedNameComponent {
4259    name: String,
4260    is_template_id: bool,
4261}
4262
4263fn split_structured_templated_cpp_name(
4264    declarator_name: Node<'_>,
4265    source: &str,
4266    scope: &ScopeInfo,
4267) -> Option<(Option<String>, String, String)> {
4268    if declarator_name.kind() != "qualified_identifier" {
4269        return None;
4270    }
4271
4272    let mut components = Vec::new();
4273    let mut current = declarator_name;
4274    let mut explicitly_global = false;
4275    loop {
4276        if current.kind() == "qualified_identifier" {
4277            if let Some(component) = current.child_by_field_name("scope") {
4278                components.push(canonical_cpp_qualified_component(component, source)?);
4279            } else if components.is_empty() {
4280                explicitly_global = true;
4281            } else {
4282                return None;
4283            }
4284            current = current.child_by_field_name("name")?;
4285        } else {
4286            components.push(canonical_cpp_qualified_component(current, source)?);
4287            break;
4288        }
4289    }
4290
4291    let terminal = components.pop()?;
4292    let owner_start = components
4293        .iter()
4294        .position(|component| component.is_template_id)?;
4295    let explicit_package = components[..owner_start]
4296        .iter()
4297        .map(|component| component.name.as_str())
4298        .collect::<Vec<_>>()
4299        .join("::");
4300    let explicit_package_is_empty = explicit_package.is_empty();
4301    let package_name = match (
4302        explicitly_global,
4303        scope.package_name.is_empty(),
4304        explicit_package_is_empty,
4305    ) {
4306        (true, _, _) => explicit_package,
4307        (false, _, true) => scope.package_name.clone(),
4308        (false, true, false) => explicit_package,
4309        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
4310    };
4311    // Same identity-split fallback as `split_cpp_name` (#1093): a template
4312    // specialization's owner class named with no namespace segment of its own
4313    // (`explicit_package` empty) at file scope (`explicitly_global` false)
4314    // with nothing enclosing (`package_name` still empty) has no structural
4315    // signal for its namespace besides an in-scope `using namespace X;`.
4316    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
4317    {
4318        cpp_using_directive_namespace_for_bare_owner(scope)
4319    } else {
4320        package_name
4321    };
4322    let owner_path = components[owner_start..]
4323        .iter()
4324        .map(|component| component.name.as_str())
4325        .collect::<Vec<_>>()
4326        .join("$");
4327    if owner_path.is_empty() || terminal.name.is_empty() {
4328        return None;
4329    }
4330
4331    Some((Some(owner_path), terminal.name, package_name))
4332}
4333
4334fn canonical_cpp_qualified_component(
4335    mut component: Node<'_>,
4336    source: &str,
4337) -> Option<CppQualifiedNameComponent> {
4338    let mut is_template_id = false;
4339    loop {
4340        match component.kind() {
4341            "template_type" => {
4342                is_template_id = true;
4343                component = component.child_by_field_name("name")?;
4344            }
4345            "dependent_name" => component = component.named_child(0)?,
4346            "identifier"
4347            | "field_identifier"
4348            | "namespace_identifier"
4349            | "type_identifier"
4350            | "operator_name"
4351            | "destructor_name" => {
4352                let name = normalize_cpp_whitespace(node_text(component, source));
4353                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
4354                    name,
4355                    is_template_id,
4356                });
4357            }
4358            _ => component = component.child_by_field_name("name")?,
4359        }
4360    }
4361}
4362
4363fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
4364    match node.kind() {
4365        "identifier"
4366        | "field_identifier"
4367        | "type_identifier"
4368        | "operator_name"
4369        | "destructor_name"
4370        | "qualified_identifier" => node_text(node, source).to_string(),
4371        "function_declarator"
4372        | "pointer_declarator"
4373        | "reference_declarator"
4374        | "parenthesized_declarator"
4375        | "array_declarator"
4376        | "template_function" => node
4377            .child_by_field_name("declarator")
4378            .or_else(|| node.child_by_field_name("name"))
4379            .or_else(|| last_named_child(node))
4380            .map(|child| extract_declarator_name(child, source))
4381            .unwrap_or_else(|| node_text(node, source).to_string()),
4382        _ => node
4383            .child_by_field_name("name")
4384            .map(|child| extract_declarator_name(child, source))
4385            .unwrap_or_else(|| node_text(node, source).to_string()),
4386    }
4387}
4388
4389/// Extract a callable identity only through declaration-shaped AST nodes.
4390/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
4391/// expose the call's parameter list as a false function declarator; accepting
4392/// arbitrary node text there emitted bogus names such as `.*f`.
4393fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
4394    match node.kind() {
4395        "identifier"
4396        | "field_identifier"
4397        | "type_identifier"
4398        | "operator_name"
4399        | "destructor_name"
4400        | "qualified_identifier" => Some(node_text(node, source).to_string()),
4401        "function_declarator"
4402        | "pointer_declarator"
4403        | "reference_declarator"
4404        | "parenthesized_declarator"
4405        | "array_declarator"
4406        | "template_function" => node
4407            .child_by_field_name("declarator")
4408            .or_else(|| node.child_by_field_name("name"))
4409            .and_then(|child| extract_callable_declarator_name(child, source)),
4410        _ => None,
4411    }
4412}
4413
4414fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
4415    match node.kind() {
4416        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
4417            let name = node_text(node, source).trim().to_string();
4418            (!name.is_empty()).then_some(name)
4419        }
4420        _ => node
4421            .child_by_field_name("declarator")
4422            .or_else(|| node.child_by_field_name("name"))
4423            .or_else(|| last_named_child(node))
4424            .and_then(|child| extract_variable_name(child, source)),
4425    }
4426}
4427
4428fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
4429    let count = node.named_child_count();
4430    if count == 0 {
4431        None
4432    } else {
4433        node.named_child(count - 1)
4434    }
4435}
4436
4437fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
4438    let name_node = node.child_by_field_name("name")?;
4439    let name = normalize_cpp_whitespace(node_text(name_node, source));
4440    (!name.is_empty()).then_some(name)
4441}
4442
4443fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
4444    if node.kind() != "declaration" {
4445        return Vec::new();
4446    }
4447    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
4448        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
4449    }) else {
4450        return Vec::new();
4451    };
4452    let Some(declarator) = node.child_by_field_name("declarator") else {
4453        return Vec::new();
4454    };
4455    if node_text(keyword, source) == "using"
4456        && (declarator.kind() != "init_declarator"
4457            || declarator.child_by_field_name("value").is_none())
4458    {
4459        return Vec::new();
4460    }
4461    if node_text(keyword, source) == "typedef"
4462        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
4463    {
4464        return vec![alias_name];
4465    }
4466    extract_typedef_declarator_name(declarator, source)
4467        .into_iter()
4468        .collect()
4469}
4470
4471fn recovered_typedef_error_alias_name(
4472    declaration: Node<'_>,
4473    declarator: Node<'_>,
4474    source: &str,
4475) -> Option<String> {
4476    // An export macro between `class` and its name can make tree-sitter parse
4477    // the recovered class body as a function body. In that shape,
4478    //
4479    //     typedef spi::Filter BASE_CLASS;
4480    //
4481    // becomes a declaration whose `declarator` is the underlying qualified
4482    // type (`spi::Filter`) and whose actual alias name is displaced into the
4483    // following ERROR node. Do not publish the terminal underlying type
4484    // (`Filter`) as a false class-owned alias.
4485    if declarator.kind() != "qualified_identifier" {
4486        return None;
4487    }
4488    let mut cursor = declaration.walk();
4489    let mut errors = declaration
4490        .named_children(&mut cursor)
4491        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
4492    let error = errors.next()?;
4493    if errors.next().is_some() || error.named_child_count() != 1 {
4494        return None;
4495    }
4496    let name = error.named_child(0)?;
4497    if !matches!(
4498        name.kind(),
4499        "identifier" | "field_identifier" | "type_identifier"
4500    ) {
4501        return None;
4502    }
4503    let name = normalize_cpp_whitespace(node_text(name, source));
4504    (!name.is_empty()).then_some(name)
4505}
4506
4507fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
4508    // A function-like token in the type position can make tree-sitter expose
4509    // its argument as a parenthesized declarator. Do not publish that argument
4510    // as an alias. The macro-specific recovery below handles the proven shape.
4511    if fragmented_parenthesized_typedef_type(node).is_some() {
4512        return Vec::new();
4513    }
4514    let has_function_like_macro_type = node
4515        .child_by_field_name("type")
4516        .filter(|type_node| type_node.kind() == "type_identifier")
4517        .is_some_and(|type_node| {
4518            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
4519        });
4520    let mut names = Vec::new();
4521    let mut cursor = node.walk();
4522    for declarator in node.children_by_field_name("declarator", &mut cursor) {
4523        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
4524            continue;
4525        }
4526        if let Some(name) = extract_typedef_declarator_name(declarator, source)
4527            && !names.contains(&name)
4528        {
4529            names.push(name);
4530        }
4531    }
4532    names
4533}
4534
4535struct RecoveredMacroTypedefAlias<'tree> {
4536    name: String,
4537    end_node: Node<'tree>,
4538}
4539
4540/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
4541/// into an identifier expression statement. The uppercase macro token, missing
4542/// typedef terminator, and complete sibling terminator prove this exact shape.
4543fn recovered_macro_typedef_alias<'tree>(
4544    node: Node<'tree>,
4545    source: &str,
4546) -> Option<RecoveredMacroTypedefAlias<'tree>> {
4547    let type_node = fragmented_parenthesized_typedef_type(node)?;
4548    if type_node.kind() != "type_identifier"
4549        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
4550    {
4551        return None;
4552    }
4553
4554    let end_node = node.next_named_sibling()?;
4555    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
4556        return None;
4557    }
4558    let name_node = end_node.named_child(0)?;
4559    if name_node.kind() != "identifier" {
4560        return None;
4561    }
4562    let has_terminator = (0..end_node.child_count()).any(|index| {
4563        end_node
4564            .child(index)
4565            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
4566    });
4567    if !has_terminator {
4568        return None;
4569    }
4570    let name = normalize_cpp_whitespace(node_text(name_node, source));
4571    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
4572}
4573
4574fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
4575    if node.kind() != "type_definition" {
4576        return None;
4577    }
4578    let mut declarator_cursor = node.walk();
4579    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
4580    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
4581        return None;
4582    }
4583    let has_missing_terminator = (0..node.child_count()).any(|index| {
4584        node.child(index)
4585            .is_some_and(|child| child.kind() == ";" && child.is_missing())
4586    });
4587    if !has_missing_terminator {
4588        return None;
4589    }
4590    node.child_by_field_name("type")
4591}
4592
4593fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
4594    match node.kind() {
4595        "identifier" | "field_identifier" | "type_identifier" => {
4596            let name = normalize_cpp_whitespace(node_text(node, source));
4597            (!name.is_empty()).then_some(name)
4598        }
4599        "qualified_identifier" => node
4600            .child_by_field_name("name")
4601            .and_then(|name| extract_typedef_declarator_name(name, source)),
4602        _ => node
4603            .child_by_field_name("declarator")
4604            .or_else(|| node.child_by_field_name("name"))
4605            .or_else(|| last_named_child(node))
4606            .and_then(|child| extract_typedef_declarator_name(child, source)),
4607    }
4608}
4609
4610fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
4611    let name = node
4612        .child_by_field_name("name")
4613        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
4614        .or_else(|| {
4615            let mut cursor = node.walk();
4616            node.named_children(&mut cursor)
4617                .find(|child| {
4618                    matches!(
4619                        child.kind(),
4620                        "identifier" | "field_identifier" | "type_identifier"
4621                    )
4622                })
4623                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
4624        })?;
4625    (!name.is_empty()).then_some(name)
4626}
4627
4628fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
4629    left.id() == right.id()
4630}
4631
4632fn render_cpp_type_signature(
4633    node: Node<'_>,
4634    source: &str,
4635    template_signature: Option<&str>,
4636) -> String {
4637    let text = normalize_cpp_whitespace(node_text(node, source));
4638    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
4639    let rendered = if head.ends_with(';') {
4640        head.to_string()
4641    } else {
4642        format!("{head} {{")
4643    };
4644    if let Some(template_signature) = template_signature {
4645        format!("template {template_signature} {rendered}")
4646    } else {
4647        rendered
4648    }
4649}
4650
4651fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
4652    if let Some(signature) =
4653        render_recovered_macro_qualified_field_signature(node, declarator, source)
4654    {
4655        return signature;
4656    }
4657    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
4658    let prefix = cpp_declaration_prefix(node, source);
4659    let name = extract_variable_name(declarator, source).unwrap_or_default();
4660    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
4661    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
4662        || (prefix.ends_with('&') && raw_suffix == "&")
4663    {
4664        String::new()
4665    } else {
4666        raw_suffix
4667    };
4668
4669    let mut rendered = if suffix.is_empty() {
4670        format!("{prefix} {name}")
4671    } else if suffix.starts_with('*') || suffix.starts_with('&') {
4672        format!("{prefix}{suffix} {name}")
4673    } else if suffix.starts_with('[') || suffix.starts_with('(') {
4674        format!("{prefix} {name}{suffix}")
4675    } else {
4676        format!("{prefix} {suffix}{name}")
4677    };
4678    rendered = collapse_cpp_whitespace(&rendered);
4679
4680    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
4681        format!("{rendered} = {initializer};")
4682    } else if declaration_text.ends_with(';') {
4683        format!("{rendered};")
4684    } else {
4685        rendered
4686    }
4687}
4688
4689fn render_recovered_macro_qualified_field_signature(
4690    node: Node<'_>,
4691    declarator: Node<'_>,
4692    source: &str,
4693) -> Option<String> {
4694    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
4695    if !recovered
4696        .iter()
4697        .any(|candidate| same_node(*candidate, declarator))
4698    {
4699        return None;
4700    }
4701    let pseudo_declarator = node.child_by_field_name("declarator")?;
4702    let mut cursor = node.walk();
4703    let clause = node
4704        .named_children(&mut cursor)
4705        .find(|child| child.kind() == "bitfield_clause")?;
4706    let mut cursor = clause.walk();
4707    let error = clause
4708        .named_children(&mut cursor)
4709        .find(|child| child.kind() == "ERROR")?;
4710    let qualified_type =
4711        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
4712    let prefix = cpp_declaration_prefix(node, source);
4713    let name = extract_variable_name(declarator, source)?;
4714    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
4715    let mut rendered = if suffix.is_empty() {
4716        format!("{prefix} {qualified_type} {name}")
4717    } else {
4718        format!("{prefix} {qualified_type} {suffix} {name}")
4719    };
4720    rendered = collapse_cpp_whitespace(&rendered);
4721
4722    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
4723        Some(format!(
4724            "{rendered} = {};",
4725            normalize_cpp_whitespace(node_text(initializer, source))
4726        ))
4727    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
4728        Some(format!("{rendered} = {initializer};"))
4729    } else {
4730        Some(format!("{rendered};"))
4731    }
4732}
4733
4734fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
4735    match node.kind() {
4736        "pointer_expression" => {
4737            let operator = node
4738                .child_by_field_name("operator")
4739                .or_else(|| node.child(0))
4740                .map(|operator| node_text(operator, source))
4741                .unwrap_or("*");
4742            let argument = node
4743                .child_by_field_name("argument")
4744                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
4745                .unwrap_or_default();
4746            format!("{operator}{argument}")
4747        }
4748        "unary_expression" => {
4749            let operator = node
4750                .child_by_field_name("operator")
4751                .or_else(|| node.child(0))
4752                .map(|operator| node_text(operator, source))
4753                .unwrap_or_default();
4754            let argument = node
4755                .child_by_field_name("argument")
4756                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
4757                .unwrap_or_default();
4758            format!("{operator}{argument}")
4759        }
4760        "identifier" | "field_identifier" => String::new(),
4761        _ => cpp_declarator_suffix_without_name(node, source),
4762    }
4763}
4764
4765fn recovered_macro_qualified_field_initializer<'tree>(
4766    clause: Node<'tree>,
4767    declarator: Node<'tree>,
4768) -> Option<Node<'tree>> {
4769    let mut stack = vec![clause];
4770    while let Some(current) = stack.pop() {
4771        if current.kind() == "assignment_expression"
4772            && current
4773                .child_by_field_name("left")
4774                .is_some_and(|left| same_node(left, declarator))
4775        {
4776            return current.child_by_field_name("right");
4777        }
4778        let mut cursor = current.walk();
4779        stack.extend(current.named_children(&mut cursor));
4780    }
4781    None
4782}
4783
4784fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
4785    let text = node_text(node, source);
4786    let mut cursor = node.walk();
4787    let first_declarator = node.named_children(&mut cursor).find(|child| {
4788        matches!(
4789            child.kind(),
4790            "init_declarator"
4791                | "identifier"
4792                | "field_identifier"
4793                | "pointer_declarator"
4794                | "reference_declarator"
4795                | "array_declarator"
4796                | "function_declarator"
4797        )
4798    });
4799    let prefix = if let Some(first_declarator) = first_declarator {
4800        let end = first_declarator
4801            .start_byte()
4802            .saturating_sub(node.start_byte());
4803        let mut prefix = text.get(..end).unwrap_or(text).to_string();
4804        let declarator_suffix = match first_declarator.kind() {
4805            "init_declarator" => first_declarator
4806                .child_by_field_name("declarator")
4807                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
4808                .unwrap_or_default(),
4809            _ => cpp_declarator_suffix_without_name(first_declarator, source),
4810        };
4811        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
4812            prefix.push_str(&declarator_suffix);
4813        }
4814        return collapse_cpp_whitespace(&prefix)
4815            .trim_end_matches(',')
4816            .trim_end_matches(';')
4817            .trim()
4818            .to_string();
4819    } else {
4820        text
4821    };
4822    collapse_cpp_whitespace(prefix)
4823        .trim_end_matches(',')
4824        .trim_end_matches(';')
4825        .trim()
4826        .to_string()
4827}
4828
4829fn cpp_preserved_initializer(
4830    declaration_node: Node<'_>,
4831    declarator: Node<'_>,
4832    source: &str,
4833) -> Option<String> {
4834    let name = extract_variable_name(declarator, source)?;
4835    let mut cursor = declaration_node.walk();
4836    for child in declaration_node.named_children(&mut cursor) {
4837        if child.kind() != "init_declarator" {
4838            continue;
4839        }
4840        let Some(inner) = child.child_by_field_name("declarator") else {
4841            continue;
4842        };
4843        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
4844            continue;
4845        }
4846        let value = child.child_by_field_name("value")?;
4847        let kind = value.kind();
4848        if matches!(
4849            kind,
4850            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
4851        ) {
4852            return Some(normalize_cpp_whitespace(node_text(value, source)));
4853        }
4854        break;
4855    }
4856    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
4857    let pattern = format!(
4858        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
4859        regex::escape(&name)
4860    );
4861    Regex::new(&pattern)
4862        .ok()
4863        .and_then(|regex| regex.captures(&declaration_text))
4864        .and_then(|captures| captures.get(1))
4865        .map(|value| value.as_str().to_string())
4866}
4867
4868fn render_cpp_function_display_signature_from_node(
4869    node: Node<'_>,
4870    source: &str,
4871    template_signature: Option<&str>,
4872    has_body: bool,
4873) -> String {
4874    let root = enclosing_cpp_declaration_node(node).unwrap_or(node);
4875    let parent_text = node_text(root, source);
4876    let body_local_start = root
4877        .child_by_field_name("body")
4878        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
4879        .unwrap_or(parent_text.len());
4880    let display = parent_text
4881        .get(..body_local_start)
4882        .unwrap_or(parent_text)
4883        .trim()
4884        .trim();
4885    let display = if let Some(template_signature) = template_signature {
4886        if display.starts_with("template ") {
4887            display.to_string()
4888        } else {
4889            format!("template {template_signature} {display}")
4890        }
4891    } else {
4892        display.to_string()
4893    };
4894    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
4895    if has_body {
4896        format!("{display} {{...}}")
4897    } else {
4898        format!("{display};")
4899    }
4900}
4901
4902fn cpp_template_signature(
4903    template_node: Node<'_>,
4904    declaration_child: Node<'_>,
4905    source: &str,
4906) -> Option<String> {
4907    let text = source
4908        .get(template_node.start_byte()..declaration_child.start_byte())
4909        .unwrap_or("");
4910    let text = normalize_cpp_whitespace(text);
4911    let start = text.find('<')?;
4912    let end = text.rfind('>')?;
4913    if end < start {
4914        return None;
4915    }
4916    Some(text[start..=end].to_string())
4917}
4918
4919struct RecoveredFragmentedPartialSpecialization<'tree> {
4920    declaration_node: Node<'tree>,
4921    name: String,
4922    range: Range,
4923    prefix_members: Vec<Node<'tree>>,
4924    member_siblings: Vec<Node<'tree>>,
4925    following_declarations: Vec<Node<'tree>>,
4926}
4927
4928struct RecoveredFragmentedPreprocessorClass<'tree> {
4929    declaration_node: Node<'tree>,
4930    class_node: Node<'tree>,
4931    body: Node<'tree>,
4932    name: String,
4933    range: Range,
4934    tail_members: Vec<Node<'tree>>,
4935    member_siblings: Vec<Node<'tree>>,
4936}
4937
4938/// Recover a class whose preprocessor-fragmented parse closes at an early
4939/// member body and publishes the remaining in-class declarations as siblings
4940/// of the surrounding alternative. Primary classes are admitted only when an
4941/// earlier branch contains the matching bodyless declaration and the class
4942/// node retains the displaced `#endif`. Partial specializations instead carry
4943/// their identity structurally in the `template_type` name and template
4944/// metadata. Retain the original AST nodes and re-own only the siblings through
4945/// the displaced structural `};` terminator.
4946fn recover_fragmented_preprocessor_class<'tree>(
4947    template_node: Node<'tree>,
4948    source: &str,
4949) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
4950    let alternative = template_node.parent()?;
4951    if alternative.kind() != "preproc_else" {
4952        return None;
4953    }
4954    let conditional = alternative.parent()?;
4955    if conditional.kind() != "preproc_if" {
4956        return None;
4957    }
4958    let declaration_node = template_node
4959        .named_children(&mut template_node.walk())
4960        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
4961    let class_node = declaration_node
4962        .named_children(&mut declaration_node.walk())
4963        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
4964    let body = cpp_body_node(class_node)?;
4965    if class_node.end_byte() >= declaration_node.end_byte() {
4966        return None;
4967    }
4968    let name = class_like_name(class_node, source)?;
4969    let is_partial_specialization = class_node
4970        .child_by_field_name("name")
4971        .is_some_and(|class_name| class_name.kind() == "template_type");
4972    if is_partial_specialization {
4973        let metadata = cpp_template_metadata(template_node, class_node, source)?;
4974        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
4975            return None;
4976        }
4977    } else {
4978        if !class_has_displaced_preprocessor_terminator(class_node) {
4979            return None;
4980        }
4981        let matching_other_branch = conditional
4982            .named_children(&mut conditional.walk())
4983            .take_while(|child| !same_node(*child, alternative))
4984            .filter(|child| child.kind() == "template_declaration")
4985            .filter_map(first_class_like_child)
4986            .any(|candidate| {
4987                cpp_body_node(candidate).is_none()
4988                    && class_like_name(candidate, source).as_deref() == Some(name.as_str())
4989            });
4990        if !matching_other_branch {
4991            return None;
4992        }
4993    }
4994
4995    let mut tail_members = Vec::new();
4996    let mut saw_class = false;
4997    let mut declaration_cursor = declaration_node.walk();
4998    for child in declaration_node.named_children(&mut declaration_cursor) {
4999        if same_node(child, class_node) {
5000            saw_class = true;
5001        } else if saw_class {
5002            tail_members.push(child);
5003        }
5004    }
5005
5006    let mut member_siblings = Vec::new();
5007    let mut saw_template = false;
5008    let mut terminator = None;
5009    for index in 0..alternative.child_count() {
5010        let Some(child) = alternative.child(index) else {
5011            continue;
5012        };
5013        if same_node(child, template_node) {
5014            saw_template = true;
5015            continue;
5016        }
5017        if !saw_template {
5018            continue;
5019        }
5020        if displaced_fragmented_class_terminator(alternative, index) {
5021            terminator = alternative.child(index + 1);
5022            break;
5023        }
5024        if child.is_named() {
5025            member_siblings.push(child);
5026        }
5027    }
5028    let terminator = terminator?;
5029    Some(RecoveredFragmentedPreprocessorClass {
5030        declaration_node,
5031        class_node,
5032        body,
5033        name,
5034        range: Range {
5035            start_byte: class_node.start_byte(),
5036            end_byte: terminator.end_byte(),
5037            start_line: class_node.start_position().row + 1,
5038            end_line: terminator.end_position().row + 1,
5039        },
5040        tail_members,
5041        member_siblings,
5042    })
5043}
5044
5045fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
5046    (0..class_node.child_count()).any(|index| {
5047        class_node.child(index).is_some_and(|child| {
5048            child.kind() == "ERROR"
5049                && (0..child.child_count()).any(|error_index| {
5050                    child
5051                        .child(error_index)
5052                        .is_some_and(|token| token.kind() == "#endif")
5053                })
5054        })
5055    })
5056}
5057
5058fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
5059    let Some(error) = parent.child(error_index) else {
5060        return false;
5061    };
5062    if error.kind() != "ERROR"
5063        || error.child_count() != 1
5064        || error.child(0).is_none_or(|child| child.kind() != "}")
5065    {
5066        return false;
5067    }
5068    let Some(semicolon) = parent.child(error_index + 1) else {
5069        return false;
5070    };
5071    semicolon.kind() == "expression_statement"
5072        && semicolon.child_count() == 1
5073        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
5074}
5075
5076/// Locate the real end of a class-like declaration when a macro invocation
5077/// without a source semicolon absorbs the class's `};` into its parsed field.
5078/// The grammar then keeps following namespace declarations as later children
5079/// of the same field list. The direct ERROR-plus-semicolon pair proves the
5080/// boundary structurally; no source-text delimiter scan is needed.
5081fn displaced_macro_class_tail(
5082    declaration_node: Node<'_>,
5083    body: Node<'_>,
5084    source: &str,
5085) -> Option<DisplacedMacroClassTail> {
5086    if !matches!(
5087        declaration_node.kind(),
5088        "class_specifier" | "struct_specifier" | "union_specifier"
5089    ) || body.kind() != "field_declaration_list"
5090    {
5091        return None;
5092    }
5093
5094    let child_count = body.named_child_count();
5095    for index in 0..child_count {
5096        let child = body.named_child(index)?;
5097        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
5098            continue;
5099        };
5100        let split_index = index + 1;
5101        if split_index >= child_count {
5102            return None;
5103        }
5104        let mut cursor = body.walk();
5105        if !body
5106            .named_children(&mut cursor)
5107            .skip(split_index)
5108            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
5109        {
5110            return None;
5111        }
5112        return Some(DisplacedMacroClassTail {
5113            split_index,
5114            class_range: Range {
5115                start_byte: declaration_node.start_byte(),
5116                end_byte: terminator.end_byte(),
5117                start_line: declaration_node.start_position().row + 1,
5118                end_line: terminator.end_position().row + 1,
5119            },
5120        });
5121    }
5122    None
5123}
5124
5125fn displaced_macro_field_terminator<'tree>(
5126    field: Node<'tree>,
5127    source: &str,
5128) -> Option<Node<'tree>> {
5129    if field.kind() != "field_declaration" {
5130        return None;
5131    }
5132    let macro_type = field.child_by_field_name("type")?;
5133    if macro_type.kind() != "type_identifier"
5134        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
5135        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
5136    {
5137        return None;
5138    }
5139    for index in 0..field.child_count() {
5140        let error = field.child(index)?;
5141        if error.kind() != "ERROR"
5142            || error.child_count() != 1
5143            || error.child(0).is_none_or(|child| child.kind() != "}")
5144        {
5145            continue;
5146        }
5147        let semicolon = field.child(index + 1)?;
5148        if semicolon.kind() == ";" {
5149            return Some(semicolon);
5150        }
5151    }
5152    None
5153}
5154
5155fn recover_fragmented_partial_specialization<'tree>(
5156    template_node: Node<'tree>,
5157    declaration_child: Node<'tree>,
5158    source: &str,
5159) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
5160    if declaration_child.kind() != "function_definition" {
5161        return None;
5162    }
5163    let class_node = declaration_child.child_by_field_name("type")?;
5164    if !matches!(
5165        class_node.kind(),
5166        "class_specifier" | "struct_specifier" | "union_specifier"
5167    ) || !class_node
5168        .child_by_field_name("name")
5169        .and_then(|name| direct_identifier_name(name, source))
5170        .is_some_and(|name| cpp_export_macro_token(&name))
5171    {
5172        return None;
5173    }
5174    let declarator = declaration_child.child_by_field_name("declarator")?;
5175    if declarator.kind() != "template_function" {
5176        return None;
5177    }
5178    let metadata = cpp_template_metadata(template_node, declaration_child, source)?;
5179    if metadata.specialization_arguments.is_empty() {
5180        return None;
5181    }
5182    let body = declaration_child.child_by_field_name("body")?;
5183    if body.kind() != "compound_statement" {
5184        return None;
5185    }
5186    let complete_prefix = body.named_child(0).filter(|first| {
5187        first.kind() == "labeled_statement"
5188            && first.has_error()
5189            && first
5190                .named_child(first.named_child_count().saturating_sub(1))
5191                .is_some_and(recovered_declaration_has_class_terminator)
5192    });
5193    let complete_body = complete_prefix.is_some();
5194    let mut prefix_members = Vec::new();
5195    if let Some(prefix) = complete_prefix {
5196        prefix_members.push(prefix);
5197    } else {
5198        let mut body_cursor = body.walk();
5199        for child in body.named_children(&mut body_cursor) {
5200            if !is_structurally_valid_fragmented_class_prefix_member(child) {
5201                break;
5202            }
5203            prefix_members.push(child);
5204        }
5205    }
5206    let containing_declarations = template_node.parent()?;
5207    if !matches!(
5208        containing_declarations.kind(),
5209        "declaration_list" | "compound_statement"
5210    ) {
5211        return None;
5212    }
5213    let mut member_siblings = Vec::new();
5214    let mut following_declarations = Vec::new();
5215    let terminator;
5216    if complete_body {
5217        terminator = complete_prefix?;
5218        let mut cursor = body.walk();
5219        let mut after_prefix = false;
5220        for child in body.named_children(&mut cursor) {
5221            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
5222                after_prefix = true;
5223            } else if after_prefix {
5224                following_declarations.push(child);
5225            }
5226        }
5227    } else {
5228        let mut found_template = false;
5229        let mut cursor = containing_declarations.walk();
5230        let mut class_terminator = None;
5231        for child in containing_declarations.children(&mut cursor) {
5232            if same_node(child, template_node) {
5233                found_template = true;
5234                continue;
5235            }
5236            if found_template && child.kind() == "}" {
5237                class_terminator = Some(child);
5238                break;
5239            }
5240            if found_template && child.is_named() {
5241                member_siblings.push(child);
5242            }
5243        }
5244        terminator = class_terminator?;
5245    }
5246    let name = format!(
5247        "{}<{}>",
5248        metadata.primary_name,
5249        metadata
5250            .specialization_arguments
5251            .iter()
5252            .map(|argument| argument.text.as_str())
5253            .collect::<Vec<_>>()
5254            .join(", ")
5255    );
5256    Some(RecoveredFragmentedPartialSpecialization {
5257        declaration_node: declaration_child,
5258        name,
5259        range: Range {
5260            start_byte: declaration_child.start_byte(),
5261            end_byte: terminator.end_byte(),
5262            start_line: declaration_child.start_position().row + 1,
5263            end_line: terminator.end_position().row + 1,
5264        },
5265        prefix_members,
5266        member_siblings,
5267        following_declarations,
5268    })
5269}
5270
5271fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
5272    if declaration.kind() != "declaration" {
5273        return false;
5274    }
5275    // With an export macro between `class` and its name, tree-sitter folds a
5276    // complete class body into a function-shaped declaration. The class's own
5277    // `};` remains structurally identifiable as a direct ERROR child holding
5278    // `}`, immediately followed by the declaration's direct `;` child.
5279    (0..declaration.child_count().saturating_sub(1)).any(|index| {
5280        let Some(error) = declaration.child(index) else {
5281            return false;
5282        };
5283        error.kind() == "ERROR"
5284            && error.child_count() == 1
5285            && error.child(0).is_some_and(|child| child.kind() == "}")
5286            && declaration
5287                .child(index + 1)
5288                .is_some_and(|child| child.kind() == ";")
5289    })
5290}
5291
5292fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
5293    if node.has_error() {
5294        return false;
5295    }
5296    match node.kind() {
5297        "declaration"
5298        | "field_declaration"
5299        | "alias_declaration"
5300        | "type_definition"
5301        | "static_assert_declaration" => true,
5302        "labeled_statement" => node
5303            .named_child(node.named_child_count().saturating_sub(1))
5304            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
5305        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
5306            matches!(
5307                child.kind(),
5308                "declaration"
5309                    | "field_declaration"
5310                    | "alias_declaration"
5311                    | "type_definition"
5312                    | "function_definition"
5313            )
5314        }),
5315        _ => false,
5316    }
5317}
5318
5319fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
5320    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
5321        .then(|| node.child_by_field_name("declarator"))
5322        .flatten()
5323        .and_then(|declarator| extract_variable_name(declarator, source))
5324}
5325
5326fn cpp_template_metadata(
5327    template_node: Node<'_>,
5328    declaration_child: Node<'_>,
5329    source: &str,
5330) -> Option<CppTemplateMetadata> {
5331    let parameters_node = template_node.child_by_field_name("parameters")?;
5332    let name_node = cpp_templated_class_name_node(declaration_child)?;
5333    let primary_node = match name_node.kind() {
5334        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
5335        _ => name_node,
5336    };
5337    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
5338    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
5339        return None;
5340    }
5341
5342    let mut parameter_nodes = Vec::new();
5343    let mut parameter_names = Vec::new();
5344    let mut cursor = parameters_node.walk();
5345    for parameter in parameters_node.named_children(&mut cursor) {
5346        let Some(name) = cpp_template_parameter_name(parameter, source) else {
5347            continue;
5348        };
5349        parameter_names.push(name);
5350        parameter_nodes.push(parameter);
5351    }
5352    let parameters = parameter_nodes
5353        .into_iter()
5354        .zip(parameter_names.iter().cloned())
5355        .map(|(parameter, name)| CppTemplateParameterMetadata {
5356            name,
5357            kind: cpp_template_parameter_kind(parameter),
5358            variadic: matches!(
5359                parameter.kind(),
5360                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
5361            ),
5362            default: cpp_template_parameter_default_expression(parameter, source, &parameter_names),
5363        })
5364        .collect();
5365    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
5366        Vec::new()
5367    } else {
5368        cpp_template_argument_expressions(name_node, source, &parameter_names).unwrap_or_default()
5369    };
5370    let alias_target = (declaration_child.kind() == "alias_declaration")
5371        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names))
5372        .flatten();
5373    Some(CppTemplateMetadata {
5374        primary_name,
5375        primary_fq_name: String::new(),
5376        parameters,
5377        specialization_arguments,
5378        alias_target,
5379    })
5380}
5381
5382fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
5383    match node.kind() {
5384        "class_specifier" | "struct_specifier" | "union_specifier" => {
5385            node.child_by_field_name("name")
5386        }
5387        "function_definition" => {
5388            let declarator = node.child_by_field_name("declarator")?;
5389            if matches!(declarator.kind(), "identifier" | "template_function") {
5390                Some(declarator)
5391            } else {
5392                None
5393            }
5394        }
5395        "alias_declaration" => node.child_by_field_name("name"),
5396        _ => None,
5397    }
5398}
5399
5400fn cpp_template_alias_target(
5401    alias: Node<'_>,
5402    source: &str,
5403    parameter_names: &[String],
5404) -> Option<CppTemplateAliasTargetMetadata> {
5405    let mut type_node = alias.child_by_field_name("type")?;
5406    while type_node.kind() == "type_descriptor" {
5407        type_node = type_node.child_by_field_name("type")?;
5408    }
5409    let global = type_node.child_by_field_name("scope").is_none()
5410        && type_node.child(0).is_some_and(|child| child.kind() == "::");
5411    let mut components = Vec::new();
5412    cpp_template_target_components(type_node, source, &mut components)?;
5413    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names);
5414    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
5415        components,
5416        global,
5417        arguments,
5418    })
5419}
5420
5421fn cpp_template_target_components(
5422    node: Node<'_>,
5423    source: &str,
5424    out: &mut Vec<String>,
5425) -> Option<()> {
5426    match node.kind() {
5427        "identifier" | "namespace_identifier" | "type_identifier" => {
5428            out.push(node_text(node, source).to_string());
5429            Some(())
5430        }
5431        "template_type" => {
5432            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
5433        }
5434        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
5435            if let Some(scope) = node.child_by_field_name("scope") {
5436                cpp_template_target_components(scope, source, out)?;
5437            }
5438            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
5439        }
5440        _ => None,
5441    }
5442}
5443
5444fn cpp_template_argument_expressions(
5445    mut node: Node<'_>,
5446    source: &str,
5447    parameter_names: &[String],
5448) -> Option<Vec<CppTemplateExpression>> {
5449    loop {
5450        match node.kind() {
5451            "template_type" | "template_function" => {
5452                let arguments = node.child_by_field_name("arguments")?;
5453                let mut cursor = arguments.walk();
5454                return Some(
5455                    arguments
5456                        .named_children(&mut cursor)
5457                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
5458                        .map(|argument| cpp_template_expression(argument, source, parameter_names))
5459                        .collect(),
5460                );
5461            }
5462            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
5463                node = node
5464                    .child_by_field_name("name")
5465                    .or_else(|| node.child_by_field_name("type"))?;
5466            }
5467            _ => return None,
5468        }
5469    }
5470}
5471
5472fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
5473    let candidate = node
5474        .child_by_field_name("name")
5475        .or_else(|| node.child_by_field_name("declarator"))
5476        .or_else(|| {
5477            let mut cursor = node.walk();
5478            node.named_children(&mut cursor).find(|child| {
5479                matches!(
5480                    child.kind(),
5481                    "identifier" | "type_identifier" | "field_identifier"
5482                )
5483            })
5484        })?;
5485    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
5486    (!name.is_empty()).then_some(name)
5487}
5488
5489fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
5490    match node.kind() {
5491        "type_parameter_declaration"
5492        | "optional_type_parameter_declaration"
5493        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
5494        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
5495        _ => CppTemplateParameterKind::Value,
5496    }
5497}
5498
5499fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
5500    node.child_by_field_name("default_type")
5501        .or_else(|| node.child_by_field_name("default_value"))
5502}
5503
5504fn cpp_template_parameter_default_expression(
5505    parameter: Node<'_>,
5506    source: &str,
5507    parameter_names: &[String],
5508) -> Option<CppTemplateExpression> {
5509    let default = cpp_template_parameter_default(parameter)?;
5510    let base = cpp_template_expression(default, source, parameter_names);
5511    let Some(pointer_error) = parameter.next_named_sibling() else {
5512        return Some(base);
5513    };
5514    let Some(pointer_declarator) =
5515        recovered_abstract_pointer_declarator_term(pointer_error, source)
5516    else {
5517        return Some(base);
5518    };
5519    Some(CppTemplateExpression {
5520        text: format!(
5521            "{}{}",
5522            base.text,
5523            normalize_cpp_whitespace(node_text(pointer_error, source))
5524        ),
5525        term: CppTemplateTerm::Node {
5526            kind: "type_descriptor".to_string(),
5527            children: vec![base.term, pointer_declarator],
5528        },
5529    })
5530}
5531
5532fn recovered_abstract_pointer_declarator_term(
5533    node: Node<'_>,
5534    source: &str,
5535) -> Option<CppTemplateTerm> {
5536    if node.kind() != "ERROR" || node.child_count() == 0 {
5537        return None;
5538    }
5539    let mut children = Vec::new();
5540    for index in 0..node.child_count() {
5541        let child = node.child(index)?;
5542        if child.kind() != "*" {
5543            return None;
5544        }
5545        children.push(CppTemplateTerm::Atom {
5546            kind: "*".to_string(),
5547            text: normalize_cpp_whitespace(node_text(child, source)),
5548        });
5549    }
5550    Some(CppTemplateTerm::Node {
5551        kind: "abstract_pointer_declarator".to_string(),
5552        children,
5553    })
5554}
5555
5556fn cpp_template_expression(
5557    node: Node<'_>,
5558    source: &str,
5559    parameter_names: &[String],
5560) -> CppTemplateExpression {
5561    let text = normalize_cpp_whitespace(node_text(node, source));
5562    CppTemplateExpression {
5563        text,
5564        term: cpp_template_term(node, source, parameter_names),
5565    }
5566}
5567
5568pub fn cpp_template_term(
5569    node: Node<'_>,
5570    source: &str,
5571    parameter_names: &[String],
5572) -> CppTemplateTerm {
5573    enum Work<'tree> {
5574        Visit(Node<'tree>),
5575        Build { kind: String, child_count: usize },
5576    }
5577
5578    let mut work = vec![Work::Visit(node)];
5579    let mut terms = Vec::new();
5580    while let Some(next) = work.pop() {
5581        match next {
5582            Work::Visit(current) => {
5583                let text = normalize_cpp_whitespace(node_text(current, source));
5584                if parameter_names.contains(&text) {
5585                    terms.push(CppTemplateTerm::Parameter(text));
5586                    continue;
5587                }
5588                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
5589                    let mut cursor = current.walk();
5590                    let named = current
5591                        .named_children(&mut cursor)
5592                        .filter(|child| !child.is_extra() && child.kind() != "comment")
5593                        .collect::<Vec<_>>();
5594                    if let [child] = named.as_slice() {
5595                        work.push(Work::Visit(*child));
5596                        continue;
5597                    }
5598                }
5599                if current.child_count() == 0 {
5600                    terms.push(CppTemplateTerm::Atom {
5601                        kind: if matches!(
5602                            current.kind(),
5603                            "identifier"
5604                                | "type_identifier"
5605                                | "field_identifier"
5606                                | "namespace_identifier"
5607                        ) {
5608                            "identifier".to_string()
5609                        } else {
5610                            current.kind().to_string()
5611                        },
5612                        text,
5613                    });
5614                    continue;
5615                }
5616                let children = (0..current.child_count())
5617                    .filter_map(|index| current.child(index))
5618                    .filter(|child| !child.is_extra() && child.kind() != "comment")
5619                    .collect::<Vec<_>>();
5620                work.push(Work::Build {
5621                    kind: current.kind().to_string(),
5622                    child_count: children.len(),
5623                });
5624                work.extend(children.into_iter().rev().map(Work::Visit));
5625            }
5626            Work::Build { kind, child_count } => {
5627                let children = terms.split_off(terms.len() - child_count);
5628                terms.push(CppTemplateTerm::Node { kind, children });
5629            }
5630        }
5631    }
5632    terms.pop().expect("template term traversal emits one root")
5633}
5634
5635fn enclosing_cpp_declaration_node(mut node: Node<'_>) -> Option<Node<'_>> {
5636    loop {
5637        match node.kind() {
5638            "declaration"
5639            | "function_declaration"
5640            | "field_declaration"
5641            | "function_definition" => return Some(node),
5642            _ => node = node.parent()?,
5643        }
5644    }
5645}
5646
5647fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
5648    let mut params = Vec::new();
5649    let mut cursor = parameters_node.walk();
5650    for child in parameters_node.children(&mut cursor) {
5651        match child.kind() {
5652            "parameter_declaration" | "optional_parameter_declaration" => {
5653                params.push(cpp_parameter_type(child, source));
5654            }
5655            "variadic_parameter_declaration" => {
5656                params.push(cpp_parameter_type(child, source));
5657            }
5658            "variadic_parameter" | "..." => params.push("...".to_string()),
5659            _ => {}
5660        }
5661    }
5662
5663    if params.is_empty() {
5664        "()".to_string()
5665    } else {
5666        format!("({})", params.join(", "))
5667    }
5668}
5669
5670fn cpp_signature_metadata(
5671    signature: String,
5672    function_declarator: Node<'_>,
5673    source: &str,
5674) -> SignatureMetadata {
5675    let dispatch = cpp_callable_dispatch_extensibility(function_declarator);
5676    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
5677    let return_type_text = cpp_callable_return_type_text(function_declarator, source);
5678    let return_type_identity = cpp_callable_return_type_identity(function_declarator, source);
5679    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
5680        return enrich(
5681            SignatureMetadata::new(signature, Vec::new())
5682                .with_return_type_text(return_type_text)
5683                .with_return_type_identity(return_type_identity),
5684        );
5685    };
5686    let callable_arity = cpp_callable_arity(parameters_node, source);
5687    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
5688    let search_from = cpp_signature_search_start(&signature, function_declarator, source);
5689    let Some(relative_start) = signature
5690        .get(search_from..)
5691        .and_then(|suffix| suffix.find(&parameter_text))
5692    else {
5693        return enrich(
5694            SignatureMetadata::new(signature, Vec::new())
5695                .with_callable_arity(callable_arity)
5696                .with_return_type_text(return_type_text)
5697                .with_return_type_identity(return_type_identity),
5698        );
5699    };
5700    let parameters_start = search_from + relative_start;
5701    let parameters_end = parameters_start + parameter_text.len();
5702    let mut search_start = parameters_start;
5703    let parameters = cpp_parameter_label_nodes(parameters_node)
5704        .into_iter()
5705        .filter_map(|label_node| {
5706            let label = normalize_cpp_whitespace(node_text(label_node, source));
5707            if label.is_empty() || search_start > parameters_end {
5708                return None;
5709            }
5710            let haystack = signature.get(search_start..parameters_end)?;
5711            let relative_start = haystack.find(&label)?;
5712            let start_byte = search_start + relative_start;
5713            let end_byte = start_byte + label.len();
5714            search_start = end_byte;
5715            Some(ParameterMetadata::new(label, start_byte, end_byte))
5716        })
5717        .collect();
5718    enrich(
5719        SignatureMetadata::new(signature, parameters)
5720            .with_callable_arity(callable_arity)
5721            .with_return_type_text(return_type_text)
5722            .with_return_type_identity(return_type_identity),
5723    )
5724}
5725
5726fn cpp_callable_is_structural_constructor(function_declarator: Node<'_>, source: &str) -> bool {
5727    let Some(name_node) = function_declarator
5728        .child_by_field_name("declarator")
5729        .or_else(|| function_declarator.child_by_field_name("name"))
5730        .or_else(|| last_named_child(function_declarator))
5731    else {
5732        return false;
5733    };
5734    let Some(callable_name) = direct_identifier_name(name_node, source) else {
5735        return false;
5736    };
5737
5738    let mut current = function_declarator.parent();
5739    while let Some(ancestor) = current {
5740        let owner_name = match ancestor.kind() {
5741            "class_specifier" | "struct_specifier" | "union_specifier" => {
5742                class_like_name(ancestor, source)
5743            }
5744            "ERROR" => malformed_class_error_owner_name(ancestor, source),
5745            _ => None,
5746        };
5747        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
5748            return true;
5749        }
5750        current = ancestor.parent();
5751    }
5752    false
5753}
5754
5755/// Recover the owner name from the direct grammar shape retained when a later
5756/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
5757/// `ERROR` node:
5758///
5759/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
5760///
5761/// Direct-child checks keep this distinct from an unrelated nested class inside
5762/// a broader error region. The closing brace may be displaced past the error
5763/// node, so the opening body token is the available structural boundary.
5764fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
5765    if node.kind() != "ERROR" {
5766        return None;
5767    }
5768    let keyword = node.child(0)?;
5769    if !matches!(keyword.kind(), "class" | "struct" | "union") {
5770        return None;
5771    }
5772    let name_node = node.child(1)?;
5773    let name = direct_identifier_name(name_node, source)?;
5774    let has_body = (2..node.child_count())
5775        .filter_map(|index| node.child(index))
5776        .any(|child| child.kind() == "{");
5777    has_body.then_some(name)
5778}
5779
5780fn cpp_callable_return_type_identity(
5781    function_declarator: Node<'_>,
5782    source: &str,
5783) -> Option<StructuredTypeIdentity> {
5784    if cpp_callable_is_structural_constructor(function_declarator, source) {
5785        return None;
5786    }
5787    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
5788    let mut cursor = function_declarator.walk();
5789    if let Some(trailing) = function_declarator
5790        .named_children(&mut cursor)
5791        .find(|child| child.kind() == "trailing_return_type")
5792        && let Some(type_descriptor) = trailing.named_child(0)
5793    {
5794        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
5795    }
5796
5797    let mut current = function_declarator;
5798    let mut wrappers = Vec::new();
5799    while let Some(parent) = current.parent() {
5800        if matches!(
5801            parent.kind(),
5802            "function_definition" | "declaration" | "field_declaration"
5803        ) {
5804            let type_node = parent.child_by_field_name("type")?;
5805            if cpp_export_macro_token(node_text(type_node, source))
5806                && (0..parent.named_child_count()).any(|index| {
5807                    parent
5808                        .named_child(index)
5809                        .is_some_and(|child| child.kind() == "ERROR")
5810                })
5811            {
5812                return None;
5813            }
5814            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
5815            for wrapper in wrappers.into_iter().rev() {
5816                identity = cpp_wrap_structured_type(identity, wrapper)?;
5817            }
5818            return Some(identity);
5819        }
5820        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
5821            || (matches!(
5822                parent.kind(),
5823                "pointer_declarator"
5824                    | "reference_declarator"
5825                    | "array_declarator"
5826                    | "parenthesized_declarator"
5827            ) && parent.named_child_count() == 1
5828                && parent.named_child(0) == Some(current));
5829        if !wraps_current_declarator {
5830            return None;
5831        }
5832        match parent.kind() {
5833            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
5834            "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
5835            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
5836            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
5837            _ => return None,
5838        }
5839        current = parent;
5840    }
5841    None
5842}
5843
5844fn cpp_structured_type_identity(
5845    node: Node<'_>,
5846    source: &str,
5847    lexical_scope: &[String],
5848) -> Option<StructuredTypeIdentity> {
5849    enum Work<'tree> {
5850        Visit(Node<'tree>),
5851        Wrap(CppStructuredTypeWrapper),
5852        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
5853        BuildGeneric { argument_count: usize },
5854    }
5855
5856    let mut work = vec![Work::Visit(node)];
5857    let mut values = Vec::new();
5858    let mut builder = StructuredTypeIdentityBuilder::default();
5859    while let Some(next) = work.pop() {
5860        match next {
5861            Work::Visit(current) => match current.kind() {
5862                "type_descriptor" => {
5863                    let type_node = current
5864                        .child_by_field_name("type")
5865                        .or_else(|| current.named_child(0))?;
5866                    let mut wrappers = Vec::new();
5867                    let mut cursor = current.walk();
5868                    for child in current.named_children(&mut cursor) {
5869                        if child.id() != type_node.id() {
5870                            wrappers.extend(cpp_structured_declarator_wrappers(child));
5871                        }
5872                    }
5873                    work.push(Work::ApplyWrappers(wrappers));
5874                    work.push(Work::Visit(type_node));
5875                }
5876                "pointer_declarator" | "abstract_pointer_declarator" => {
5877                    let child = current
5878                        .child_by_field_name("declarator")
5879                        .or_else(|| current.named_child(0))?;
5880                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
5881                    work.push(Work::Visit(child));
5882                }
5883                "reference_declarator" => {
5884                    let child = current
5885                        .child_by_field_name("declarator")
5886                        .or_else(|| current.named_child(0))?;
5887                    work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
5888                    work.push(Work::Visit(child));
5889                }
5890                "array_declarator" | "abstract_array_declarator" => {
5891                    let child = current
5892                        .child_by_field_name("declarator")
5893                        .or_else(|| current.named_child(0))?;
5894                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
5895                    work.push(Work::Visit(child));
5896                }
5897                "template_type" => {
5898                    let name_node = current.child_by_field_name("name")?;
5899                    let arguments = current
5900                        .child_by_field_name("arguments")
5901                        .map(|arguments_node| {
5902                            let mut cursor = arguments_node.walk();
5903                            arguments_node
5904                                .named_children(&mut cursor)
5905                                .filter(|child| !child.is_extra() && child.kind() != "comment")
5906                                .collect::<Vec<_>>()
5907                        })
5908                        .unwrap_or_default();
5909                    work.push(Work::BuildGeneric {
5910                        argument_count: arguments.len(),
5911                    });
5912                    work.extend(arguments.into_iter().rev().map(Work::Visit));
5913                    work.push(Work::Visit(name_node));
5914                }
5915                "qualified_identifier"
5916                | "scoped_identifier"
5917                | "scoped_type_identifier"
5918                | "type_identifier"
5919                | "identifier"
5920                | "namespace_identifier"
5921                | "primitive_type" => {
5922                    values.push(builder.named(cpp_structured_named_type(
5923                        current,
5924                        source,
5925                        lexical_scope,
5926                    )?)?);
5927                }
5928                _ => {
5929                    let child = current.child_by_field_name("type").or_else(|| {
5930                        (current.named_child_count() == 1)
5931                            .then(|| current.named_child(0))
5932                            .flatten()
5933                    })?;
5934                    work.push(Work::Visit(child));
5935                }
5936            },
5937            Work::Wrap(wrapper) => {
5938                let root = values.pop()?;
5939                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
5940            }
5941            Work::ApplyWrappers(wrappers) => {
5942                let mut root = values.pop()?;
5943                for wrapper in wrappers.into_iter().rev() {
5944                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
5945                }
5946                values.push(root);
5947            }
5948            Work::BuildGeneric { argument_count } => {
5949                let value_count = argument_count.checked_add(1)?;
5950                let start = values.len().checked_sub(value_count)?;
5951                let mut built = values.split_off(start);
5952                let base = built.remove(0);
5953                values.push(builder.generic(base, built)?);
5954            }
5955        }
5956    }
5957    (values.len() == 1)
5958        .then(|| values.pop())
5959        .flatten()
5960        .and_then(|root| builder.finish(root))
5961}
5962
5963fn cpp_structured_named_type(
5964    node: Node<'_>,
5965    source: &str,
5966    lexical_scope: &[String],
5967) -> Option<StructuredTypeName> {
5968    let path = cpp_structured_type_path(node, source)?;
5969    let absolute = node.child_by_field_name("scope").is_none()
5970        && node.child(0).is_some_and(|child| child.kind() == "::");
5971    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
5972}
5973
5974#[derive(Clone, Copy)]
5975enum CppStructuredTypeWrapper {
5976    Pointer,
5977    Reference,
5978    Array,
5979}
5980
5981fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
5982    let mut wrappers = Vec::new();
5983    let mut current = node;
5984    loop {
5985        match current.kind() {
5986            "pointer_declarator" | "abstract_pointer_declarator" => {
5987                wrappers.push(CppStructuredTypeWrapper::Pointer)
5988            }
5989            "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
5990            "array_declarator" | "abstract_array_declarator" => {
5991                wrappers.push(CppStructuredTypeWrapper::Array)
5992            }
5993            _ => break,
5994        }
5995        let Some(child) = current
5996            .child_by_field_name("declarator")
5997            .or_else(|| current.named_child(0))
5998        else {
5999            break;
6000        };
6001        current = child;
6002    }
6003    wrappers
6004}
6005
6006fn cpp_wrap_structured_type(
6007    identity: StructuredTypeIdentity,
6008    wrapper: CppStructuredTypeWrapper,
6009) -> Option<StructuredTypeIdentity> {
6010    match wrapper {
6011        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
6012        CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
6013        CppStructuredTypeWrapper::Array => identity.wrap_array(),
6014    }
6015}
6016
6017fn cpp_wrap_structured_type_node(
6018    builder: &mut StructuredTypeIdentityBuilder,
6019    inner: StructuredTypeNodeId,
6020    wrapper: CppStructuredTypeWrapper,
6021) -> Option<StructuredTypeNodeId> {
6022    match wrapper {
6023        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
6024        CppStructuredTypeWrapper::Reference => builder.reference(inner),
6025        CppStructuredTypeWrapper::Array => builder.array(inner),
6026    }
6027}
6028
6029fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
6030    let mut path = Vec::new();
6031    let mut stack = vec![node];
6032    while let Some(current) = stack.pop() {
6033        match current.kind() {
6034            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
6035                let component = node_text(current, source).to_string();
6036                if component.is_empty() {
6037                    return None;
6038                }
6039                path.push(component);
6040            }
6041            "template_type" | "dependent_type" => {
6042                stack.push(current.child_by_field_name("name")?);
6043            }
6044            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
6045                stack.push(current.child_by_field_name("name")?);
6046                if let Some(scope) = current.child_by_field_name("scope") {
6047                    stack.push(scope);
6048                }
6049            }
6050            _ => return None,
6051        }
6052    }
6053    (!path.is_empty()).then_some(path)
6054}
6055
6056fn cpp_callable_lexical_scope(node: Node<'_>, source: &str) -> Vec<String> {
6057    let mut groups = Vec::new();
6058    let mut current = node.parent();
6059    while let Some(parent) = current {
6060        if matches!(
6061            parent.kind(),
6062            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
6063        ) && let Some(name_node) = parent.child_by_field_name("name")
6064            && let Some(components) = cpp_structured_type_path(name_node, source)
6065            && !components.is_empty()
6066        {
6067            groups.push(components);
6068        }
6069        current = parent.parent();
6070    }
6071    groups.reverse();
6072    groups.into_iter().flatten().collect()
6073}
6074
6075fn cpp_callable_dispatch_extensibility(function_declarator: Node<'_>) -> DispatchExtensibility {
6076    let mut declaration = None;
6077    let mut current = Some(function_declarator);
6078    while let Some(node) = current {
6079        match node.kind() {
6080            "template_declaration"
6081            | "preproc_if"
6082            | "preproc_ifdef"
6083            | "preproc_else"
6084            | "preproc_elif"
6085            | "preproc_call"
6086            | "ERROR" => return DispatchExtensibility::Open,
6087            "declaration" | "field_declaration" | "function_definition" => {
6088                declaration.get_or_insert(node);
6089            }
6090            "translation_unit" => break,
6091            _ => {}
6092        }
6093        current = node.parent();
6094    }
6095    let Some(declaration) = declaration else {
6096        return DispatchExtensibility::Open;
6097    };
6098
6099    let mut saw_virtual_boundary = false;
6100    let mut stack = vec![declaration];
6101    while let Some(node) = stack.pop() {
6102        match node.kind() {
6103            "compound_statement" | "field_declaration_list" => continue,
6104            "final" | "final_specifier" => return DispatchExtensibility::Closed,
6105            "virtual"
6106            | "override"
6107            | "virtual_specifier"
6108            | "pure_virtual_clause"
6109            | "template_parameter_list"
6110            | "template_method"
6111            | "template_function"
6112            | "ERROR" => saw_virtual_boundary = true,
6113            _ => {}
6114        }
6115        let mut cursor = node.walk();
6116        stack.extend(node.children(&mut cursor));
6117    }
6118
6119    if saw_virtual_boundary {
6120        DispatchExtensibility::Open
6121    } else {
6122        DispatchExtensibility::Closed
6123    }
6124}
6125
6126fn cpp_callable_linkage(declaration: Node<'_>, source: &str) -> CallableLinkage {
6127    let mut enclosed_by_class = false;
6128    let mut current = declaration.parent();
6129    while let Some(node) = current {
6130        if node.kind() == "namespace_definition"
6131            && node
6132                .child_by_field_name("name")
6133                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6134        {
6135            return CallableLinkage::Internal;
6136        }
6137        if matches!(
6138            node.kind(),
6139            "class_specifier" | "struct_specifier" | "union_specifier"
6140        ) {
6141            if node
6142                .child_by_field_name("name")
6143                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6144            {
6145                return CallableLinkage::Internal;
6146            }
6147            enclosed_by_class = true;
6148        }
6149        if matches!(node.kind(), "function_definition" | "lambda_expression") {
6150            return CallableLinkage::Internal;
6151        }
6152        current = node.parent();
6153    }
6154
6155    if enclosed_by_class {
6156        return CallableLinkage::External;
6157    }
6158
6159    let mut cursor = declaration.walk();
6160    if declaration.named_children(&mut cursor).any(|child| {
6161        child.kind() == "storage_class_specifier"
6162            && normalize_cpp_whitespace(node_text(child, source)) == "static"
6163    }) {
6164        CallableLinkage::Internal
6165    } else {
6166        CallableLinkage::External
6167    }
6168}
6169
6170fn cpp_callable_return_type_text(function_declarator: Node<'_>, source: &str) -> Option<String> {
6171    if cpp_callable_is_structural_constructor(function_declarator, source) {
6172        return None;
6173    }
6174    let mut cursor = function_declarator.walk();
6175    if let Some(trailing) = function_declarator
6176        .named_children(&mut cursor)
6177        .find(|child| child.kind() == "trailing_return_type")
6178        && let Some(type_descriptor) = trailing.named_child(0)
6179    {
6180        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
6181        if !text.is_empty() {
6182            return Some(text);
6183        }
6184    }
6185
6186    let mut current = function_declarator;
6187    let mut indirection = String::new();
6188    while let Some(parent) = current.parent() {
6189        if matches!(
6190            parent.kind(),
6191            "function_definition" | "declaration" | "field_declaration"
6192        ) {
6193            let type_node = parent.child_by_field_name("type")?;
6194            if cpp_export_macro_token(node_text(type_node, source))
6195                && (0..parent.named_child_count()).any(|index| {
6196                    parent
6197                        .named_child(index)
6198                        .is_some_and(|child| child.kind() == "ERROR")
6199                })
6200            {
6201                // Export/decorator macros commonly occupy the grammar's `type`
6202                // field and leave the semantic return type in an ERROR sibling.
6203                // Do not persist the macro token as a return type. The malformed
6204                // declaration does not carry enough structured evidence here.
6205                return None;
6206            }
6207            let base = normalize_cpp_whitespace(node_text(type_node, source));
6208            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
6209        }
6210        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
6211            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
6212                && parent.named_child_count() == 1
6213                && parent.named_child(0) == Some(current));
6214        if wraps_current_declarator {
6215            match parent.kind() {
6216                "pointer_declarator" => indirection.push('*'),
6217                "reference_declarator" => {
6218                    let reference = parent
6219                        .children(&mut parent.walk())
6220                        .find(|child| !child.is_named())
6221                        .map(|child| node_text(child, source))
6222                        .unwrap_or("&");
6223                    indirection.push_str(reference);
6224                }
6225                "init_declarator" | "parenthesized_declarator" => {}
6226                _ => return None,
6227            }
6228            current = parent;
6229            continue;
6230        }
6231        return None;
6232    }
6233    None
6234}
6235
6236fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
6237    let mut required = 0;
6238    let mut total = 0;
6239    let mut repeated = false;
6240    let mut cursor = parameters_node.walk();
6241    for child in parameters_node.children(&mut cursor) {
6242        match child.kind() {
6243            "parameter_declaration" => {
6244                if child.child_by_field_name("declarator").is_none()
6245                    && child
6246                        .child_by_field_name("type")
6247                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
6248                {
6249                    continue;
6250                }
6251                required += 1;
6252                total += 1;
6253            }
6254            "optional_parameter_declaration" => total += 1,
6255            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
6256                repeated = true;
6257            }
6258            _ => {}
6259        }
6260    }
6261    CallableArity::new(required, total, repeated)
6262}
6263
6264fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
6265    let mut labels = Vec::new();
6266    let mut cursor = parameters_node.walk();
6267    for child in parameters_node.children(&mut cursor) {
6268        match child.kind() {
6269            "parameter_declaration" | "optional_parameter_declaration" => {
6270                if let Some(name_node) = child
6271                    .child_by_field_name("declarator")
6272                    .and_then(cpp_declarator_label_node)
6273                {
6274                    labels.push(name_node);
6275                } else {
6276                    labels.push(child);
6277                }
6278            }
6279            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
6280                labels.push(child);
6281            }
6282            _ => {}
6283        }
6284    }
6285    labels
6286}
6287
6288fn cpp_signature_search_start(
6289    signature: &str,
6290    function_declarator: Node<'_>,
6291    source: &str,
6292) -> usize {
6293    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator) else {
6294        return 0;
6295    };
6296    let raw = node_text(enclosing, source);
6297    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
6298    let offset = function_declarator
6299        .start_byte()
6300        .saturating_sub(enclosing.start_byte())
6301        .saturating_sub(leading_trim_bytes);
6302    offset.min(signature.len())
6303}
6304
6305fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
6306    match node.kind() {
6307        "identifier" | "field_identifier" => Some(node),
6308        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
6309            .child_by_field_name("declarator")
6310            .or_else(|| last_named_child(node))
6311            .and_then(cpp_declarator_label_node),
6312        "array_declarator" => node
6313            .child_by_field_name("declarator")
6314            .and_then(cpp_declarator_label_node),
6315        "function_declarator" => node
6316            .child_by_field_name("declarator")
6317            .or_else(|| node.child_by_field_name("name"))
6318            .or_else(|| last_named_child(node))
6319            .and_then(cpp_declarator_label_node),
6320        _ => None,
6321    }
6322}
6323
6324fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
6325    let base_type = parameter
6326        .child_by_field_name("type")
6327        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
6328        .unwrap_or_default();
6329    let declarator = cpp_parameter_declarator(parameter);
6330    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
6331    // are discarded, so `f(const int)` and `f(int)` declare one function. A
6332    // qualifier written next to the parameter's type is only top-level when
6333    // the declarator adds no indirection; behind a pointer, reference or array
6334    // declarator the same qualifier belongs to the pointee, referent or
6335    // element and keeps distinguishing the type (#1827).
6336    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
6337    let mut cursor = parameter.walk();
6338    let qualifiers = parameter
6339        .named_children(&mut cursor)
6340        .filter(|child| child.kind() == "type_qualifier")
6341        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
6342        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
6343        .collect::<Vec<_>>()
6344        .join(" ");
6345    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
6346        (true, _) => base_type,
6347        (_, true) => qualifiers,
6348        (false, false) => format!("{qualifiers} {base_type}"),
6349    };
6350    let declarator_suffix = declarator
6351        .map(|node| cpp_declarator_suffix_without_name(node, source))
6352        .unwrap_or_default();
6353
6354    let combined = if type_text.is_empty() {
6355        declarator_suffix
6356    } else if declarator_suffix.is_empty() {
6357        type_text
6358    } else {
6359        format!("{type_text} {declarator_suffix}")
6360    };
6361    normalize_cpp_type_text(&combined)
6362}
6363
6364fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
6365    parameter.child_by_field_name("declarator").or_else(|| {
6366        // Some unnamed prototype parameters expose their abstract declarator
6367        // as a direct named child without the grammar's `declarator` field.
6368        // Recover only the structured abstract-declarator node; the parameter's
6369        // type and qualifiers are distinct children and must not be guessed from
6370        // source text.
6371        let mut cursor = parameter.walk();
6372        parameter
6373            .named_children(&mut cursor)
6374            .find(|child| is_cpp_abstract_declarator(child.kind()))
6375    })
6376}
6377
6378/// Whether a parameter's declarator chain adds indirection - a pointer,
6379/// reference, array or function declarator - to the parameter's written type.
6380fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
6381    let mut current = Some(declarator);
6382    while let Some(node) = current {
6383        if matches!(
6384            node.kind(),
6385            "pointer_declarator"
6386                | "abstract_pointer_declarator"
6387                | "reference_declarator"
6388                | "abstract_reference_declarator"
6389                | "array_declarator"
6390                | "abstract_array_declarator"
6391                | "function_declarator"
6392                | "abstract_function_declarator"
6393        ) {
6394            return true;
6395        }
6396        current = cpp_nested_declarator(node);
6397    }
6398    false
6399}
6400
6401fn is_cpp_abstract_declarator(kind: &str) -> bool {
6402    matches!(
6403        kind,
6404        "abstract_pointer_declarator"
6405            | "abstract_reference_declarator"
6406            | "abstract_array_declarator"
6407            | "abstract_function_declarator"
6408            | "abstract_parenthesized_declarator"
6409    )
6410}
6411
6412fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
6413    node.child_by_field_name("declarator").or_else(|| {
6414        if is_cpp_abstract_declarator(node.kind()) {
6415            let mut cursor = node.walk();
6416            node.named_children(&mut cursor)
6417                .find(|child| is_cpp_abstract_declarator(child.kind()))
6418        } else {
6419            // Named declarators historically use their last named child when
6420            // tree-sitter omits the field. Keep that broad fallback for
6421            // attributed, variadic, and recovered named shapes.
6422            last_named_child(node)
6423        }
6424    })
6425}
6426
6427fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
6428    match node.kind() {
6429        "identifier" | "field_identifier" => String::new(),
6430        "pointer_declarator" | "abstract_pointer_declarator" => {
6431            let inner = cpp_nested_declarator(node)
6432                .map(|child| cpp_declarator_suffix_without_name(child, source))
6433                .unwrap_or_default();
6434            format!("*{inner}")
6435        }
6436        "reference_declarator" | "abstract_reference_declarator" => {
6437            let inner = cpp_nested_declarator(node)
6438                .map(|child| cpp_declarator_suffix_without_name(child, source))
6439                .unwrap_or_default();
6440            let reference = node
6441                .children(&mut node.walk())
6442                .find(|child| matches!(child.kind(), "&" | "&&"))
6443                .map(|child| node_text(child, source))
6444                .unwrap_or("&");
6445            format!("{reference}{inner}")
6446        }
6447        "array_declarator" | "abstract_array_declarator" => {
6448            let inner = cpp_nested_declarator(node)
6449                .map(|child| cpp_declarator_suffix_without_name(child, source))
6450                .unwrap_or_default();
6451            let size = node
6452                .child_by_field_name("size")
6453                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
6454                .unwrap_or_default();
6455            format!("{inner}[{size}]")
6456        }
6457        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
6458            let inner = cpp_nested_declarator(node);
6459            inner
6460                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
6461                .unwrap_or_default()
6462        }
6463        "function_declarator" | "abstract_function_declarator" => {
6464            let inner = cpp_nested_declarator(node)
6465                .map(|child| cpp_declarator_suffix_without_name(child, source))
6466                .unwrap_or_default();
6467            let params = node
6468                .child_by_field_name("parameters")
6469                .map(|child| cpp_parameter_signature(child, source))
6470                .unwrap_or_else(|| "()".to_string());
6471            format!("{inner}{params}")
6472        }
6473        _ => {
6474            let text = normalize_cpp_whitespace(node_text(node, source));
6475            let name = extract_declarator_name(node, source);
6476            if name.is_empty() {
6477                text
6478            } else {
6479                text.replace(&name, "").trim().to_string()
6480            }
6481        }
6482    }
6483}
6484
6485fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
6486    collapse_cpp_whitespace(
6487        suffix
6488            .trim()
6489            .trim_start_matches("->")
6490            .trim_start_matches('{')
6491            .trim_end_matches(';'),
6492    )
6493}
6494
6495pub fn normalize_cpp_whitespace(value: &str) -> String {
6496    collapse_cpp_whitespace(value)
6497}
6498
6499fn normalize_cpp_type_text(value: &str) -> String {
6500    collapse_cpp_whitespace(value)
6501        .replace(", ", ",")
6502        .replace(" <", "<")
6503        .replace("< ", "<")
6504        .replace(" >", ">")
6505}
6506
6507fn collapse_cpp_whitespace(value: &str) -> String {
6508    let mut result = String::new();
6509    let mut prev_space = false;
6510    for ch in value.chars() {
6511        if ch.is_whitespace() {
6512            if !prev_space {
6513                result.push(' ');
6514            }
6515            prev_space = true;
6516        } else {
6517            result.push(ch);
6518            prev_space = false;
6519        }
6520    }
6521    result.trim().to_string()
6522}
6523
6524pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
6525    node_source_text(node, source)
6526}
6527
6528pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
6529    walk_named_tree_preorder(node, true, |node| {
6530        match node.kind() {
6531            "type_identifier" | "identifier" | "qualified_identifier" => {
6532                let text = node_text(node, source).trim();
6533                if !text.is_empty() {
6534                    identifiers.insert(text.to_string());
6535                }
6536            }
6537            _ => {}
6538        }
6539        WalkControl::Continue
6540    });
6541}
6542
6543fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
6544    node.child_by_field_name("body").or_else(|| {
6545        let mut cursor = node.walk();
6546        node.named_children(&mut cursor).find(|child| {
6547            matches!(
6548                child.kind(),
6549                "declaration_list" | "field_declaration_list" | "enumerator_list"
6550            )
6551        })
6552    })
6553}
6554
6555/// Return a class body's actual closing brace when the parser supplied one.
6556///
6557/// A malformed namespace sentinel can leave a class node carrying unrelated
6558/// parser errors even though its own class body is complete.  `has_error()` is
6559/// therefore too coarse an admission predicate for sentinel ownership.  The
6560/// body list, however, exposes the opening and closing punctuation directly;
6561/// a real (non-missing) final `}` proves that the class did not borrow the
6562/// enclosing namespace's close.  Requiring the body to end before its parent
6563/// container also rejects a recovered node whose body swallowed that outer
6564/// boundary.
6565fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
6566    if !matches!(
6567        node.kind(),
6568        "class_specifier" | "struct_specifier" | "union_specifier"
6569    ) {
6570        return None;
6571    }
6572    let body = cpp_body_node(node)?;
6573    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
6574        return None;
6575    }
6576    let open = body.child(0)?;
6577    let close = body.child(body.child_count().checked_sub(1)?)?;
6578    if open.kind() != "{"
6579        || open.is_missing()
6580        || close.kind() != "}"
6581        || close.is_missing()
6582        || close.end_byte() != body.end_byte()
6583        || body.end_byte() > node.end_byte()
6584        || node
6585            .parent()
6586            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
6587    {
6588        return None;
6589    }
6590    Some(close)
6591}
6592
6593fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
6594    if node.kind() == "namespace_definition" {
6595        return true;
6596    }
6597    let mut cursor = node.walk();
6598    node.named_children(&mut cursor)
6599        .any(cpp_contains_namespace_definition)
6600}
6601
6602struct CppNestedNamespaceSentinel<'tree> {
6603    function: Node<'tree>,
6604    body: Node<'tree>,
6605    namespace_components: Vec<String>,
6606}
6607
6608/// Owned structural recovery metadata for a namespace-sentinel region.
6609///
6610/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
6611/// instead of the namespace/class scopes that the declaration visitor restores.
6612/// The inverted usage walk has the original CST, so it needs the same ownership
6613/// evidence without borrowing parser nodes across its file scan.  Keep this
6614/// descriptor deliberately source-range based: callers can match a reference
6615/// node by containment and then resolve its structured type spelling in the
6616/// recovered class scope.
6617#[derive(Debug, Clone)]
6618pub struct CppSentinelRecoveredOwner {
6619    pub range: Range,
6620    /// Start of the qualified owner name (`btree<P>::method`).  A leading
6621    /// return type before this byte is looked up from the namespace; parameters,
6622    /// trailing returns, and the body use the member owner scope.
6623    pub owner_name_start_byte: usize,
6624    /// Number of leading components belonging to the namespace rather than
6625    /// the qualified class owner.  A leading return type is looked up before
6626    /// every owner component, not merely before the innermost class.
6627    pub namespace_component_count: usize,
6628    pub scope_components: Vec<String>,
6629}
6630
6631#[derive(Debug, Clone)]
6632pub struct CppSentinelRecoveredClass {
6633    pub namespace_range: Range,
6634    pub namespace_scope_components: Vec<String>,
6635    pub class_range: Range,
6636    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
6637    pub scope_components: Vec<String>,
6638    /// Qualified out-of-line member definitions owned by this class.  Their
6639    /// ranges may extend beyond `class_range` when the malformed sentinel
6640    /// swallowed the namespace close and left definitions as function siblings.
6641    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
6642}
6643
6644/// Resolve the lexical scope restored for a node in a malformed
6645/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
6646/// outrank class spans, which in turn outrank the surviving namespace body.
6647/// The class ancestor suffix is recovered from the original CST so nested
6648/// members keep their complete `Outer::Inner` owner chain.
6649pub fn cpp_sentinel_recovered_scope_for_node(
6650    node: Node<'_>,
6651    source: &str,
6652    recovered_classes: &[CppSentinelRecoveredClass],
6653) -> Option<Vec<String>> {
6654    let contains =
6655        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
6656    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
6657    for recovered in recovered_classes {
6658        for owner in recovered
6659            .owner_ranges
6660            .iter()
6661            .filter(|owner| contains(owner.range))
6662        {
6663            let replace = best_owner.is_none_or(|existing| {
6664                owner.range.end_byte.saturating_sub(owner.range.start_byte)
6665                    < existing
6666                        .range
6667                        .end_byte
6668                        .saturating_sub(existing.range.start_byte)
6669            });
6670            if replace {
6671                best_owner = Some(owner);
6672            }
6673        }
6674    }
6675    if let Some(owner) = best_owner {
6676        let mut scope = owner.scope_components.clone();
6677        if node.start_byte() < owner.owner_name_start_byte {
6678            scope.truncate(owner.namespace_component_count);
6679        }
6680        return Some(scope);
6681    }
6682
6683    let class = recovered_classes
6684        .iter()
6685        .filter(|recovered| contains(recovered.class_range))
6686        .min_by_key(|recovered| {
6687            recovered
6688                .class_range
6689                .end_byte
6690                .saturating_sub(recovered.class_range.start_byte)
6691        });
6692    let class_scope = class.is_some();
6693    let mut scope = if let Some(class) = class {
6694        class.scope_components.clone()
6695    } else {
6696        let namespace = recovered_classes
6697            .iter()
6698            .filter(|recovered| contains(recovered.namespace_range))
6699            .min_by_key(|recovered| {
6700                recovered
6701                    .namespace_range
6702                    .end_byte
6703                    .saturating_sub(recovered.namespace_range.start_byte)
6704            })?;
6705        let mut scope = namespace.namespace_scope_components.clone();
6706        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
6707        let common_prefix = scope
6708            .iter()
6709            .zip(&parser_namespace)
6710            .take_while(|(recovered, parser)| recovered == parser)
6711            .count();
6712        scope.extend(parser_namespace.into_iter().skip(common_prefix));
6713        scope
6714    };
6715    if class_scope {
6716        let mut ancestor_components = Vec::new();
6717        let mut ancestor = node.parent();
6718        while let Some(current) = ancestor {
6719            if matches!(
6720                current.kind(),
6721                "class_specifier" | "struct_specifier" | "union_specifier"
6722            ) && let Some(name) = current.child_by_field_name("name")
6723                && let Some(name_components) = cpp_name_components(name, source)
6724            {
6725                ancestor_components.push(
6726                    name_components
6727                        .into_iter()
6728                        .map(|component| component.name)
6729                        .collect::<Vec<_>>(),
6730                );
6731            }
6732            ancestor = current.parent();
6733        }
6734        ancestor_components.reverse();
6735        let base_len = scope.len();
6736        for component in ancestor_components.into_iter().flatten() {
6737            if scope.len() >= base_len && scope.last() == Some(&component) {
6738                continue;
6739            }
6740            scope.push(component);
6741        }
6742    }
6743    Some(scope)
6744}
6745
6746struct CppSentinelFragmentedClassTail<'tree> {
6747    class_node: Node<'tree>,
6748    template_node: Option<Node<'tree>>,
6749    name: String,
6750    fragmented: FragmentedExportBody,
6751    consumed_start: usize,
6752}
6753
6754struct CppSentinelDirectBodyClassRegion {
6755    namespace_components: Vec<String>,
6756    class_start: usize,
6757    class_start_line: usize,
6758    class_close_end: usize,
6759    class_close_line: usize,
6760    name: String,
6761}
6762
6763fn cpp_sentinel_body_class_candidate<'tree>(
6764    child: Node<'tree>,
6765) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
6766    if matches!(
6767        child.kind(),
6768        "class_specifier" | "struct_specifier" | "union_specifier"
6769    ) {
6770        return Some((child, None));
6771    }
6772    if child.kind() != "template_declaration" {
6773        if child.kind() == "declaration" {
6774            return Some((first_class_like_child(child)?, None));
6775        }
6776        return None;
6777    }
6778    let mut cursor = child.walk();
6779    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
6780        if matches!(
6781            candidate.kind(),
6782            "class_specifier" | "struct_specifier" | "union_specifier"
6783        ) {
6784            Some(candidate)
6785        } else if candidate.kind() == "declaration" {
6786            first_class_like_child(candidate)
6787        } else {
6788            None
6789        }
6790    })?;
6791    Some((class_node, Some(child)))
6792}
6793
6794fn cpp_sentinel_direct_body_class_candidate<'tree>(
6795    child: Node<'tree>,
6796) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
6797    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
6798        return Some(candidate);
6799    }
6800    if child.kind() != "template_declaration" {
6801        return None;
6802    }
6803    let mut cursor = child.walk();
6804    let wrapper = child
6805        .named_children(&mut cursor)
6806        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
6807    Some((first_class_like_child(wrapper)?, Some(child)))
6808}
6809
6810fn cpp_sentinel_direct_namespace_components(
6811    function: Node<'_>,
6812    body: Node<'_>,
6813    source: &str,
6814) -> Option<Vec<String>> {
6815    let mut cursor = function.walk();
6816    let children = function
6817        .named_children(&mut cursor)
6818        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
6819        .collect::<Vec<_>>();
6820    let sentinel_index = children.iter().rposition(|child| {
6821        direct_identifier_name(*child, source)
6822            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
6823    })?;
6824    let mut identifiers = Vec::new();
6825    let mut stack = children[sentinel_index + 1..]
6826        .iter()
6827        .rev()
6828        .copied()
6829        .collect::<Vec<_>>();
6830    while let Some(current) = stack.pop() {
6831        if let Some(name) = direct_identifier_name(current, source) {
6832            identifiers.push(name);
6833            continue;
6834        }
6835        let mut cursor = current.walk();
6836        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
6837        stack.extend(children.into_iter().rev());
6838    }
6839    let [keyword, namespace] = identifiers.as_slice() else {
6840        return None;
6841    };
6842    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
6843        .then(|| vec![namespace.clone()])
6844}
6845
6846fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
6847    let mut sibling = class_semicolon.next_named_sibling();
6848    let namespace_close = loop {
6849        let Some(current) = sibling else {
6850            return false;
6851        };
6852        sibling = current.next_named_sibling();
6853        if current.kind() != "comment" {
6854            break current;
6855        }
6856    };
6857    if !cpp_is_stray_close_brace(namespace_close, source) {
6858        return false;
6859    }
6860    loop {
6861        let Some(current) = sibling else {
6862            return false;
6863        };
6864        sibling = current.next_named_sibling();
6865        if current.kind() == "comment" {
6866            continue;
6867        }
6868        return direct_identifier_name(current, source)
6869            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
6870    }
6871}
6872
6873fn cpp_sentinel_macro_body_class_region(
6874    node: Node<'_>,
6875    source: &str,
6876) -> Option<CppSentinelDirectBodyClassRegion> {
6877    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
6878        return None;
6879    };
6880    if node.kind() != "function_definition" || !node.has_error() {
6881        return None;
6882    }
6883    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
6884    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
6885    let mut cursor = body.walk();
6886    let candidates = body
6887        .named_children(&mut cursor)
6888        .filter_map(cpp_sentinel_direct_body_class_candidate)
6889        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
6890        .collect::<Vec<_>>();
6891    let [(class_node, template_node)] = candidates.as_slice() else {
6892        return None;
6893    };
6894    let original_body = cpp_body_node(*class_node)?;
6895    let name = class_like_name(*class_node, source)?;
6896    if name.is_empty() || cpp_export_macro_token(&name) {
6897        return None;
6898    }
6899
6900    let mut sibling = node.next_named_sibling();
6901    let (class_close_start, class_close_end, class_close_line) = loop {
6902        let current = sibling?;
6903        let next = current.next_named_sibling();
6904        if cpp_is_stray_close_brace(current, source)
6905            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
6906        {
6907            let semicolon = next.expect("checked above");
6908            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
6909                return None;
6910            }
6911            break (
6912                current.start_byte(),
6913                semicolon.end_byte(),
6914                semicolon.end_position().row + 1,
6915            );
6916        }
6917        sibling = next;
6918    };
6919    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
6920    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
6921    let root = tree.root_node();
6922    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
6923    let reparsed = cpp_sentinel_reparsed_class(root, reparsed_template, source)?;
6924    if reparsed.name != name
6925        || reparsed.declaration_node.start_byte() != class_node.start_byte()
6926        || reparsed.body.start_byte() != original_body.start_byte()
6927        || class_close_start <= reparsed.body.end_byte()
6928        || class_close_end <= class_node.end_byte()
6929    {
6930        return None;
6931    }
6932    Some(CppSentinelDirectBodyClassRegion {
6933        namespace_components,
6934        class_start: reparse_start,
6935        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
6936            node.start_position().row + 1
6937        }),
6938        class_close_end,
6939        class_close_line,
6940        name,
6941    })
6942}
6943
6944/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
6945/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
6946///
6947/// The parser puts the namespace opener and the malformed function in one root
6948/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
6949/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
6950/// the malformed function must begin with an all-caps type, then an ERROR whose
6951/// sole identifier is `namespace`, followed by the inner namespace identifier
6952/// and a compound body; and that body must contain complete named class
6953/// specifiers.  A text reparse cannot prove any of those ownership boundaries.
6954fn cpp_nested_namespace_sentinel<'tree>(
6955    node: Node<'tree>,
6956    source: &str,
6957) -> Option<CppNestedNamespaceSentinel<'tree>> {
6958    if !node.has_error() {
6959        return None;
6960    }
6961
6962    let (function, mut namespace_components) = if node.kind() == "ERROR" {
6963        let mut cursor = node.walk();
6964        let functions = node
6965            .named_children(&mut cursor)
6966            .filter(|child| child.kind() == "function_definition")
6967            .collect::<Vec<_>>();
6968        let [function] = functions.as_slice() else {
6969            return None;
6970        };
6971        if !function.has_error() {
6972            return None;
6973        }
6974        let mut cursor = node.walk();
6975        let children = node.children(&mut cursor).collect::<Vec<_>>();
6976        let function_index = children
6977            .iter()
6978            .position(|child| same_node(*child, *function))?;
6979        let [outer_keyword, outer_name, outer_open] =
6980            children.get(function_index.checked_sub(3)?..function_index)?
6981        else {
6982            return None;
6983        };
6984        if outer_keyword.kind() != "namespace"
6985            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
6986            || outer_open.kind() != "{"
6987        {
6988            return None;
6989        }
6990        (
6991            *function,
6992            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
6993        )
6994    } else if node.kind() == "function_definition" {
6995        let declaration_list = node.parent()?;
6996        let namespace = declaration_list.parent()?;
6997        if declaration_list.kind() != "declaration_list"
6998            || namespace.kind() != "namespace_definition"
6999            || namespace.child_by_field_name("body") != Some(declaration_list)
7000        {
7001            return None;
7002        }
7003        (node, Vec::new())
7004    } else {
7005        return None;
7006    };
7007
7008    let mut cursor = function.walk();
7009    let named = function
7010        .named_children(&mut cursor)
7011        .filter(|child| child.kind() != "comment")
7012        .collect::<Vec<_>>();
7013    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
7014        return None;
7015    };
7016    if first_type.kind() != "type_identifier" {
7017        return None;
7018    }
7019    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
7020    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
7021        return None;
7022    }
7023    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
7024        return None;
7025    }
7026    let inner_keyword = inner_error.named_child(0)?;
7027    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
7028        return None;
7029    }
7030    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
7031        return None;
7032    }
7033    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
7034    if inner_name.is_empty() || body.kind() != "compound_statement" {
7035        return None;
7036    }
7037    namespace_components.push(inner_name);
7038
7039    let mut cursor = body.walk();
7040    let classes = body
7041        .named_children(&mut cursor)
7042        .filter_map(cpp_sentinel_body_class_candidate)
7043        .filter(|(child, _)| {
7044            cpp_body_node(*child).is_some()
7045                && class_like_name(*child, source)
7046                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
7047        })
7048        .collect::<Vec<_>>();
7049    if classes.is_empty() {
7050        return None;
7051    }
7052
7053    Some(CppNestedNamespaceSentinel {
7054        function,
7055        body: *body,
7056        namespace_components,
7057    })
7058}
7059
7060/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
7061/// malformed namespace-sentinel function.  The recovery is deliberately
7062/// structural: the class must be a direct body item, its own class node must be
7063/// erroneous and end before a unique anonymous `}` in the enclosing
7064/// declaration-list, and that namespace's next sibling must be a standalone
7065/// `;`.  The complete interior must pass the existing member-shaped reparse
7066/// gate. This avoids source brace scans and does not borrow a close from an
7067/// unrelated later declaration.
7068fn cpp_sentinel_fragmented_class_tail<'tree>(
7069    function: Node<'tree>,
7070    body: Node<'tree>,
7071    source: &str,
7072) -> Option<CppSentinelFragmentedClassTail<'tree>> {
7073    let mut cursor = body.walk();
7074    let candidates = body
7075        .named_children(&mut cursor)
7076        .filter_map(cpp_sentinel_body_class_candidate)
7077        .filter(|(class_node, _)| cpp_body_node(*class_node).is_some() && class_node.has_error())
7078        .collect::<Vec<_>>();
7079    let [(class_node, template_node)] = candidates.as_slice() else {
7080        return None;
7081    };
7082    let name = class_like_name(*class_node, source)?;
7083    if name.is_empty() || cpp_export_macro_token(&name) {
7084        return None;
7085    }
7086    let class_body = cpp_body_node(*class_node)?;
7087
7088    let (close, semicolon) =
7089        cpp_sentinel_fragment_boundary(function, *class_node, class_body, source)?;
7090
7091    let reparse_start = class_body.start_byte().checked_add(1)?;
7092    let reparse_end = close.start_byte();
7093    if reparse_start >= reparse_end {
7094        return None;
7095    }
7096    let tree = cpp_reparse_region_items(source, reparse_start, reparse_end)?;
7097    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
7098        return None;
7099    }
7100    let class_range = Range {
7101        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
7102        end_byte: semicolon.end_byte(),
7103        start_line: template_node.map_or(class_node.start_position().row, |node| {
7104            node.start_position().row
7105        }) + 1,
7106        end_line: semicolon.end_position().row + 1,
7107    };
7108    Some(CppSentinelFragmentedClassTail {
7109        class_node: *class_node,
7110        template_node: *template_node,
7111        name,
7112        fragmented: FragmentedExportBody {
7113            reparse_start,
7114            reparse_end,
7115            class_range,
7116        },
7117        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
7118    })
7119}
7120
7121/// Recover the class and out-of-line owner scopes from every malformed
7122/// namespace-sentinel region in `root`.
7123///
7124/// This is the shared structural counterpart to
7125/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
7126/// reuses the visitor's sentinel/class admission predicates instead of parsing
7127/// source text a second time.  The returned values own only ranges and names, so
7128/// they can be retained by an inverted usage scan after the tree borrow ends.
7129pub fn cpp_sentinel_recovered_classes(
7130    root: Node<'_>,
7131    source: &str,
7132) -> Vec<CppSentinelRecoveredClass> {
7133    if !root.has_error() {
7134        return Vec::new();
7135    }
7136    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
7137    let mut stack = vec![root];
7138    while let Some(current) = stack.pop() {
7139        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source) {
7140            let namespace_components = cpp_sentinel_recovered_namespace_components(
7141                recovered.function,
7142                &recovered.namespace_components,
7143                source,
7144            );
7145            let fragmented =
7146                cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, source);
7147            let mut class_candidates = Vec::new();
7148            let mut cursor = recovered.body.walk();
7149            for (class_node, template_node) in recovered
7150                .body
7151                .named_children(&mut cursor)
7152                .filter_map(cpp_sentinel_body_class_candidate)
7153            {
7154                let Some(name) = class_like_name(class_node, source) else {
7155                    continue;
7156                };
7157                if name.is_empty() || cpp_export_macro_token(&name) {
7158                    continue;
7159                }
7160                let is_fragmented = fragmented
7161                    .as_ref()
7162                    .is_some_and(|tail| same_node(tail.class_node, class_node));
7163                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
7164                    continue;
7165                }
7166                let class_range = if is_fragmented {
7167                    fragmented
7168                        .as_ref()
7169                        .map(|tail| tail.fragmented.class_range)
7170                        .expect("fragmented class range is present when class matches")
7171                } else {
7172                    cpp_declaration_range(template_node.unwrap_or(class_node))
7173                };
7174                class_candidates.push((class_range, name));
7175            }
7176
7177            let mut owner_ranges =
7178                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
7179            cpp_sentinel_extend_unique_owner_ranges(
7180                &mut owner_ranges,
7181                cpp_sentinel_recovered_sibling_owner_ranges(
7182                    recovered.function,
7183                    &namespace_components,
7184                    source,
7185                ),
7186            );
7187            for (class_range, name) in class_candidates {
7188                push_cpp_sentinel_recovered_class(
7189                    &mut recovered_classes,
7190                    cpp_declaration_range(recovered.body),
7191                    &namespace_components,
7192                    class_range,
7193                    name,
7194                    &owner_ranges,
7195                );
7196            }
7197
7198            if let Some(declaration_list) = recovered
7199                .function
7200                .parent()
7201                .filter(|parent| parent.kind() == "declaration_list")
7202            {
7203                let outer_namespace =
7204                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
7205                push_cpp_sentinel_sibling_classes(
7206                    &mut recovered_classes,
7207                    declaration_list,
7208                    recovered.function,
7209                    &outer_namespace,
7210                    source,
7211                );
7212            }
7213        } else if let Some(region) = cpp_sentinel_macro_body_class_region(current, source) {
7214            let namespace_components = cpp_sentinel_recovered_namespace_components(
7215                current,
7216                &region.namespace_components,
7217                source,
7218            );
7219            let owner_container = current
7220                .parent()
7221                .filter(|parent| parent.kind() == "declaration_list")
7222                .unwrap_or(current);
7223            let owner_ranges =
7224                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
7225            push_cpp_sentinel_recovered_class(
7226                &mut recovered_classes,
7227                cpp_declaration_range(owner_container),
7228                &namespace_components,
7229                Range {
7230                    start_byte: region.class_start,
7231                    end_byte: region.class_close_end,
7232                    start_line: region.class_start_line,
7233                    end_line: region.class_close_line,
7234                },
7235                region.name,
7236                &owner_ranges,
7237            );
7238        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
7239            // A generic sentinel-prefixed class can be reduced as a malformed
7240            // function/ERROR without the explicit `namespace X` token pair.
7241            // Reuse the declaration visitor's bounded reparse and retain only
7242            // the recovered class identity/range here.
7243            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
7244                region;
7245            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
7246                continue;
7247            };
7248            let root = tree.root_node();
7249            let template_node = cpp_sentinel_reparsed_leading_template(root);
7250            let Some(reparsed_class) = cpp_sentinel_reparsed_class(root, template_node, source)
7251            else {
7252                continue;
7253            };
7254            let class_node = reparsed_class.declaration_node;
7255            let name = reparsed_class.name;
7256            let namespace_components =
7257                cpp_sentinel_recovered_namespace_components(current, &[], source);
7258            let owner_container = current
7259                .parent()
7260                .filter(|parent| parent.kind() == "declaration_list")
7261                .unwrap_or(current);
7262            let mut owner_ranges =
7263                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
7264            cpp_sentinel_extend_unique_owner_ranges(
7265                &mut owner_ranges,
7266                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
7267            );
7268            push_cpp_sentinel_recovered_class(
7269                &mut recovered_classes,
7270                cpp_declaration_range(owner_container),
7271                &namespace_components,
7272                Range {
7273                    start_byte: class_start,
7274                    end_byte: close_end,
7275                    start_line: class_node.start_position().row + 1,
7276                    end_line: class_node.end_position().row + 1,
7277                },
7278                name,
7279                &owner_ranges,
7280            );
7281            if owner_container.kind() == "declaration_list" {
7282                push_cpp_sentinel_sibling_classes(
7283                    &mut recovered_classes,
7284                    owner_container,
7285                    current,
7286                    &namespace_components,
7287                    source,
7288                );
7289            }
7290        }
7291
7292        let mut cursor = current.walk();
7293        stack.extend(current.named_children(&mut cursor));
7294    }
7295    // A shallower sentinel can expose nested classes as apparent namespace
7296    // siblings even after a deeper sentinel proves that a containing class
7297    // owns their ranges. Drop those shadow descriptors; scope recovery starts
7298    // from the proven containing class and appends parser-visible class
7299    // ancestors, preserving the full `Outer::Inner` chain.
7300    let shadowed = recovered_classes
7301        .iter()
7302        .map(|candidate| {
7303            recovered_classes.iter().any(|container| {
7304                container.class_range.start_byte <= candidate.class_range.start_byte
7305                    && container.class_range.end_byte >= candidate.class_range.end_byte
7306                    && container.class_range != candidate.class_range
7307                    && container.namespace_scope_components.len()
7308                        > candidate.namespace_scope_components.len()
7309                    && container
7310                        .namespace_scope_components
7311                        .starts_with(&candidate.namespace_scope_components)
7312            })
7313        })
7314        .collect::<Vec<_>>();
7315    let mut index = 0usize;
7316    recovered_classes.retain(|_| {
7317        let keep = !shadowed[index];
7318        index += 1;
7319        keep
7320    });
7321    recovered_classes
7322}
7323
7324/// A flat sentinel can swallow the first class while leaving later classes and
7325/// their out-of-line definitions as ordinary declaration-list siblings.  Once
7326/// the malformed class proves the sentinel envelope, retain those structurally
7327/// complete sibling classes under the same surviving namespace so every member
7328/// owner in the region uses one recovery contract.
7329fn push_cpp_sentinel_sibling_classes(
7330    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
7331    declaration_list: Node<'_>,
7332    sentinel_node: Node<'_>,
7333    namespace_components: &[String],
7334    source: &str,
7335) {
7336    let owner_ranges =
7337        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
7338    let namespace_range = cpp_declaration_range(declaration_list);
7339    let mut cursor = declaration_list.walk();
7340    for (class_node, template_node) in declaration_list
7341        .named_children(&mut cursor)
7342        .filter(|child| !same_node(*child, sentinel_node))
7343        .filter_map(cpp_sentinel_body_class_candidate)
7344    {
7345        let Some(name) = class_like_name(class_node, source) else {
7346            continue;
7347        };
7348        if name.is_empty()
7349            || cpp_export_macro_token(&name)
7350            || cpp_complete_class_body_close(class_node).is_none()
7351        {
7352            continue;
7353        }
7354        push_cpp_sentinel_recovered_class(
7355            recovered_classes,
7356            namespace_range,
7357            namespace_components,
7358            cpp_declaration_range(template_node.unwrap_or(class_node)),
7359            name,
7360            &owner_ranges,
7361        );
7362    }
7363}
7364
7365fn push_cpp_sentinel_recovered_class(
7366    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
7367    namespace_range: Range,
7368    namespace_components: &[String],
7369    class_range: Range,
7370    name: String,
7371    owner_ranges: &[CppSentinelRecoveredOwner],
7372) {
7373    let mut scope_components = namespace_components.to_vec();
7374    scope_components.push(name);
7375    let owner_ranges = owner_ranges
7376        .iter()
7377        .filter(|owner| owner.scope_components.starts_with(&scope_components))
7378        .cloned()
7379        .collect::<Vec<_>>();
7380    if recovered_classes.iter().any(|existing| {
7381        existing.class_range == class_range && existing.scope_components == scope_components
7382    }) {
7383        return;
7384    }
7385    recovered_classes.push(CppSentinelRecoveredClass {
7386        namespace_range,
7387        namespace_scope_components: namespace_components.to_vec(),
7388        class_range,
7389        scope_components,
7390        owner_ranges,
7391    });
7392}
7393
7394fn cpp_sentinel_recovered_namespace_components(
7395    function: Node<'_>,
7396    recovered_components: &[String],
7397    source: &str,
7398) -> Vec<String> {
7399    let mut ancestor_components = Vec::new();
7400    let mut ancestor = function.parent();
7401    while let Some(current) = ancestor {
7402        if current.kind() == "namespace_definition"
7403            && let Some(name_node) = current.child_by_field_name("name")
7404            && let Some(components) = cpp_name_components(name_node, source)
7405        {
7406            ancestor_components.push(
7407                components
7408                    .into_iter()
7409                    .map(|component| component.name)
7410                    .collect::<Vec<_>>(),
7411            );
7412        }
7413        ancestor = current.parent();
7414    }
7415    ancestor_components.reverse();
7416    let mut ancestors = ancestor_components
7417        .into_iter()
7418        .flatten()
7419        .collect::<Vec<_>>();
7420
7421    let overlap = (0..=ancestors.len().min(recovered_components.len()))
7422        .rev()
7423        .find(|length| {
7424            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
7425        })
7426        .unwrap_or(0);
7427    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
7428    ancestors
7429}
7430
7431fn cpp_sentinel_recovered_owner_ranges(
7432    body: Node<'_>,
7433    namespace_components: &[String],
7434    source: &str,
7435) -> Vec<CppSentinelRecoveredOwner> {
7436    let mut owners = Vec::new();
7437    walk_named_tree_preorder(body, true, |node| {
7438        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
7439    });
7440    owners
7441}
7442
7443fn cpp_sentinel_collect_owner_range(
7444    node: Node<'_>,
7445    namespace_components: &[String],
7446    source: &str,
7447    owners: &mut Vec<CppSentinelRecoveredOwner>,
7448) -> WalkControl {
7449    if node.kind() != "function_definition" {
7450        return WalkControl::Continue;
7451    }
7452    let Some(function_declarator) = extract_function_declarator(node) else {
7453        return WalkControl::Continue;
7454    };
7455    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
7456        return WalkControl::Continue;
7457    };
7458    let Some(mut components) = cpp_name_components(name_node, source) else {
7459        return WalkControl::Continue;
7460    };
7461    if components.len() <= 1 {
7462        return WalkControl::Continue;
7463    }
7464    components.pop();
7465    let mut owner_components = components
7466        .into_iter()
7467        .map(|component| component.name)
7468        .collect::<Vec<_>>();
7469    let overlap = (0..=namespace_components.len().min(owner_components.len()))
7470        .rev()
7471        .find(|length| {
7472            owner_components[..*length]
7473                == namespace_components[namespace_components.len().saturating_sub(*length)..]
7474        })
7475        .unwrap_or(0);
7476    let mut scope_components = namespace_components.to_vec();
7477    scope_components.extend(owner_components.drain(overlap..));
7478    if scope_components.len() <= namespace_components.len() {
7479        return WalkControl::Continue;
7480    }
7481    let range = cpp_declaration_range(node);
7482    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
7483        existing.range == range && existing.scope_components == scope_components
7484    }) {
7485        owners.push(CppSentinelRecoveredOwner {
7486            range,
7487            owner_name_start_byte: name_node.start_byte(),
7488            namespace_component_count: namespace_components.len(),
7489            scope_components,
7490        });
7491    }
7492    WalkControl::Continue
7493}
7494
7495fn cpp_sentinel_extend_unique_owner_ranges(
7496    owners: &mut Vec<CppSentinelRecoveredOwner>,
7497    additional: Vec<CppSentinelRecoveredOwner>,
7498) {
7499    for owner in additional {
7500        if !owners.iter().any(|existing| {
7501            existing.range == owner.range && existing.scope_components == owner.scope_components
7502        }) {
7503            owners.push(owner);
7504        }
7505    }
7506}
7507
7508fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
7509    if node.kind() != "ERROR" || node.named_child_count() != 1 {
7510        return false;
7511    }
7512    let Some(end_name) = node.named_child(0) else {
7513        return false;
7514    };
7515    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
7516        return false;
7517    }
7518    let mut cursor = node.walk();
7519    node.children(&mut cursor)
7520        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
7521}
7522
7523/// Collect owner definitions that the malformed sentinel left as later
7524/// declaration-list siblings. Parser-visible namespace siblings are a hard
7525/// boundary: their declarations must keep their own lexical namespace.
7526fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
7527    parent: Node<'_>,
7528    sentinel_node: Node<'_>,
7529    namespace_components: &[String],
7530    source: &str,
7531) -> Vec<CppSentinelRecoveredOwner> {
7532    let mut owners = Vec::new();
7533    let mut after_sentinel = false;
7534    let mut cursor = parent.walk();
7535    for child in parent.named_children(&mut cursor) {
7536        if !after_sentinel {
7537            if same_node(child, sentinel_node) {
7538                after_sentinel = true;
7539            }
7540            continue;
7541        }
7542        walk_named_tree_preorder(child, true, |node| {
7543            if node.kind() == "namespace_definition" {
7544                return WalkControl::SkipChildren;
7545            }
7546            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
7547        });
7548    }
7549    owners
7550}
7551
7552/// Collect owner definitions after a malformed namespace, stopping only at
7553/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
7554/// enclosing container is not trusted to belong to the recovered namespace.
7555fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
7556    parent: Node<'_>,
7557    sentinel_node: Node<'_>,
7558    namespace_components: &[String],
7559    source: &str,
7560) -> Option<Vec<CppSentinelRecoveredOwner>> {
7561    let mut owners = Vec::new();
7562    let mut after_namespace = false;
7563    let mut cursor = parent.walk();
7564    for child in parent.named_children(&mut cursor) {
7565        if !after_namespace {
7566            if same_node(child, sentinel_node) {
7567                after_namespace = true;
7568            }
7569            continue;
7570        }
7571        if cpp_sentinel_namespace_end(child, source) {
7572            return Some(owners);
7573        }
7574        walk_named_tree_preorder(child, true, |node| {
7575            if node.kind() == "namespace_definition" {
7576                return WalkControl::SkipChildren;
7577            }
7578            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
7579        });
7580    }
7581    None
7582}
7583
7584fn cpp_sentinel_recovered_sibling_owner_ranges(
7585    sentinel_node: Node<'_>,
7586    namespace_components: &[String],
7587    source: &str,
7588) -> Vec<CppSentinelRecoveredOwner> {
7589    let Some(declaration_list) = sentinel_node
7590        .parent()
7591        .filter(|parent| parent.kind() == "declaration_list")
7592    else {
7593        return Vec::new();
7594    };
7595    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
7596        declaration_list,
7597        sentinel_node,
7598        namespace_components,
7599        source,
7600    );
7601
7602    let Some(namespace) = declaration_list
7603        .parent()
7604        .filter(|parent| parent.kind() == "namespace_definition")
7605    else {
7606        return owners;
7607    };
7608    let Some(outer_parent) = namespace.parent() else {
7609        return owners;
7610    };
7611    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
7612        outer_parent,
7613        namespace,
7614        namespace_components,
7615        source,
7616    ) {
7617        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
7618    }
7619    owners
7620}
7621
7622fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
7623    let mut current = function_declarator.child_by_field_name("declarator")?;
7624    loop {
7625        if matches!(
7626            current.kind(),
7627            "qualified_identifier"
7628                | "scoped_identifier"
7629                | "scoped_type_identifier"
7630                | "identifier"
7631                | "field_identifier"
7632                | "operator_name"
7633                | "destructor_name"
7634                | "literal_operator_name"
7635        ) {
7636            return Some(current);
7637        }
7638        current = current
7639            .child_by_field_name("declarator")
7640            .or_else(|| current.child_by_field_name("name"))
7641            .or_else(|| last_named_child(current))?;
7642    }
7643}
7644
7645fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
7646    match node.kind() {
7647        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
7648            let mut components = match node.child_by_field_name("scope") {
7649                Some(scope) => cpp_name_components(scope, source)?,
7650                None => Vec::new(),
7651            };
7652            let name = node.child_by_field_name("name")?;
7653            components.push(canonical_cpp_qualified_component(name, source)?);
7654            Some(components)
7655        }
7656        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
7657    }
7658}
7659
7660fn cpp_sentinel_fragment_boundary<'tree>(
7661    function: Node<'tree>,
7662    class_node: Node<'tree>,
7663    class_body: Node<'tree>,
7664    source: &str,
7665) -> Option<(Node<'tree>, Node<'tree>)> {
7666    let declaration_list = function.parent()?;
7667    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
7668        return None;
7669    }
7670    let namespace = declaration_list.parent()?;
7671    if namespace.kind() != "namespace_definition"
7672        || namespace.child_by_field_name("body") != Some(declaration_list)
7673    {
7674        return None;
7675    }
7676    let mut cursor = declaration_list.walk();
7677    let closes = declaration_list
7678        .children(&mut cursor)
7679        .filter(|child| {
7680            !child.is_named()
7681                && child.kind() == "}"
7682                && child.start_byte() >= function.end_byte()
7683                && child.start_byte() > class_node.end_byte()
7684                && child.start_byte() > class_body.start_byte()
7685        })
7686        .collect::<Vec<_>>();
7687    let [close] = closes.as_slice() else {
7688        return None;
7689    };
7690    let semicolon = namespace.next_named_sibling()?;
7691    if !cpp_is_stray_semicolon(semicolon, source)
7692        || close.end_byte() != namespace.end_byte()
7693        || semicolon.start_byte() < namespace.end_byte()
7694    {
7695        return None;
7696    }
7697    Some((*close, semicolon))
7698}
7699
7700/// Detect the bogus declaration/function tree that tree-sitter recovers for a
7701/// region prefixed by an object-like macro sentinel the parser cannot see
7702/// (issue #941), and return the byte range `[start, end)` of the swallowed
7703/// declaration interior to reparse.
7704///
7705/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
7706/// `function_definition` whose first non-comment named child is the sentinel
7707/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
7708/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
7709/// the real items.
7710/// `start` is the end of the sentinel identifier -- everything after it is the
7711/// genuine source. `end` is the node's end, extended across any trailing empty
7712/// `;` statement the mis-parse displaced past the node (the class/struct closing
7713/// semicolon), so the reparse sees a complete, brace-balanced item.
7714///
7715/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
7716/// node (`has_error`). Unknown annotation/export macros can make a real callable
7717/// error-recovered even though tree-sitter still preserves its declarator, so a
7718/// preserved callable is admitted only when a displaced class keyword precedes
7719/// that declarator. The clean-reparse-to-items gate in
7720/// `cpp_reparsed_items_are_indexable` is the final arbiter.
7721/// Return the reparse start and, when present, the structurally recovered class
7722/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
7723/// separately from the reparse start because an opaque template-declaration
7724/// macro may precede it.
7725fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
7726    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
7727    {
7728        return None;
7729    }
7730    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
7731    // retain a valid function declarator despite the unknown export macro making
7732    // the outer node erroneous. Remember that declarator for the ordering gate
7733    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
7734    // class keyword precedes a spurious callable assembled from a later member.
7735    let mut declarator_cursor = node.walk();
7736    let preserved_callable = node
7737        .children_by_field_name("declarator", &mut declarator_cursor)
7738        .find_map(extract_function_declarator);
7739    // Leading documentation comments are attached to the malformed
7740    // `function_definition` as named children.  They are not part of the
7741    // sentinel prefix, so select the first non-comment child structurally
7742    // rather than requiring the sentinel to be child zero.  This is the shape
7743    // emitted for nlohmann/json's `basic_json`: its class documentation comment
7744    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
7745    // envelope otherwise ends at the first nested union.
7746    let mut cursor = node.walk();
7747    let first = node
7748        .named_children(&mut cursor)
7749        .find(|child| child.kind() != "comment")?;
7750    if first.kind() != "type_identifier" {
7751        return None;
7752    }
7753    let sentinel = normalize_cpp_whitespace(node_text(first, source));
7754    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
7755        return None;
7756    }
7757    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
7758    // makes the trailing sentinel of one region and the leading sentinel of the
7759    // next both land as bare macro-token identifiers ahead of the real content.
7760    // Advance past every leading macro-token identifier so the reparse begins at
7761    // genuine source rather than another sentinel that would re-form the bogus
7762    // shape and fail the reparse gate.
7763    let mut start = first.end_byte();
7764    let mut after_first = false;
7765    let mut cursor = node.walk();
7766    for child in node.named_children(&mut cursor) {
7767        if !after_first {
7768            if same_node(child, first) {
7769                after_first = true;
7770            }
7771            continue;
7772        }
7773        if matches!(child.kind(), "identifier" | "type_identifier")
7774            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
7775        {
7776            start = child.end_byte();
7777        } else {
7778            break;
7779        }
7780    }
7781    // An additional opaque template-declaration macro before a class can be
7782    // folded into the bogus function's qualified declarator.  In that shape
7783    // the macro is not a direct sibling we can skip above; tree-sitter exposes
7784    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
7785    // Reparse from that keyword (or a real preceding `template` keyword) so the
7786    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
7787    // a class nested in a genuine sentinel-wrapped namespace lies after the
7788    // body opening and must not change the established region start.
7789    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
7790    let mut class_start = None;
7791    let mut template_start = None;
7792    let mut stack = vec![node];
7793    while let Some(current) = stack.pop() {
7794        if current.start_byte() >= prefix_end {
7795            continue;
7796        }
7797        if matches!(
7798            current.kind(),
7799            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
7800        ) {
7801            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
7802                "class" | "struct" | "union" | "enum" => {
7803                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
7804                        seen.min(current.start_byte())
7805                    }));
7806                }
7807                "template" => {
7808                    template_start =
7809                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
7810                            seen.min(current.start_byte())
7811                        }));
7812                }
7813                _ => {}
7814            }
7815        }
7816        let mut cursor = current.walk();
7817        stack.extend(current.children(&mut cursor));
7818    }
7819    if preserved_callable.is_some_and(|callable| {
7820        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
7821    }) {
7822        return None;
7823    }
7824    if let Some(class_start) = class_start {
7825        start = template_start
7826            .filter(|template_start| *template_start < class_start)
7827            .unwrap_or(class_start);
7828    }
7829    Some((start, class_start))
7830}
7831
7832/// Locate a sentinel-prefixed class whose malformed declaration was split across
7833/// root-level siblings. The true class close is represented structurally as a
7834/// lone `}` error followed by the class's displaced `;`; nested method/body
7835/// errors are not direct siblings of the sentinel node and therefore cannot
7836/// satisfy this pair.
7837fn cpp_sentinel_macro_class_region(
7838    node: Node<'_>,
7839    source: &str,
7840) -> Option<(usize, usize, usize, usize, usize, usize)> {
7841    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
7842        return None;
7843    };
7844    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
7845        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
7846        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
7847    if class_start >= body_open_start {
7848        return None;
7849    }
7850    let sibling_close = {
7851        let mut sibling = node.next_named_sibling();
7852        let mut found = None;
7853        while let Some(current) = sibling {
7854            let next = current.next_named_sibling();
7855            if cpp_is_stray_close_brace(current, source)
7856                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
7857            {
7858                let semicolon = next.expect("checked above");
7859                found = Some((
7860                    current.start_byte(),
7861                    semicolon.end_byte(),
7862                    semicolon.end_position().row + 1,
7863                ));
7864                break;
7865            }
7866            sibling = next;
7867        }
7868        found
7869    };
7870    let (class_close_start, class_close_end, class_close_line) =
7871        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
7872            (class_close_start, class_close_end, class_close_line)
7873        } else {
7874            // When the malformed envelope itself is an ERROR, tree-sitter can
7875            // leave the class's balanced close in the source while promoting
7876            // all following members to siblings. Reparse the complete suffix
7877            // and use the first body-bearing class node's own field range as
7878            // the partition boundary. This keeps balancing in tree-sitter and
7879            // preserves the source's original byte offsets.
7880            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
7881            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
7882            let reparsed_class =
7883                cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)?;
7884            let body = reparsed_class.body;
7885            let class_close_end = body.end_byte();
7886            let class_close_start = class_close_end.checked_sub(1)?;
7887            let class_close_line = body.end_position().row + 1;
7888            (class_close_start, class_close_end, class_close_line)
7889        };
7890    if class_close_start <= class_start {
7891        return None;
7892    }
7893
7894    // Reparse only far enough to expose the class body opening. This is a
7895    // structured check that the candidate really begins with a body-bearing
7896    // class-like item; the original malformed tree cannot provide that node.
7897    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
7898    let class_root = tree.root_node();
7899    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
7900    let reparsed_class = cpp_sentinel_reparsed_class(class_root, template_node, source)?;
7901    let body = reparsed_class.body;
7902    // The class body opening must agree with the malformed wrapper's structured
7903    // body field. This rejects an inner nested class while permitting later
7904    // members to remain fragmented as root-level siblings in the bounded parse.
7905    if body.start_byte() != body_open_start {
7906        return None;
7907    }
7908    let body_start = body.start_byte().checked_add(1)?;
7909    (body_start < class_close_start).then_some((
7910        reparse_start,
7911        class_start,
7912        body_start,
7913        class_close_start,
7914        class_close_end,
7915        class_close_line,
7916    ))
7917}
7918
7919/// Find the `{` token immediately following the class/struct/union/enum token
7920/// at `class_start` in the malformed tree. The token is anonymous in the C++
7921/// grammar, so this deliberately walks all children (not only named children)
7922/// and relies on sibling structure rather than source-text searching.
7923fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
7924    let mut stack = vec![node];
7925    while let Some(current) = stack.pop() {
7926        if current.start_byte() == class_start
7927            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
7928        {
7929            let mut sibling = current.next_sibling();
7930            while let Some(candidate) = sibling {
7931                if candidate.kind() == "{" {
7932                    return Some(candidate.start_byte());
7933                }
7934                sibling = candidate.next_sibling();
7935            }
7936        }
7937        let mut cursor = current.walk();
7938        stack.extend(current.children(&mut cursor));
7939    }
7940    None
7941}
7942
7943/// The class body that tree-sitter displaced out of a sentinel-prefixed
7944/// declaration and left as the malformed node's next sibling.
7945///
7946/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
7947/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
7948/// the last child of that `ERROR` and its `{` opens a sibling
7949/// `compound_statement` instead. The body is still the malformed tree's own
7950/// structured token, which is what the caller's `body.start_byte() !=
7951/// body_open_start` agreement check needs; it just is not reachable by walking
7952/// forward from the class token inside the node.
7953fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
7954    node.next_named_sibling()
7955        .filter(|sibling| sibling.kind() == "compound_statement")
7956}
7957
7958fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
7959    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
7960    let mut end = if class_start.is_some() {
7961        cpp_macro_prefixed_class_end(source, start)?
7962    } else {
7963        node.end_byte()
7964    };
7965    let mut sibling = node.next_named_sibling();
7966    while let Some(current) = sibling {
7967        if !cpp_is_stray_semicolon(current, source) {
7968            break;
7969        }
7970        end = current.end_byte();
7971        sibling = current.next_named_sibling();
7972    }
7973    (start < end).then_some((start, end))
7974}
7975
7976/// Parse the source suffix beginning at a structurally recovered class/template
7977/// keyword and return the end of its first body-bearing class item.  The parser,
7978/// rather than a brace scanner, owns nested-body balancing.  This is needed when
7979/// the original error tree truncates the class and scatters later members as
7980/// top-level siblings.
7981fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
7982    let tree = cpp_reparse_region_items(source, start, source.len())?;
7983    let root = tree.root_node();
7984    let mut cursor = root.walk();
7985    for item in root.named_children(&mut cursor) {
7986        if item.end_byte() <= start || item.kind() == "comment" {
7987            continue;
7988        }
7989        let mut stack = vec![item];
7990        while let Some(current) = stack.pop() {
7991            if matches!(
7992                current.kind(),
7993                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7994            ) && cpp_body_node(current).is_some()
7995            {
7996                return Some(current.end_byte());
7997            }
7998            let mut cursor = current.walk();
7999            stack.extend(current.named_children(&mut cursor));
8000        }
8001        // The recovered prefix is required to begin with the class item.  If
8002        // the first real item is something else, fail closed rather than skip
8003        // arbitrary source looking for a later class.
8004        return None;
8005    }
8006    None
8007}
8008
8009/// An empty `;` statement: the displaced closing semicolon of a struct/class that
8010/// the sentinel mis-parse split off past the bogus function node.
8011fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
8012    node.kind() == "expression_statement"
8013        && node.named_child_count() == 0
8014        && node_text(node, source).trim() == ";"
8015}
8016
8017/// Recover the real field name when a leading object-like annotation macro
8018/// displaces a qualified type into tree-sitter's bit-field recovery shape.
8019///
8020/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
8021/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
8022/// `bitfield_clause` containing an error plus an assignment.  The assignment's
8023/// left field is the only structured declaration name in that malformed tail.
8024/// A real bit-field is excluded by the all-caps macro type and required error.
8025fn recovered_macro_qualified_field_declarators<'tree>(
8026    node: Node<'tree>,
8027    source: &str,
8028) -> Option<Vec<Node<'tree>>> {
8029    if node.kind() != "field_declaration" {
8030        return None;
8031    }
8032    let macro_type = node.child_by_field_name("type")?;
8033    if macro_type.kind() != "type_identifier"
8034        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8035    {
8036        return None;
8037    }
8038    let pseudo_declarator = node.child_by_field_name("declarator")?;
8039    if pseudo_declarator.kind() != "field_identifier" {
8040        return None;
8041    }
8042    let mut cursor = node.walk();
8043    let clause = node
8044        .named_children(&mut cursor)
8045        .find(|child| child.kind() == "bitfield_clause")?;
8046    if !(0..clause.named_child_count()).any(|index| {
8047        clause
8048            .named_child(index)
8049            .is_some_and(|child| child.kind() == "ERROR")
8050    }) {
8051        return None;
8052    }
8053    let mut recovered = Vec::new();
8054    let mut stack = vec![clause];
8055    while let Some(current) = stack.pop() {
8056        if current.kind() == "assignment_expression"
8057            && let Some(left) = current.child_by_field_name("left")
8058            && extract_variable_name(left, source).is_some()
8059        {
8060            recovered.push(left);
8061            break;
8062        }
8063        let mut cursor = current.walk();
8064        stack.extend(current.named_children(&mut cursor));
8065    }
8066    if recovered.is_empty() {
8067        return None;
8068    }
8069    let mut cursor = node.walk();
8070    recovered.extend(
8071        node.children_by_field_name("declarator", &mut cursor)
8072            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
8073    );
8074    Some(recovered)
8075}
8076
8077/// Recover a macro-qualified member function declaration that tree-sitter
8078/// represents as a pseudo-field. An object-like export macro before a qualified
8079/// return type can displace the namespace and type into an ERROR/bitfield
8080/// recovery, leaving the callable as a structured `call_expression`.
8081///
8082/// The caller must route this shape before ordinary declarator classification;
8083/// otherwise the displaced namespace identifier is published as a field.
8084fn recovered_macro_qualified_function_call<'tree>(
8085    node: Node<'tree>,
8086    source: &str,
8087) -> Option<Node<'tree>> {
8088    if node.kind() != "field_declaration" {
8089        return None;
8090    }
8091    let macro_type = node.child_by_field_name("type")?;
8092    if macro_type.kind() != "type_identifier"
8093        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8094    {
8095        return None;
8096    }
8097    let declarator = node.child_by_field_name("declarator")?;
8098    if declarator.kind() != "field_identifier" {
8099        return None;
8100    }
8101    let mut cursor = node.walk();
8102    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
8103    if !named.iter().any(|child| {
8104        child.kind() == "storage_class_specifier"
8105            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
8106    }) {
8107        return None;
8108    }
8109    let bitfield = named
8110        .iter()
8111        .find(|child| child.kind() == "bitfield_clause")?;
8112    let mut bitfield_cursor = bitfield.walk();
8113    let payload = bitfield
8114        .named_children(&mut bitfield_cursor)
8115        .collect::<Vec<_>>();
8116    let [displaced_error, call] = payload.as_slice() else {
8117        return None;
8118    };
8119    if displaced_error.kind() != "ERROR"
8120        || displaced_error.named_child_count() != 1
8121        || displaced_error
8122            .named_child(0)
8123            .is_none_or(|child| child.kind() != "identifier")
8124        || call.kind() != "call_expression"
8125        || call
8126            .child_by_field_name("function")
8127            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
8128        || call
8129            .child_by_field_name("arguments")
8130            .is_none_or(|arguments| arguments.kind() != "argument_list")
8131    {
8132        return None;
8133    }
8134    Some(*call)
8135}
8136
8137fn recovered_macro_qualified_function_parameters(
8138    arguments: Node<'_>,
8139    source: &str,
8140) -> Option<(String, Vec<String>)> {
8141    if arguments.kind() != "argument_list" {
8142        return None;
8143    }
8144    let mut cursor = arguments.walk();
8145    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
8146    if named.is_empty() {
8147        return Some(("()".to_string(), Vec::new()));
8148    }
8149    let mut types = Vec::new();
8150    let mut labels = Vec::new();
8151    let mut index = 0;
8152    while index < named.len() {
8153        let parameter_type = named[index];
8154        let parameter_name = named.get(index + 1).copied()?;
8155        if !matches!(
8156            parameter_type.kind(),
8157            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
8158        ) || parameter_name.kind() != "ERROR"
8159            || parameter_name.named_child_count() != 1
8160            || parameter_name
8161                .named_child(0)
8162                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
8163        {
8164            return None;
8165        }
8166        let parameter_name = parameter_name.named_child(0)?;
8167        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
8168        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
8169        index += 2;
8170    }
8171    Some((format!("({})", types.join(", ")), labels))
8172}
8173
8174/// Recognize the phantom field tree-sitter emits for a macro-qualified
8175/// function return type.  For example,
8176/// `static API result_type ThresholdForSmallA() { ... }` can become a
8177/// `field_declaration` (`API` as the type and `result_type` as a field name)
8178/// followed by a clean `function_definition` for `ThresholdForSmallA`.
8179///
8180/// Keep this predicate entirely tied to the CST envelope: the type must be an
8181/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
8182/// the declaration must carry a missing semicolon rather than a real one, and
8183/// the immediate named sibling must expose a function declarator.  A real
8184/// macro-decorated field with an explicit semicolon therefore remains a field.
8185pub fn recovered_macro_return_type_node<'tree>(
8186    node: Node<'tree>,
8187    source: &str,
8188) -> Option<Node<'tree>> {
8189    if node.kind() != "field_declaration" {
8190        return None;
8191    }
8192    let macro_type = node.child_by_field_name("type")?;
8193    if macro_type.kind() != "type_identifier"
8194        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8195    {
8196        return None;
8197    }
8198    let declarator = node.child_by_field_name("declarator")?;
8199    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
8200        return None;
8201    }
8202    let mut has_missing_semicolon = false;
8203    let mut has_real_semicolon = false;
8204    for index in 0..node.child_count() {
8205        let Some(child) = node.child(index) else {
8206            continue;
8207        };
8208        if child.kind() != ";" {
8209            continue;
8210        }
8211        if child.is_missing() {
8212            has_missing_semicolon = true;
8213        } else {
8214            has_real_semicolon = true;
8215        }
8216    }
8217    if !has_missing_semicolon || has_real_semicolon {
8218        return None;
8219    }
8220    let mut next = node.next_named_sibling();
8221    while next.is_some_and(|sibling| sibling.kind() == "comment") {
8222        next = next.and_then(|sibling| sibling.next_named_sibling());
8223    }
8224    let next = next?;
8225    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
8226        return None;
8227    }
8228    let function_declarator = next.child_by_field_name("declarator")?;
8229    extract_function_declarator(function_declarator).map(|_| declarator)
8230}
8231
8232/// Whether `name` is a type parameter of a template declaration that lexically
8233/// encloses `node`. The malformed macro-return field uses the parameter name as
8234/// its pseudo-declarator; preserving that field is necessary to publish a
8235/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
8236/// ancestors instead of interpreting source text so nested templates and
8237/// parser-recovered regions retain their real lexical scopes.
8238fn cpp_active_template_type_parameter(node: Node<'_>, name: &str, source: &str) -> bool {
8239    let mut ancestor = node.parent();
8240    while let Some(current) = ancestor {
8241        if current.kind() == "template_declaration"
8242            && let Some(parameters) = current.child_by_field_name("parameters")
8243        {
8244            let mut cursor = parameters.walk();
8245            if parameters.named_children(&mut cursor).any(|parameter| {
8246                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
8247                    && cpp_template_parameter_name(parameter, source)
8248                        .is_some_and(|parameter_name| parameter_name == name)
8249            }) {
8250                return true;
8251            }
8252        }
8253        ancestor = current.parent();
8254    }
8255    false
8256}
8257
8258/// Reparse the region `[start, end)` of `source` as C++, confined to the region
8259/// via included ranges so every reparsed node keeps its original byte offset and
8260/// line number. The existing visitors read node text from the original source,
8261/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
8262/// `parse_rust_region_tree` technique.
8263fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
8264    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
8265}
8266
8267/// Reparse a fragmented class-body interior while preserving its original byte
8268/// and line offsets. Unlike an included-range translation-unit parse, a padded
8269/// prefix keeps C++ preprocessor directives after an access label in the same
8270/// recovery shape tree-sitter produces for a complete class body.
8271fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
8272    let bytes = source.as_bytes();
8273    let prefix = bytes.get(..start)?;
8274    let interior = bytes.get(start..end)?;
8275    let mut padded = Vec::with_capacity(end);
8276    padded.extend(
8277        prefix
8278            .iter()
8279            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
8280    );
8281    padded.extend_from_slice(interior);
8282    let padded = String::from_utf8(padded).ok()?;
8283    let mut parser = Parser::new();
8284    parser
8285        .set_language(&tree_sitter_cpp::LANGUAGE.into())
8286        .ok()?;
8287    parser.parse(&padded, None)
8288}
8289
8290/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
8291/// reparsed interior is indexed only when every top-level named node is a
8292/// well-formed C++ item (or a comment) and at least one real item is present.
8293/// Expression/statement soup surfaces as a top-level `ERROR` or
8294/// `expression_statement`, neither of which is an item kind, so it is rejected.
8295///
8296/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
8297/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
8298/// swallowed by a preceding dangling sentinel) reparses to a real
8299/// `namespace_definition` whose body still holds a bogus `function_definition`,
8300/// so the subtree legitimately carries an error. Container items are admitted
8301/// even with an internal error; the inner bogus function is recovered recursively
8302/// when `visit_function_definition` walks it. Each recursion strips at least one
8303/// leading sentinel, so the region strictly shrinks and recovery terminates.
8304///
8305/// A top-level `function_definition` is the one place we stay strict: it is
8306/// admitted only when it is clean or is itself a sentinel candidate. A function
8307/// that has an error and is not a sentinel is a real callable with a broken body,
8308/// so we refuse the whole reparse and let the ordinary path handle it (preserving
8309/// its real return type rather than re-deriving an implicit one).
8310fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
8311    let mut cursor = root.walk();
8312    let mut saw_item = false;
8313    for child in root.named_children(&mut cursor) {
8314        match child.kind() {
8315            "comment" => {}
8316            "function_definition" => {
8317                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
8318                    return false;
8319                }
8320                saw_item = true;
8321            }
8322            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
8323            _ => return false,
8324        }
8325    }
8326    saw_item
8327}
8328
8329/// Robustness gate for a reparsed fragmented multiple-base export class body
8330/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
8331/// kinds a class body produces when reparsed at translation-unit scope: the
8332/// access-specifier label preceding the first member surfaces as a
8333/// `labeled_statement` wrapping that member, and members surface as
8334/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
8335/// Statement or expression soup surfaces as other top-level kinds and is rejected,
8336/// so only a genuinely member-shaped body is ever re-owned as members; anything
8337/// ambiguous falls back to indexing the class alone.
8338fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
8339    if node.kind() != "ERROR" {
8340        return false;
8341    }
8342    let mut stack = Vec::new();
8343    let mut saw_function_declarator = false;
8344    let mut cursor = node.walk();
8345    for child in node.named_children(&mut cursor) {
8346        stack.push(child);
8347    }
8348    while let Some(current) = stack.pop() {
8349        match current.kind() {
8350            // Tree-sitter may wrap adjacent copy-control declarations in a
8351            // nested ERROR. Keep descending only through ERROR wrappers; the
8352            // actual declaration payload must be a function_declarator.
8353            "ERROR" => {
8354                let mut cursor = current.walk();
8355                stack.extend(current.named_children(&mut cursor));
8356            }
8357            "function_declarator" => saw_function_declarator = true,
8358            _ => return false,
8359        }
8360    }
8361    saw_function_declarator
8362}
8363
8364fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
8365    if node.kind() != "ERROR" {
8366        return false;
8367    }
8368    let mut cursor = node.walk();
8369    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
8370    let [explicit, constructor_error, destructor] = named.as_slice() else {
8371        return false;
8372    };
8373    let Some(constructor) = constructor_error.named_child(0) else {
8374        return false;
8375    };
8376    let Some(constructor_name) =
8377        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
8378    else {
8379        return false;
8380    };
8381    let Some(destructor_name) =
8382        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
8383    else {
8384        return false;
8385    };
8386    let Some(destroyed_type) = destructor_name.named_child(0) else {
8387        return false;
8388    };
8389    explicit.kind() == "explicit_function_specifier"
8390        && constructor_error.kind() == "ERROR"
8391        && constructor_error.named_child_count() == 1
8392        && constructor.kind() == "function_declarator"
8393        && constructor_name.kind() == "identifier"
8394        && destructor.kind() == "function_declarator"
8395        && destructor_name.kind() == "destructor_name"
8396        && destroyed_type.kind() == "identifier"
8397        && node_text(constructor_name, source) == node_text(destroyed_type, source)
8398}
8399
8400fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
8401    if node.kind() != "compound_statement" {
8402        return false;
8403    }
8404    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
8405        return false;
8406    };
8407    if prefix.kind() == "labeled_statement"
8408        && prefix.named_child(0).is_some_and(|label| {
8409            matches!(
8410                node_text(label, source).trim(),
8411                "public" | "private" | "protected"
8412            )
8413        })
8414    {
8415        return prefix.named_children(&mut prefix.walk()).any(|child| {
8416            child.kind() == "declaration"
8417                && child.has_error()
8418                && child
8419                    .named_children(&mut child.walk())
8420                    .any(cpp_reparsed_member_error_is_indexable)
8421        });
8422    }
8423    // A malformed constructor initializer can be split into a declaration
8424    // followed by its compound body when the class prefix already contains
8425    // realistic members. Keep this admission tied to that exact structured
8426    // declaration/error/body chain rather than accepting arbitrary blocks.
8427    prefix.kind() == "declaration"
8428        && prefix.has_error()
8429        && prefix
8430            .named_children(&mut prefix.walk())
8431            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
8432}
8433
8434fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
8435    if !cpp_reparsed_member_error_is_indexable(node) {
8436        return false;
8437    }
8438    let Some(preproc) = node.next_named_sibling() else {
8439        return false;
8440    };
8441    preproc.kind() == "preproc_if"
8442        && preproc.has_error()
8443        && preproc
8444            .named_children(&mut preproc.walk())
8445            .any(|child| child.kind() == "expression_statement" && child.has_error())
8446        && preproc
8447            .next_named_sibling()
8448            .is_some_and(|body| body.kind() == "compound_statement")
8449}
8450
8451/// Return a function body whose braces and ownership are explicit in the
8452/// reparsed class-member tree. An error below a real function envelope is
8453/// recoverable by the ordinary function visitor; a missing/deferred body is
8454/// not, because accepting it would let statement soup masquerade as a member.
8455fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
8456    if node.kind() != "function_definition" {
8457        return None;
8458    }
8459    let body = node.child_by_field_name("body")?;
8460    if body.kind() != "compound_statement" {
8461        return None;
8462    }
8463    let open = body.child(0)?;
8464    let close = body.child(body.child_count().checked_sub(1)?)?;
8465    if open.kind() != "{"
8466        || open.is_missing()
8467        || close.kind() != "}"
8468        || close.is_missing()
8469        || close.end_byte() != body.end_byte()
8470        || body.end_byte() != node.end_byte()
8471    {
8472        return None;
8473    }
8474    Some(body)
8475}
8476
8477fn cpp_reparsed_member_function_errors_are_in_body(
8478    node: Node<'_>,
8479    body: Node<'_>,
8480    source: &str,
8481) -> bool {
8482    let mut cursor = node.walk();
8483    node.children(&mut cursor).all(|child| {
8484        same_node(child, body)
8485            || cpp_reparsed_member_attribute_error(child, source)
8486            || cpp_reparsed_member_signature_identifier_errors(child)
8487            || (!child.has_error() && !child.is_error() && !child.is_missing())
8488    })
8489}
8490
8491/// A complete callable can still carry parser errors in its signature when a
8492/// project annotation is not part of the C++ grammar (`nonneg int`,
8493/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
8494/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
8495/// leaves inside the already-proven callable envelope; structured statements,
8496/// literals, missing tokens, and other malformed signature payload remain
8497/// rejected.
8498fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
8499    if !node.has_error() && !node.is_error() && !node.is_missing() {
8500        return false;
8501    }
8502    let mut stack = vec![node];
8503    let mut saw_error = false;
8504    while let Some(current) = stack.pop() {
8505        if current.is_missing() {
8506            return false;
8507        }
8508        if current.kind() == "ERROR" {
8509            saw_error = true;
8510            let mut cursor = current.walk();
8511            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
8512            if children
8513                .iter()
8514                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
8515            {
8516                return false;
8517            }
8518            stack.extend(children);
8519            continue;
8520        }
8521        let mut cursor = current.walk();
8522        stack.extend(current.children(&mut cursor));
8523    }
8524    saw_error
8525}
8526
8527fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
8528    node.kind() == "ERROR"
8529        && node.named_child_count() == 1
8530        && node.named_child(0).is_some_and(|attribute| {
8531            attribute.kind() == "identifier"
8532                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
8533        })
8534}
8535
8536/// A C++ attribute placed between a member's declarator and body can make
8537/// tree-sitter expose the callable as
8538/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
8539/// Keep this admission tied to that exact node geometry. In particular, an
8540/// arbitrary ERROR or identifier before a compound statement is not enough.
8541fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
8542    let Some(body) = cpp_reparsed_member_function_body(node) else {
8543        return false;
8544    };
8545    let mut cursor = node.walk();
8546    let named = node
8547        .named_children(&mut cursor)
8548        .filter(|child| child.kind() != "comment")
8549        .collect::<Vec<_>>();
8550    let [type_node, error, attribute, body_node] = named.as_slice() else {
8551        return false;
8552    };
8553    if !same_node(*body_node, body)
8554        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
8555        || attribute.kind() != "identifier"
8556        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
8557        || error.kind() != "ERROR"
8558        || error.named_child_count() != 1
8559    {
8560        return false;
8561    }
8562    error
8563        .named_child(0)
8564        .is_some_and(cpp_reparsed_attribute_callable_declarator)
8565}
8566
8567fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
8568    cpp_structured_type_path(node, source).is_some()
8569        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
8570}
8571
8572fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
8573    let Some(body) = cpp_reparsed_member_function_body(node) else {
8574        return false;
8575    };
8576    let mut cursor = node.walk();
8577    let named = node
8578        .named_children(&mut cursor)
8579        .filter(|child| child.kind() != "comment")
8580        .collect::<Vec<_>>();
8581    let [friend, return_error, declarator, body_node] = named.as_slice() else {
8582        return false;
8583    };
8584    let Some(return_type) = return_error.named_child(0) else {
8585        return false;
8586    };
8587    same_node(*body_node, body)
8588        && friend.kind() == "type_identifier"
8589        && node_text(*friend, source) == "friend"
8590        && return_error.kind() == "ERROR"
8591        && return_error.named_child_count() == 1
8592        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
8593        && extract_function_declarator(*declarator)
8594            .and_then(cpp_function_declarator_name_node)
8595            .is_some()
8596}
8597
8598fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
8599    let Some(body) = cpp_reparsed_member_function_body(node) else {
8600        return false;
8601    };
8602    let mut cursor = node.walk();
8603    let named = node
8604        .named_children(&mut cursor)
8605        .filter(|child| child.kind() != "comment")
8606        .collect::<Vec<_>>();
8607    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
8608        return false;
8609    };
8610    let Some(return_type) = return_error.named_child(0) else {
8611        return false;
8612    };
8613    same_node(*body_node, body)
8614        && prefix
8615            .iter()
8616            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
8617        && attribute.kind() == "type_identifier"
8618        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
8619        && return_error.kind() == "ERROR"
8620        && return_error.named_child_count() == 1
8621        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
8622        && extract_function_declarator(*declarator)
8623            .and_then(cpp_function_declarator_name_node)
8624            .is_some()
8625}
8626
8627/// An included-range reparse that begins inside a malformed class can merge an
8628/// access label and following template member. Tree-sitter then emits the label
8629/// as the `template_type` name, the template parameter list as its arguments,
8630/// an ERROR-wrapped return type, the callable declarator, and its complete
8631/// body. Admit only that exact structured displacement.
8632fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
8633    let Some(body) = cpp_reparsed_member_function_body(node) else {
8634        return false;
8635    };
8636    let mut cursor = node.walk();
8637    let named = node
8638        .named_children(&mut cursor)
8639        .filter(|child| child.kind() != "comment")
8640        .collect::<Vec<_>>();
8641    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
8642        return false;
8643    };
8644    let Some(template_name) = template_type.child_by_field_name("name") else {
8645        return false;
8646    };
8647    let Some(arguments) = template_type.child_by_field_name("arguments") else {
8648        return false;
8649    };
8650    let Some(return_type) = return_error.named_child(0) else {
8651        return false;
8652    };
8653    let mut cursor = template_type.walk();
8654    let template_errors = template_type
8655        .named_children(&mut cursor)
8656        .filter(|child| child.kind() == "ERROR")
8657        .collect::<Vec<_>>();
8658    let [comment_error] = template_errors.as_slice() else {
8659        return false;
8660    };
8661    let mut cursor = comment_error.walk();
8662    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
8663    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
8664        return false;
8665    };
8666    same_node(*body_node, body)
8667        && template_type.kind() == "template_type"
8668        && template_name.kind() == "type_identifier"
8669        && matches!(
8670            node_text(template_name, source).trim(),
8671            "public" | "private" | "protected"
8672        )
8673        && arguments.kind() == "template_argument_list"
8674        && arguments.named_child_count() > 0
8675        && !arguments.has_error()
8676        && !colon.is_named()
8677        && colon.kind() == ":"
8678        && comments.iter().all(|child| child.kind() == "comment")
8679        && !template_keyword.is_named()
8680        && template_keyword.kind() == "template"
8681        && return_error.kind() == "ERROR"
8682        && return_error.named_child_count() == 1
8683        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
8684        && extract_function_declarator(*declarator)
8685            .and_then(cpp_function_declarator_name_node)
8686            .is_some()
8687}
8688
8689/// Return the constructor declaration tree-sitter can merge into an access
8690/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
8691/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
8692/// declaration's apparent type; the callable name must still exactly match the
8693/// recovered class, so unrelated labeled statements are never re-owned.
8694fn cpp_reparsed_preprocessor_constructor<'tree>(
8695    node: Node<'tree>,
8696    class_name: &str,
8697    source: &str,
8698) -> Option<Node<'tree>> {
8699    if node.kind() != "labeled_statement" {
8700        return None;
8701    }
8702    let mut cursor = node.walk();
8703    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
8704    let [label, directive_error, declaration] = named.as_slice() else {
8705        return None;
8706    };
8707    if label.kind() != "statement_identifier"
8708        || !matches!(
8709            node_text(*label, source),
8710            "public" | "private" | "protected"
8711        )
8712        || directive_error.kind() != "ERROR"
8713        || directive_error.child_count() != 1
8714        || directive_error
8715            .child(0)
8716            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
8717        || declaration.kind() != "declaration"
8718        || declaration.named_child_count() != 2
8719    {
8720        return None;
8721    }
8722    let apparent_type = declaration.child_by_field_name("type")?;
8723    if apparent_type.kind() != "type_identifier"
8724        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
8725    {
8726        return None;
8727    }
8728    let declarator = declaration.child_by_field_name("declarator")?;
8729    let function = extract_function_declarator(declarator)?;
8730    let name = cpp_function_declarator_name_node(function)?;
8731    (node_text(name, source) == class_name).then_some(*declaration)
8732}
8733
8734fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
8735    if extract_function_declarator(node)
8736        .and_then(cpp_function_declarator_name_node)
8737        .is_some()
8738    {
8739        return true;
8740    }
8741    node.kind() == "init_declarator"
8742        && node
8743            .child_by_field_name("declarator")
8744            .is_some_and(|declarator| declarator.kind() == "identifier")
8745        && node
8746            .child_by_field_name("value")
8747            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
8748}
8749
8750/// Return true for the constrained/attribute form that tree-sitter splits into
8751/// an ERROR declaration, a preprocessor `requires` clause, and a following
8752/// compound statement. The three nodes must remain immediate named siblings;
8753/// this deliberately does not search source text or skip unrelated statements.
8754fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
8755    if node.kind() != "ERROR" || node.named_child_count() != 3 {
8756        return false;
8757    }
8758    let mut cursor = node.walk();
8759    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
8760    let [type_node, function_declarator, attribute] = named.as_slice() else {
8761        return false;
8762    };
8763    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
8764        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
8765        || attribute.kind() != "identifier"
8766        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
8767    {
8768        return false;
8769    }
8770    let Some(preproc) =
8771        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
8772    else {
8773        return false;
8774    };
8775    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
8776        .filter(|sibling| sibling.kind() == "compound_statement")
8777    else {
8778        return false;
8779    };
8780    let Some(open) = body.child(0) else {
8781        return false;
8782    };
8783    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
8784        return false;
8785    };
8786    let Some(condition) = preproc.child_by_field_name("condition") else {
8787        return false;
8788    };
8789    let mut cursor = preproc.walk();
8790    let payload = preproc
8791        .named_children(&mut cursor)
8792        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
8793        .collect::<Vec<_>>();
8794    let [requires_statement] = payload.as_slice() else {
8795        return false;
8796    };
8797    let requires_clause = requires_statement.named_child(0);
8798
8799    open.kind() == "{"
8800        && !open.is_missing()
8801        && close.kind() == "}"
8802        && !close.is_missing()
8803        && close.end_byte() == body.end_byte()
8804        && requires_statement.kind() == "expression_statement"
8805        && requires_statement.named_child_count() == 1
8806        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
8807}
8808
8809fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
8810    let mut sibling = node.next_named_sibling();
8811    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
8812        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
8813    }
8814    sibling
8815}
8816
8817fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
8818    let mut sibling = node.prev_named_sibling();
8819    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
8820        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
8821    }
8822    sibling
8823}
8824
8825fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
8826    let Some(preproc) =
8827        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
8828    else {
8829        return false;
8830    };
8831    let Some(error) =
8832        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
8833    else {
8834        return false;
8835    };
8836    cpp_reparsed_attribute_requires_error(error, source)
8837}
8838
8839fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
8840    node: Node<'tree>,
8841    source: &str,
8842) -> Option<Node<'tree>> {
8843    if node.kind() != "ERROR" {
8844        return None;
8845    }
8846    let mut cursor = node.walk();
8847    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
8848    let [parameter, macro_name, message] = named.as_slice() else {
8849        return None;
8850    };
8851    let parameter_name = parameter.named_child(0)?;
8852    (parameter.kind() == "type_parameter_declaration"
8853        && parameter_name.kind() == "type_identifier"
8854        && macro_name.kind() == "type_identifier"
8855        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
8856        && message.kind() == "string_literal")
8857        .then_some(parameter_name)
8858}
8859
8860fn cpp_reparsed_template_macro_companion_is_indexable(
8861    node: Node<'_>,
8862    parameter_name: Node<'_>,
8863    source: &str,
8864) -> bool {
8865    let Some(body) = cpp_reparsed_member_function_body(node) else {
8866        return false;
8867    };
8868    let mut cursor = node.walk();
8869    let named = node
8870        .named_children(&mut cursor)
8871        .filter(|child| child.kind() != "comment")
8872        .collect::<Vec<_>>();
8873    let [
8874        constraint,
8875        close_error,
8876        storage,
8877        return_error,
8878        declarator,
8879        body_node,
8880    ] = named.as_slice()
8881    else {
8882        return false;
8883    };
8884    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
8885        return false;
8886    };
8887    let Some(constraint_template) = constraint.child_by_field_name("name") else {
8888        return false;
8889    };
8890    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
8891        return false;
8892    };
8893    let Some(return_type) = return_error.named_child(0) else {
8894        return false;
8895    };
8896    let mut cursor = constraint_arguments.walk();
8897    let constraint_types = constraint_arguments
8898        .named_children(&mut cursor)
8899        .collect::<Vec<_>>();
8900    same_node(*body_node, body)
8901        && constraint.kind() == "qualified_identifier"
8902        && constraint_scope.kind() == "namespace_identifier"
8903        && constraint_template.kind() == "template_type"
8904        && matches!(constraint_types.as_slice(), [left, right]
8905            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
8906        && !constraint_arguments.has_error()
8907        && close_error.kind() == "ERROR"
8908        && close_error.named_child_count() == 0
8909        && storage.kind() == "storage_class_specifier"
8910        && return_error.kind() == "ERROR"
8911        && return_error.named_child_count() == 1
8912        && return_type.kind() == "identifier"
8913        && node_text(return_type, source) == node_text(parameter_name, source)
8914        && extract_function_declarator(*declarator)
8915            .and_then(cpp_function_declarator_name_node)
8916            .is_some()
8917}
8918
8919fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
8920    node: Node<'tree>,
8921    parameter_name: Node<'_>,
8922    source: &str,
8923) -> Option<Node<'tree>> {
8924    let body = cpp_reparsed_member_function_body(node)?;
8925    let constraint = node.child_by_field_name("type")?;
8926    let constraint_template = constraint.child_by_field_name("name")?;
8927    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
8928    let mut argument_cursor = constraint_arguments.walk();
8929    let constraint_types = constraint_arguments
8930        .named_children(&mut argument_cursor)
8931        .collect::<Vec<_>>();
8932    if constraint.kind() != "qualified_identifier"
8933        || constraint_template.kind() != "template_type"
8934        || !matches!(constraint_types.as_slice(), [left, right]
8935            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
8936        || constraint_arguments.has_error()
8937        || node
8938            .child_by_field_name("body")
8939            .is_none_or(|candidate| !same_node(candidate, body))
8940    {
8941        return None;
8942    }
8943
8944    let mut cursor = node.walk();
8945    let recovery_errors = node
8946        .named_children(&mut cursor)
8947        .filter(|child| child.kind() == "ERROR")
8948        .collect::<Vec<_>>();
8949    if !recovery_errors
8950        .iter()
8951        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
8952        || !recovery_errors.iter().all(|error| {
8953            error.named_child_count() == 0
8954                || cpp_reparsed_constraint_macro_error(*error, source)
8955                || (error.named_child_count() == 1
8956                    && error
8957                        .named_child(0)
8958                        .is_some_and(|child| child.kind() == "function_declarator"))
8959        })
8960    {
8961        return None;
8962    }
8963
8964    let parameter_text = node_text(parameter_name, source);
8965    let mut declarators = node
8966        .child_by_field_name("declarator")
8967        .and_then(extract_function_declarator)
8968        .into_iter()
8969        .collect::<Vec<_>>();
8970    for error in recovery_errors {
8971        let mut stack = vec![error];
8972        while let Some(current) = stack.pop() {
8973            if current.kind() == "function_declarator" {
8974                declarators.push(current);
8975            }
8976            let mut cursor = current.walk();
8977            stack.extend(current.named_children(&mut cursor));
8978        }
8979    }
8980    declarators.into_iter().find(|declarator| {
8981        cpp_function_declarator_name_node(*declarator)
8982            .is_some_and(|name| name.kind() == "identifier")
8983            && declarator
8984                .child_by_field_name("parameters")
8985                .is_some_and(|parameters| {
8986                    parameters
8987                        .named_children(&mut parameters.walk())
8988                        .filter_map(|parameter| parameter.child_by_field_name("type"))
8989                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
8990                })
8991    })
8992}
8993
8994fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
8995    node: Node<'_>,
8996    parameter_name: Node<'_>,
8997    source: &str,
8998) -> bool {
8999    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
9000}
9001
9002fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
9003    if node.kind() != "ERROR" {
9004        return false;
9005    }
9006    let mut stack = vec![node];
9007    while let Some(current) = stack.pop() {
9008        let macro_shape = match current.kind() {
9009            "call_expression" => current
9010                .child_by_field_name("function")
9011                .zip(current.child_by_field_name("arguments")),
9012            "init_declarator" => current
9013                .child_by_field_name("declarator")
9014                .zip(current.child_by_field_name("value")),
9015            _ => None,
9016        };
9017        if let Some((name, arguments)) = macro_shape
9018            && name.kind() == "identifier"
9019            && arguments.kind() == "argument_list"
9020            && arguments.named_child_count() >= 2
9021            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
9022        {
9023            return true;
9024        }
9025        let mut cursor = current.walk();
9026        stack.extend(current.named_children(&mut cursor));
9027    }
9028    false
9029}
9030
9031fn cpp_recovered_template_macro_constructor<'tree>(
9032    node: Node<'tree>,
9033    source: &str,
9034) -> Option<(Node<'tree>, Node<'tree>)> {
9035    let mut prefix = node.prev_named_sibling()?;
9036    while prefix.kind() == "comment" {
9037        prefix = prefix.prev_named_sibling()?;
9038    }
9039    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
9040    let parameter = parameter_name
9041        .parent()
9042        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
9043    let declarator =
9044        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
9045    Some((declarator, parameter))
9046}
9047
9048fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
9049    let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) else {
9050        return false;
9051    };
9052    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
9053        cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
9054            || cpp_reparsed_template_macro_constructor_companion_is_indexable(
9055                function,
9056                parameter_name,
9057                source,
9058            )
9059    })
9060}
9061
9062fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
9063    let function_name = node
9064        .child_by_field_name("declarator")
9065        .and_then(extract_function_declarator)
9066        .and_then(cpp_function_declarator_name_node);
9067    if let Some(body) = cpp_reparsed_member_function_body(node)
9068        && function_name.is_some()
9069        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
9070    {
9071        return true;
9072    }
9073    cpp_reparsed_attribute_member_function(node, source)
9074        || cpp_reparsed_friend_function_is_indexable(node, source)
9075        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
9076        || cpp_reparsed_access_template_function_is_indexable(node, source)
9077        || cpp_recovered_template_macro_constructor(node, source).is_some()
9078}
9079
9080fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
9081    let mut cursor = root.walk();
9082    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
9083    let mut saw_member = false;
9084    let mut index = 0;
9085    while index < children.len() {
9086        let child = children[index];
9087        if let Some((_, fragmented)) = fragmented_plain_class_body(child, source) {
9088            let Some(tree) = cpp_reparse_fragmented_class_body(
9089                source,
9090                fragmented.reparse_start,
9091                fragmented.reparse_end,
9092            ) else {
9093                return false;
9094            };
9095            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
9096                return false;
9097            }
9098            saw_member = true;
9099            index += 1;
9100            while index < children.len()
9101                && children[index].end_byte() <= fragmented.class_range.end_byte
9102            {
9103                index += 1;
9104            }
9105            continue;
9106        }
9107        match child.kind() {
9108            "comment" => {}
9109            "labeled_statement" => saw_member = true,
9110            "function_definition" => {
9111                if child.has_error()
9112                    && !cpp_reparsed_member_function_is_indexable(child, source)
9113                    && cpp_sentinel_macro_region(child, source).is_none()
9114                {
9115                    return false;
9116                }
9117                saw_member = true;
9118            }
9119            "ERROR"
9120                if (cpp_reparsed_member_error_is_indexable(child)
9121                    || cpp_reparsed_adjacent_copy_control_error(child, source))
9122                    && (child
9123                        .next_named_sibling()
9124                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
9125                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
9126            {
9127                saw_member = true;
9128            }
9129            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
9130                saw_member = true;
9131            }
9132            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
9133                saw_member = true;
9134            }
9135            "expression_statement"
9136                if cpp_is_stray_semicolon(child, source)
9137                    && child.prev_named_sibling().is_some_and(|error| {
9138                        cpp_reparsed_member_error_is_indexable(error)
9139                            || cpp_reparsed_adjacent_copy_control_error(error, source)
9140                    }) =>
9141            {
9142                saw_member = true;
9143            }
9144            "compound_statement"
9145                if cpp_reparsed_constructor_body_is_indexable(child, source)
9146                    || cpp_reparsed_attribute_requires_body(child, source) =>
9147            {
9148                saw_member = true;
9149            }
9150            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
9151            _ => return false,
9152        }
9153        index += 1;
9154    }
9155    saw_member
9156}
9157
9158/// Detect the malformed constructor shape that tree-sitter exposes as an
9159/// access-label statement followed by initializer-looking declarations. The
9160/// declarations are not class members: visiting their `location(loc)` and
9161/// `string(s)` function declarators would publish synthetic functions. The
9162/// export-class fallback keeps the original sibling nodes and therefore avoids
9163/// this parser artifact. The returned range identifies the real constructor
9164/// header, which can be reparsed independently as a structured declarator.
9165fn cpp_reparsed_synthetic_initializer_constructor_range(
9166    root: Node<'_>,
9167    class_name: &str,
9168    source: &str,
9169    constructor_end: usize,
9170) -> Option<std::ops::Range<usize>> {
9171    let mut stack = {
9172        let mut cursor = root.walk();
9173        root.named_children(&mut cursor).collect::<Vec<_>>()
9174    };
9175    while let Some(current) = stack.pop() {
9176        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
9177            current,
9178            class_name,
9179            source,
9180            constructor_end,
9181        ) {
9182            return Some(range);
9183        }
9184        if current.kind() == "ERROR" {
9185            let mut cursor = current.walk();
9186            stack.extend(current.named_children(&mut cursor));
9187        }
9188    }
9189    None
9190}
9191
9192fn cpp_reparsed_synthetic_initializer_constructor(
9193    node: Node<'_>,
9194    class_name: &str,
9195    source: &str,
9196    constructor_end: usize,
9197) -> Option<std::ops::Range<usize>> {
9198    if node.kind() != "labeled_statement" {
9199        return None;
9200    }
9201    let mut cursor = node.walk();
9202    let named = node
9203        .named_children(&mut cursor)
9204        .filter(|child| child.kind() != "comment")
9205        .collect::<Vec<_>>();
9206    let label = named.first()?;
9207    if label.kind() != "statement_identifier"
9208        || !matches!(
9209            node_text(*label, source).trim(),
9210            "public" | "private" | "protected"
9211        )
9212    {
9213        return None;
9214    }
9215    let call_error_index = named.iter().position(|child| {
9216        if child.kind() != "ERROR" {
9217            return false;
9218        }
9219        let mut stack = vec![*child];
9220        while let Some(current) = stack.pop() {
9221            if current.kind() == "call_expression"
9222                && current
9223                    .child_by_field_name("function")
9224                    .is_some_and(|function| {
9225                        function.kind() == "identifier"
9226                            && node_text(function, source).trim() == class_name
9227                    })
9228            {
9229                return true;
9230            }
9231            let mut cursor = current.walk();
9232            stack.extend(current.named_children(&mut cursor));
9233        }
9234        false
9235    })?;
9236    let constructor_call = {
9237        let mut stack = vec![named[call_error_index]];
9238        let mut found = None;
9239        while let Some(current) = stack.pop() {
9240            if current.kind() == "call_expression"
9241                && current
9242                    .child_by_field_name("function")
9243                    .is_some_and(|function| {
9244                        function.kind() == "identifier"
9245                            && node_text(function, source).trim() == class_name
9246                    })
9247            {
9248                found = Some(current);
9249                break;
9250            }
9251            let mut cursor = current.walk();
9252            stack.extend(current.named_children(&mut cursor));
9253        }
9254        found
9255    };
9256    let constructor_call = constructor_call?;
9257    named.iter().skip(call_error_index + 1).find(|child| {
9258        child.kind() == "declaration" && child.has_error() && {
9259            let mut cursor = child.walk();
9260            child.named_children(&mut cursor).any(|declarator| {
9261                declarator.kind() == "init_declarator"
9262                    && declarator
9263                        .child_by_field_name("declarator")
9264                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
9265                    && declarator
9266                        .child_by_field_name("value")
9267                        .is_some_and(|value| value.kind() == "initializer_list")
9268            })
9269        }
9270    })?;
9271    Some(constructor_call.start_byte()..constructor_end)
9272}
9273
9274fn cpp_reparsed_exact_constructor_declarator<'tree>(
9275    root: Node<'tree>,
9276    start: usize,
9277    class_name: &str,
9278    source: &str,
9279) -> Option<Node<'tree>> {
9280    let mut candidate = None;
9281    let mut stack = vec![root];
9282    while let Some(current) = stack.pop() {
9283        if current.kind() == "function_declarator"
9284            && current.start_byte() == start
9285            && cpp_function_declarator_name_node(current)
9286                .is_some_and(|name| node_text(name, source).trim() == class_name)
9287        {
9288            if candidate.is_some() {
9289                return None;
9290            }
9291            candidate = Some(current);
9292            continue;
9293        }
9294        let mut cursor = current.walk();
9295        stack.extend(current.named_children(&mut cursor));
9296    }
9297    candidate
9298}
9299
9300fn cpp_is_indexable_item_kind(kind: &str) -> bool {
9301    matches!(
9302        kind,
9303        "namespace_definition"
9304            | "class_specifier"
9305            | "struct_specifier"
9306            | "union_specifier"
9307            | "enum_specifier"
9308            | "function_definition"
9309            | "template_declaration"
9310            | "declaration"
9311            | "field_declaration"
9312            | "alias_declaration"
9313            | "static_assert_declaration"
9314            | "type_definition"
9315            | "using_declaration"
9316            | "linkage_specification"
9317            | "preproc_def"
9318            | "preproc_function_def"
9319            | "preproc_include"
9320            | "preproc_if"
9321            | "preproc_ifdef"
9322            | "preproc_call"
9323    )
9324}
9325
9326#[cfg(test)]
9327mod tests {
9328    use super::*;
9329    use crate::adapter::parse_cpp_file;
9330    use brokk_bifrost_core::analyzer::parsed_file::{
9331        finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
9332    };
9333    use std::fmt::Write;
9334
9335    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
9336        let mut parser = tree_sitter::Parser::new();
9337        parser
9338            .set_language(&tree_sitter_cpp::LANGUAGE.into())
9339            .unwrap();
9340        let tree = parser.parse(source, None).unwrap();
9341        let file = ProjectFile::new(std::env::temp_dir(), name);
9342        parse_cpp_file(&file, source, &tree)
9343    }
9344
9345    #[test]
9346    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
9347        let source = r#"namespace control {
9348template <typename T>
9349class AnySpan;
9350template <typename T>
9351class ABSL_ATTRIBUTE_VIEW AnySpan {
9352 public:
9353  int begin() const;
9354};
9355}
9356
9357namespace absl {
9358ABSL_NAMESPACE_BEGIN
9359template <typename T>
9360class ABSL_ATTRIBUTE_VIEW Span {
9361 public:
9362  int begin() const;
9363  int back() const;
9364};
9365
9366int begin();
9367int back();
9368}
9369"#;
9370        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
9371        let declarations = parsed.declarations();
9372        assert!(
9373            declarations
9374                .iter()
9375                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
9376        );
9377        for method in ["begin", "back"] {
9378            assert!(declarations.iter().any(|unit| {
9379                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
9380            }));
9381            assert!(
9382                declarations.iter().any(|unit| {
9383                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
9384                })
9385            );
9386        }
9387        assert!(
9388            declarations
9389                .iter()
9390                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
9391        );
9392        assert!(
9393            declarations
9394                .iter()
9395                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
9396        );
9397        assert!(
9398            declarations
9399                .iter()
9400                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
9401        );
9402    }
9403
9404    #[test]
9405    fn explicit_global_member_definition_has_canonical_package_boundary() {
9406        let source = r#"
9407namespace arangodb::aql {
9408class ExecutionPlan {
9409 public:
9410  template<class... Args> Node* createNode(Args&&... args);
9411};
9412}
9413
9414template<class... Args>
9415Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
9416"#;
9417        let parsed = parse_cpp_declarations(source, "global-member.cpp");
9418
9419        assert!(parsed.declarations().iter().any(|unit| {
9420            unit.is_function()
9421                && unit.package_name() == "arangodb::aql"
9422                && unit.short_name() == "ExecutionPlan.createNode"
9423                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
9424        }));
9425    }
9426
9427    #[test]
9428    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
9429        let source = r#"
9430#ifndef TINYXML2_INCLUDED
9431#define TINYXML2_INCLUDED
9432namespace tinyxml2 {
9433class TINYXML2_LIB XMLUtil {
9434 public:
9435  static const char* SkipWhiteSpace(const char* p) {
9436    while (*p) {
9437      if (*p == ' ') {
9438        ++p;
9439      }
9440    }
9441    return p;
9442  }
9443  static bool StringEqual(const char* p, const char* q) {
9444    return p == q;
9445  }
9446  class TINYXML2_LIB Helper {
9447   public:
9448    void Touch();
9449  };
9450  static void ToStr(int value, char* buffer);
9451 private:
9452  static const char* writeBoolTrue;
9453};
9454
9455class TINYXML2_LIB XMLNode {
9456 public:
9457  virtual XMLNode* ShallowClone() const = 0;
9458  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
9459};
9460}
9461#endif
9462"#;
9463        let mut parser = tree_sitter::Parser::new();
9464        parser
9465            .set_language(&tree_sitter_cpp::LANGUAGE.into())
9466            .unwrap();
9467        let tree = parser.parse(source, None).unwrap();
9468        let mut boundary_found = false;
9469        walk_named_tree_preorder(tree.root_node(), true, |node| {
9470            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
9471                && name == "XMLUtil"
9472            {
9473                boundary_found = fragmented_export_sibling_class_boundary(node, source)
9474                    .and_then(|boundary| {
9475                        recover_exported_class_function_definition(boundary, source)
9476                    })
9477                    .is_some_and(|(_, name, _)| name == "XMLNode");
9478            }
9479            WalkControl::Continue
9480        });
9481        assert!(
9482            boundary_found,
9483            "fixture must exercise the recovered sibling boundary"
9484        );
9485
9486        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
9487        assert!(
9488            parsed
9489                .declarations()
9490                .iter()
9491                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
9492            "{:#?}",
9493            parsed.declarations()
9494        );
9495        assert!(
9496            parsed
9497                .declarations()
9498                .iter()
9499                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
9500            "{:#?}",
9501            parsed.declarations()
9502        );
9503        assert!(parsed.declarations().iter().any(|unit| {
9504            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
9505        }));
9506        assert!(
9507            parsed
9508                .declarations()
9509                .iter()
9510                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
9511        );
9512        assert!(
9513            parsed
9514                .declarations()
9515                .iter()
9516                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
9517        );
9518    }
9519
9520    #[test]
9521    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
9522        // Clang's diagnostic suite intentionally contains this ill-formed
9523        // spelling. The analyzer must retain the parser's explicit-global AST
9524        // boundary instead of constructing `cwg311::::cwg311::X`.
9525        let parsed = parse_cpp_declarations(
9526            r#"
9527namespace cwg311 {
9528namespace X { namespace Y {} }
9529namespace ::cwg311::X {}
9530}
9531"#,
9532            "explicit-global-namespace.cpp",
9533        );
9534
9535        assert!(parsed.declarations().iter().any(|unit| {
9536            unit.kind() == CodeUnitType::Module
9537                && unit.short_name() == "cwg311::X"
9538                && unit.fq_name() == "cwg311::X"
9539        }));
9540        assert!(
9541            parsed
9542                .declarations()
9543                .iter()
9544                .all(|unit| !unit.short_name().contains("::::")),
9545            "recovered namespace names must not retain empty scope components: {:#?}",
9546            parsed.declarations()
9547        );
9548    }
9549
9550    #[test]
9551    fn repeated_scope_separator_does_not_create_empty_function_owner() {
9552        let scope = ScopeInfo {
9553            package_name: "X".to_string(),
9554            module: None,
9555            class_unit: None,
9556            template_signature: None,
9557            template_metadata: None,
9558            declarations_are_fields: false,
9559            recovered_specialization_member_scope: false,
9560            visible_using_namespaces: Vec::new(),
9561        };
9562
9563        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
9564
9565        assert_eq!(owner, None);
9566        assert_eq!(name, "doit");
9567        assert_eq!(package, "X");
9568    }
9569
9570    #[test]
9571    fn trailing_decltype_expression_is_not_a_function_declarator() {
9572        let source = r#"
9573namespace boost { namespace detail {
9574#if ! defined(BOOST_NO_SFINAE_EXPR) && \
9575    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
9576    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
9577#define BOOST_THREAD_PROVIDES_INVOKE
9578#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
9579template <class Fp, class A0, class ...Args>
9580inline auto
9581invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
9582       BOOST_THREAD_RV_REF(Args) ...args)
9583    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
9584{
9585    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
9586}
9587#endif
9588#endif
9589}}
9590"#;
9591        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
9592
9593        assert!(
9594            parsed
9595                .declarations()
9596                .iter()
9597                .all(|unit| unit.short_name() != ".*f")
9598        );
9599    }
9600
9601    fn find_class_named<'tree>(
9602        root: Node<'tree>,
9603        source: &str,
9604        expected_name: &str,
9605    ) -> Option<Node<'tree>> {
9606        let mut stack = vec![root];
9607        while let Some(node) = stack.pop() {
9608            if node.kind() == "class_specifier"
9609                && node
9610                    .child_by_field_name("name")
9611                    .is_some_and(|name| node_text(name, source) == expected_name)
9612            {
9613                return Some(node);
9614            }
9615            let mut cursor = node.walk();
9616            stack.extend(node.named_children(&mut cursor));
9617        }
9618        None
9619    }
9620
9621    #[test]
9622    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
9623        let source = r#"EXPORT void definition(struct Value value) {}
9624EXPORT void prototype(struct Value value);
9625"#;
9626        let mut parser = tree_sitter::Parser::new();
9627        parser
9628            .set_language(&tree_sitter_cpp::LANGUAGE.into())
9629            .unwrap();
9630        let tree = parser.parse(source, None).unwrap();
9631        let root = tree.root_node();
9632        let mut cursor = root.walk();
9633        let callables = root
9634            .named_children(&mut cursor)
9635            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
9636            .collect::<Vec<_>>();
9637
9638        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
9639        for callable in callables {
9640            assert!(callable.has_error(), "fixture must exercise error recovery");
9641            assert!(
9642                cpp_sentinel_macro_parts(callable, source).is_none(),
9643                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
9644            );
9645        }
9646    }
9647
9648    #[test]
9649    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
9650        let source = r#"namespace absl {
9651ABSL_NAMESPACE_BEGIN
9652// Generate a floating-point variate conforming to a Beta distribution:
9653template <typename RealType = double>
9654class beta_distribution {
9655 public:
9656  using result_type = RealType;
9657
9658
9659  beta_distribution() : beta_distribution(1) {}
9660
9661  explicit beta_distribution(result_type alpha, result_type beta = 1)
9662      : param_(alpha, beta) {}
9663
9664  explicit beta_distribution(const param_type& p) : param_(p) {}
9665
9666  void reset() {}
9667
9668  // Generating functions
9669  template <typename URBG>
9670  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
9671    return (*this)(g, param_);
9672  }
9673
9674};
9675ABSL_NAMESPACE_END
9676}  // namespace absl
9677"#;
9678        let mut parser = tree_sitter::Parser::new();
9679        parser
9680            .set_language(&tree_sitter_cpp::LANGUAGE.into())
9681            .unwrap();
9682        let tree = parser.parse(source, None).unwrap();
9683        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
9684        let body = namespace
9685            .child_by_field_name("body")
9686            .expect("fixture namespace body");
9687        let sentinel = body.named_child(0).expect("sentinel envelope");
9688        let callable = sentinel
9689            .child_by_field_name("declarator")
9690            .and_then(extract_function_declarator)
9691            .and_then(cpp_function_declarator_name_node)
9692            .expect("preserved callable name");
9693
9694        assert_eq!(sentinel.kind(), "function_definition");
9695        assert_eq!(callable.kind(), "operator_name");
9696        assert!(
9697            cpp_sentinel_macro_parts(sentinel, source).is_some(),
9698            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
9699        );
9700    }
9701
9702    #[test]
9703    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
9704        let source = r#"namespace absl {
9705ABSL_NAMESPACE_BEGIN
9706// absl::discrete_distribution
9707//
9708// A discrete distribution produces random integers i, where 0 <= i < n
9709template <typename IntType = int>
9710class discrete_distribution {
9711 public:
9712  using result_type = IntType;
9713  class param_type {
9714   public:
9715    param_type() { init(); }
9716    template <typename InputIterator>
9717    explicit param_type(InputIterator begin, InputIterator end)
9718        : p_(begin, end) {
9719      init();
9720    }
9721  };
9722  discrete_distribution() : param_() {}
9723  explicit discrete_distribution(const param_type& p) : param_(p) {}
9724};
9725ABSL_NAMESPACE_END
9726}  // namespace absl
9727"#;
9728        let mut parser = tree_sitter::Parser::new();
9729        parser
9730            .set_language(&tree_sitter_cpp::LANGUAGE.into())
9731            .unwrap();
9732        let tree = parser.parse(source, None).unwrap();
9733        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
9734        let body = namespace
9735            .child_by_field_name("body")
9736            .expect("fixture namespace body");
9737        let sentinel = body.named_child(0).expect("sentinel envelope");
9738        let callable = sentinel
9739            .child_by_field_name("declarator")
9740            .and_then(extract_function_declarator)
9741            .and_then(cpp_function_declarator_name_node)
9742            .expect("preserved callable name");
9743
9744        assert_eq!(sentinel.kind(), "function_definition");
9745        assert_eq!(callable.kind(), "identifier");
9746        assert!(
9747            cpp_sentinel_macro_parts(sentinel, source).is_some(),
9748            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
9749        );
9750    }
9751
9752    #[test]
9753    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
9754        let source = r#"
9755#define CPPCHECKLIB
9756class Library {
9757    struct Container {
9758        CPPCHECKLIB static std::string toString(Yield yield);
9759        CPPCHECKLIB static std::string toString(Action action);
9760    };
9761};
9762"#;
9763        let mut parser = tree_sitter::Parser::new();
9764        parser
9765            .set_language(&tree_sitter_cpp::LANGUAGE.into())
9766            .unwrap();
9767        let tree = parser.parse(source, None).unwrap();
9768        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
9769        let parsed = parse_cpp_file(&file, source, &tree);
9770        assert!(
9771            parsed
9772                .declarations()
9773                .iter()
9774                .all(|unit| unit.fq_name() != "Library$Container.std"),
9775            "the qualified return-type namespace must not become a field: {:#?}",
9776            parsed.declarations()
9777        );
9778        for expected in ["(Yield)", "(Action)"] {
9779            assert!(
9780                parsed.declarations().iter().any(|unit| {
9781                    unit.is_function()
9782                        && unit.fq_name() == "Library$Container.toString"
9783                        && unit.signature() == Some(expected)
9784                }),
9785                "recovered toString overload {expected} is missing: {:#?}",
9786                parsed.declarations()
9787            );
9788        }
9789    }
9790
9791    #[test]
9792    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
9793        let source = r#"
9794#define SIMPLECPP_LIB
9795namespace simplecpp {
9796using TokenString = std::string;
9797struct Location { int line{}; };
9798class SIMPLECPP_LIB Token {
9799  TokenString prefix;
9800  void prefix_method() {}
9801 public:
9802  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
9803      whitespaceahead(wsahead), location(loc), string(s)
9804      // The comment must not hide the constructor body from recovery.
9805      {
9806      flags();
9807  }
9808  TokenString string;
9809  bool whitespaceahead;
9810  Location location;
9811  Token *previous{};
9812 private:
9813  void flags() {
9814      whitespaceahead = true;
9815  }
9816};
9817}
9818"#;
9819        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
9820
9821        let location_fields = parsed
9822            .declarations()
9823            .iter()
9824            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
9825            .collect::<Vec<_>>();
9826        assert_eq!(
9827            location_fields.len(),
9828            1,
9829            "location should have one class-owned declaration: {:#?}",
9830            parsed.declarations()
9831        );
9832        assert!(
9833            location_fields[0].is_field(),
9834            "location has wrong kind: {:#?}",
9835            parsed.declarations()
9836        );
9837        assert!(
9838            parsed.declarations().iter().all(|unit| {
9839                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
9840            })
9841        );
9842        assert!(
9843            parsed.declarations().iter().all(|unit| {
9844                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
9845            })
9846        );
9847        assert!(
9848            parsed
9849                .declarations()
9850                .iter()
9851                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
9852        );
9853        assert!(
9854            parsed
9855                .declarations()
9856                .iter()
9857                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
9858            "the recovered class must retain its constructor: {:#?}",
9859            parsed.declarations()
9860        );
9861        assert!(
9862            parsed
9863                .declarations()
9864                .iter()
9865                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
9866        );
9867        assert!(parsed.declarations().iter().any(|unit| {
9868            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
9869        }));
9870        let constructor = parsed
9871            .declarations()
9872            .iter()
9873            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
9874            .expect("recovered constructor");
9875        let constructor_start = source.find("Token(const").expect("constructor start");
9876        let constructor_end = source
9877            .get(
9878                ..source
9879                    .find("  TokenString string;")
9880                    .expect("constructor end"),
9881            )
9882            .expect("constructor slice")
9883            .trim_end()
9884            .len();
9885        assert!(
9886            parsed
9887                .navigation_ranges
9888                .get(constructor)
9889                .is_some_and(|ranges| {
9890                    ranges.iter().any(|range| {
9891                        range.start_byte == constructor_start && range.end_byte == constructor_end
9892                    })
9893                }),
9894            "constructor navigation must span the full body: {:#?}",
9895            parsed.navigation_ranges
9896        );
9897        assert_eq!(
9898            parsed
9899                .signature_metadata
9900                .get(constructor)
9901                .and_then(|metadata| metadata.first())
9902                .and_then(SignatureMetadata::callable_linkage),
9903            Some(CallableLinkage::External)
9904        );
9905        let token_class = parsed
9906            .declarations()
9907            .iter()
9908            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
9909            .expect("recovered Token class");
9910        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
9911        assert!(
9912            parsed
9913                .navigation_ranges
9914                .get(token_class)
9915                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
9916            "class navigation must include the terminating semicolon: {:#?}",
9917            parsed.navigation_ranges
9918        );
9919    }
9920
9921    #[test]
9922    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
9923        let source = r#"
9924#define SIMPLECPP_LIB
9925namespace simplecpp {
9926using TokenString = std::string;
9927class Macro;
9928struct Location {
9929  unsigned int fileIndex{};
9930  unsigned int line{};
9931  unsigned int col{};
9932};
9933struct Output {
9934  int type;
9935};
9936class SIMPLECPP_LIB Token {
9937 public:
9938  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
9939      whitespaceahead(wsahead), location(loc), string(s) {
9940      flags();
9941  }
9942  Token(const Token &tok) :
9943      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
9944      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
9945      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
9946  Token &operator=(const Token &tok) = delete;
9947  const TokenString& str() const { return string; }
9948  void setstr(const std::string &s) { string = s; flags(); }
9949  bool isOneOf(const char ops[]) const;
9950  TokenString macro;
9951  char op;
9952  bool comment;
9953  bool name;
9954  bool number;
9955  bool whitespaceahead;
9956  Location location;
9957  Token *previous{};
9958  Token *next{};
9959 private:
9960  void flags() {
9961      name = !string.empty();
9962      comment = false;
9963      number = false;
9964      op = 0;
9965  }
9966  TokenString string;
9967};
9968}
9969struct Following {
9970  int type;
9971};
9972class SIMPLECPP_LIB Later {
9973 public:
9974  Later(int value) : value(value) {}
9975  int value;
9976};
9977"#;
9978        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
9979        assert!(
9980            parsed
9981                .declarations()
9982                .iter()
9983                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
9984        );
9985        assert!(
9986            !parsed
9987                .declarations()
9988                .iter()
9989                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
9990        );
9991        assert!(
9992            parsed
9993                .declarations()
9994                .iter()
9995                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
9996        );
9997        assert!(
9998            !parsed
9999                .declarations()
10000                .iter()
10001                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
10002        );
10003        assert!(
10004            parsed
10005                .declarations()
10006                .iter()
10007                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
10008        );
10009        assert!(
10010            parsed
10011                .declarations()
10012                .iter()
10013                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
10014        );
10015        assert!(
10016            parsed
10017                .declarations()
10018                .iter()
10019                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
10020        );
10021        assert!(
10022            parsed
10023                .declarations()
10024                .iter()
10025                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
10026        );
10027        assert!(
10028            parsed
10029                .declarations()
10030                .iter()
10031                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
10032        );
10033        assert!(
10034            parsed
10035                .declarations()
10036                .iter()
10037                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
10038        );
10039        assert!(parsed.declarations().iter().all(|unit| {
10040            !matches!(
10041                unit.fq_name().as_str(),
10042                "simplecpp.Token.Following" | "simplecpp.Token.Later"
10043            )
10044        }));
10045        assert!(
10046            !parsed
10047                .declarations()
10048                .iter()
10049                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
10050            "the following struct must remain outside the recovered Token class"
10051        );
10052    }
10053
10054    #[test]
10055    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
10056        let source = r#"
10057#define SIMPLECPP_LIB
10058namespace {
10059namespace simplecpp {
10060using TokenString = std::string;
10061struct Location { int line{}; };
10062class SIMPLECPP_LIB HiddenToken {
10063 public:
10064  HiddenToken(const TokenString &s, const Location &loc) :
10065      location(loc), string(s) {
10066      flags();
10067  }
10068  TokenString string;
10069  Location location;
10070  HiddenToken *previous{};
10071 private:
10072  void flags() {}
10073};
10074}
10075}
10076"#;
10077        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
10078        let constructor = parsed
10079            .declarations()
10080            .iter()
10081            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
10082            .expect("recovered anonymous-namespace constructor");
10083        assert_eq!(
10084            parsed
10085                .signature_metadata
10086                .get(constructor)
10087                .and_then(|metadata| metadata.first())
10088                .and_then(SignatureMetadata::callable_linkage),
10089            Some(CallableLinkage::Internal)
10090        );
10091    }
10092
10093    #[test]
10094    fn macro_qualified_static_field_keeps_real_declarator() {
10095        let source = r#"#define JSON_INLINE_VARIABLE
10096struct Reader {
10097static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
10098static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
10099static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
10100};"#;
10101        let mut parser = tree_sitter::Parser::new();
10102        parser
10103            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10104            .unwrap();
10105        let tree = parser.parse(source, None).unwrap();
10106        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
10107        let parsed = parse_cpp_file(&file, source, &tree);
10108        for expected in [
10109            "Reader.npos",
10110            "Reader.other",
10111            "Reader.pointer",
10112            "Reader.reference",
10113        ] {
10114            assert!(
10115                parsed
10116                    .declarations()
10117                    .iter()
10118                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
10119                "real macro-decorated field {expected} is missing: {:#?}",
10120                parsed.declarations()
10121            );
10122        }
10123        assert!(
10124            parsed
10125                .declarations()
10126                .iter()
10127                .all(|unit| unit.fq_name() != "Reader.std"),
10128            "qualified type prefix became a pseudo-field: {:#?}",
10129            parsed.declarations()
10130        );
10131        let root = tree.root_node();
10132        let mut stack = vec![root];
10133        let mut signatures = Vec::new();
10134        while let Some(current) = stack.pop() {
10135            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
10136            {
10137                signatures.extend(
10138                    declarators
10139                        .into_iter()
10140                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
10141                );
10142            }
10143            let mut cursor = current.walk();
10144            stack.extend(current.named_children(&mut cursor));
10145        }
10146        signatures.sort();
10147        assert_eq!(
10148            signatures,
10149            [
10150                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
10151                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
10152                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
10153                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
10154            ]
10155        );
10156    }
10157
10158    fn member_function_linkage(source: &str) -> CallableLinkage {
10159        let mut parser = tree_sitter::Parser::new();
10160        parser
10161            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10162            .unwrap();
10163        let tree = parser.parse(source, None).unwrap();
10164        let mut stack = vec![tree.root_node()];
10165        while let Some(node) = stack.pop() {
10166            if node.kind() == "function_definition" {
10167                let mut current = node.parent();
10168                while let Some(parent) = current {
10169                    if matches!(
10170                        parent.kind(),
10171                        "class_specifier" | "struct_specifier" | "union_specifier"
10172                    ) {
10173                        return cpp_callable_linkage(node, source);
10174                    }
10175                    current = parent.parent();
10176                }
10177            }
10178            let mut cursor = node.walk();
10179            stack.extend(node.named_children(&mut cursor));
10180        }
10181        panic!("fixture has no member function definition");
10182    }
10183
10184    #[test]
10185    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
10186        assert_eq!(
10187            member_function_linkage("struct Named { int method() { return 1; } };"),
10188            CallableLinkage::External
10189        );
10190        assert_eq!(
10191            member_function_linkage(
10192                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
10193            ),
10194            CallableLinkage::Internal
10195        );
10196        assert_eq!(
10197            member_function_linkage("struct { int method() { return 1; } } instance;"),
10198            CallableLinkage::Internal
10199        );
10200        assert_eq!(
10201            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
10202            CallableLinkage::Internal
10203        );
10204    }
10205
10206    #[test]
10207    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
10208        let source = r#"
10209#ifndef PROTON_VALUE_HPP
10210#define PROTON_VALUE_HPP
10211namespace proton {
10212namespace internal {
10213class value_base {
10214  protected:
10215    internal::data& data();
10216    internal::data data_;
10217  friend class codec::encoder;
10218  friend class codec::decoder;
10219};
10220}
10221class value : public internal::value_base, private internal::comparable<value> {
10222  private:
10223    template<class T, class U=void> struct assignable :
10224        public std::enable_if<codec::is_encodable<T>::value, U> {};
10225    template<class U> struct assignable<value, U> {};
10226  public:
10227    PN_CPP_EXTERN value();
10228    PN_CPP_EXTERN value(const value&);
10229    PN_CPP_EXTERN value& operator=(const value&);
10230    PN_CPP_EXTERN value(value&&);
10231    PN_CPP_EXTERN value& operator=(value&&);
10232    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
10233    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
10234        codec::encoder e(*this);
10235        e << x;
10236        return *this;
10237    }
10238    PN_CPP_EXTERN type_id type() const;
10239    PN_CPP_EXTERN bool empty() const;
10240    PN_CPP_EXTERN void clear();
10241    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
10242    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
10243  friend PN_CPP_EXTERN void swap(value&, value&);
10244  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
10245  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
10246  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
10247    value(pn_data_t* d);
10248    void reset(pn_data_t* d = 0);
10249};
10250}
10251#endif
10252"#;
10253        let mut parser = tree_sitter::Parser::new();
10254        parser
10255            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10256            .unwrap();
10257        let tree = parser.parse(source, None).unwrap();
10258        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
10259        let parsed = parse_cpp_file(&file, source, &tree);
10260        let macro_constructors = parsed
10261            .signature_metadata
10262            .iter()
10263            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
10264            .flat_map(|(_, metadata)| metadata)
10265            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
10266            .collect::<Vec<_>>();
10267
10268        assert_eq!(
10269            macro_constructors.len(),
10270            3,
10271            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
10272            parsed.declarations()
10273        );
10274        assert!(
10275            macro_constructors.iter().all(|metadata| {
10276                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
10277            }),
10278            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
10279        );
10280    }
10281
10282    #[test]
10283    fn recovered_export_class_typedef_uses_displaced_alias_name() {
10284        let source = r#"
10285namespace spi {
10286class Filter {
10287public:
10288    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
10289};
10290}
10291namespace filter {
10292class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
10293{
10294public:
10295    typedef spi::Filter BASE_CLASS;
10296    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
10297    BEGIN_LOG4CXX_CAST_MAP()
10298    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
10299    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
10300    END_LOG4CXX_CAST_MAP()
10301    FilterDecision decide() const;
10302};
10303}
10304"#;
10305        let mut parser = tree_sitter::Parser::new();
10306        parser
10307            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10308            .unwrap();
10309        let tree = parser.parse(source, None).unwrap();
10310        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
10311        let parsed = parse_cpp_file(&file, source, &tree);
10312        assert!(
10313            parsed.declarations().iter().any(|unit| {
10314                unit.is_class()
10315                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
10316                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
10317            }),
10318            "the displaced typedef alias must retain its declared name: {:#?}",
10319            parsed.declarations()
10320        );
10321        assert!(
10322            parsed
10323                .declarations()
10324                .iter()
10325                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
10326            "the qualified underlying type must not become a false nested alias: {:#?}",
10327            parsed.declarations()
10328        );
10329    }
10330
10331    #[test]
10332    fn exported_single_base_recovery_uses_displaced_class_name() {
10333        let source = r#"
10334class CORE_EXPORT QgsPoint : public AbstractGeometry
10335{
10336    Q_GADGET
10337
10338    Q_PROPERTY( double x READ x WRITE setX )
10339    Q_PROPERTY( double y READ y WRITE setY )
10340    Q_PROPERTY( double z READ z WRITE setZ )
10341    Q_PROPERTY( double m READ m WRITE setM )
10342
10343  public:
10344#ifndef SIP_RUN
10345    QgsPoint(
10346      double x = std::numeric_limits<double>::quiet_NaN(),
10347      double y = std::numeric_limits<double>::quiet_NaN(),
10348      double z = std::numeric_limits<double>::quiet_NaN(),
10349      double m = std::numeric_limits<double>::quiet_NaN(),
10350      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
10351    );
10352#else
10353    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 )];
10354    % MethodCode
10355    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
10356    {
10357      int state;
10358      sipIsErr = 0;
10359      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
10360      if ( !sipIsErr )
10361      {
10362        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
10363      }
10364      sipReleaseType( p, sipType_QgsPointXY, state );
10365    }
10366    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
10367    {
10368      int state;
10369      sipIsErr = 0;
10370
10371      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
10372      if ( !sipIsErr )
10373      {
10374        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
10375      }
10376      sipReleaseType( p, sipType_QPointF, state );
10377    }
10378    else if (
10379      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
10380      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
10381      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
10382      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
10383    {
10384      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
10385      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
10386      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
10387      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
10388      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
10389      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
10390    }
10391    else // Invalid ctor arguments
10392    {
10393      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
10394      sipIsErr = 1;
10395    }
10396    % End
10397#endif
10398
10399    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
10400    explicit QgsPoint( QPointF p ) SIP_SKIP;
10401    explicit QgsPoint(
10402      Qgis::WkbType wkbType,
10403      double x = std::numeric_limits<double>::quiet_NaN(),
10404      double y = std::numeric_limits<double>::quiet_NaN(),
10405      double z = std::numeric_limits<double>::quiet_NaN(),
10406      double m = std::numeric_limits<double>::quiet_NaN()
10407    ) SIP_SKIP;
10408    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
10409    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
10410    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
10411#ifndef SIP_RUN
10412  private:
10413    bool fuzzyHelper(
10414      double epsilon,
10415      const AbstractGeometry &other,
10416      bool is3DFlag,
10417      bool isMeasureFlag
10418    ) const
10419    {
10420      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
10421    }
10422#endif
10423};
10424class Ordinary : public Base { public: Ordinary(); };
10425class API_EXPORT Plain { public: Plain(); };
10426class API_EXPORT : public Base {};
10427class
10428PN_CPP_CLASS_EXTERN Sender : public Link {
10429    Sender();
10430};
10431class thread_ctx_t {};
10432class ctx_t ZMQ_FINAL : public thread_ctx_t {
10433    bool start();
10434};
10435"#;
10436        let mut parser = tree_sitter::Parser::new();
10437        parser
10438            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10439            .unwrap();
10440        let tree = parser.parse(source, None).unwrap();
10441        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
10442        let parsed = parse_cpp_file(&file, source, &tree);
10443        let declarations = parsed.declarations();
10444
10445        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
10446            assert!(
10447                declarations
10448                    .iter()
10449                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
10450                "missing recovered class {expected}: {declarations:#?}"
10451            );
10452        }
10453        let qgs_point = declarations
10454            .iter()
10455            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
10456            .expect("recovered QgsPoint class");
10457        assert_eq!(
10458            parsed.raw_supertypes.get(qgs_point),
10459            Some(&vec!["AbstractGeometry".to_string()]),
10460            "single-base export recovery must retain its displaced base"
10461        );
10462        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
10463        assert!(
10464            parsed
10465                .navigation_ranges
10466                .get(qgs_point)
10467                .is_some_and(|ranges| {
10468                    !ranges.is_empty()
10469                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
10470                }),
10471            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
10472            parsed.navigation_ranges.get(qgs_point)
10473        );
10474        let sender = declarations
10475            .iter()
10476            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
10477            .expect("recovered Sender class");
10478        assert_eq!(
10479            parsed.raw_supertypes.get(sender),
10480            Some(&vec!["Link".to_string()]),
10481            "post-declarator export recovery must retain its displaced base"
10482        );
10483        let ctx = declarations
10484            .iter()
10485            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
10486            .expect("recovered ctx_t class");
10487        assert_eq!(
10488            parsed.raw_supertypes.get(ctx),
10489            Some(&vec!["thread_ctx_t".to_string()]),
10490            "postfix export-macro recovery must retain its displaced base"
10491        );
10492        assert!(
10493            declarations.iter().any(|unit| {
10494                unit.is_function()
10495                    && unit.fq_name() == "QgsPoint.QgsPoint"
10496                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
10497            }),
10498            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
10499        );
10500        assert!(
10501            declarations.iter().all(|unit| {
10502                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
10503            }),
10504            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
10505        );
10506    }
10507
10508    #[test]
10509    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
10510        let positive_source =
10511            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
10512        let mut parser = tree_sitter::Parser::new();
10513        parser
10514            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10515            .unwrap();
10516        let positive_tree = parser.parse(positive_source, None).unwrap();
10517        assert!(cpp_reparsed_members_are_indexable(
10518            positive_tree.root_node(),
10519            positive_source
10520        ));
10521
10522        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
10523        let negative_tree = parser.parse(negative_source, None).unwrap();
10524        assert!(!cpp_reparsed_members_are_indexable(
10525            negative_tree.root_node(),
10526            negative_source
10527        ));
10528    }
10529
10530    #[test]
10531    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
10532        let copy_control_source = r#"
10533public:
10534    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
10535    explicit Token(const Token* tok);
10536    ~Token();
10537    Token* astOperand1() { return nullptr; }
10538"#;
10539        let constraint_source = r#"
10540private:
10541    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
10542    static T *tokAtImpl(T *tok, int index) {
10543        return tok;
10544    }
10545
10546    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
10547    static T *linkAtImpl(T *tok, int index) {
10548        return tok;
10549    }
10550
10551public:
10552    int late() const { return 1; }
10553"#;
10554        let mut parser = tree_sitter::Parser::new();
10555        parser
10556            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10557            .unwrap();
10558        let copy_control_tree = parser
10559            .parse(copy_control_source, None)
10560            .expect("parse copy-control fixture");
10561        assert!(
10562            copy_control_tree.root_node().has_error(),
10563            "fixture must exercise adjacent copy-control recovery"
10564        );
10565        assert!(
10566            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
10567            "a complete late getter must remain recoverable after adjacent copy-control declarations"
10568        );
10569        let mut cursor = copy_control_tree.root_node().walk();
10570        assert!(
10571            copy_control_tree
10572                .root_node()
10573                .named_children(&mut cursor)
10574                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
10575            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
10576            copy_control_tree.root_node().to_sexp()
10577        );
10578        let constraint_tree = parser
10579            .parse(constraint_source, None)
10580            .expect("parse constraint-macro fixture");
10581        assert!(constraint_tree.root_node().has_error());
10582        assert!(
10583            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
10584            "complete constraint-macro members must not hide a later ordinary member"
10585        );
10586        let mut cursor = constraint_tree.root_node().walk();
10587        assert!(
10588            constraint_tree
10589                .root_node()
10590                .named_children(&mut cursor)
10591                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
10592                    child,
10593                    constraint_source
10594                )),
10595            "fixture must retain the split constraint-macro prefix/function geometry"
10596        );
10597    }
10598
10599    #[test]
10600    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
10601        let source = r#"
10602struct Analyzer {
10603    struct Action {
10604        Action() = default;
10605        Action(const Action&) = default;
10606        Action& operator=(const Action& rhs) & = default;
10607
10608        template<class T,
10609                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
10610                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
10611        // NOLINTNEXTLINE(google-explicit-constructor)
10612        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
10613        {}
10614
10615        enum : std::uint16_t { None = 0, Read = (1 << 0) };
10616        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
10617
10618    private:
10619        unsigned int mFlag{};
10620    };
10621
10622    enum class Direction : unsigned char { Forward, Reverse };
10623    virtual Action analyze(Direction d) const = 0;
10624};
10625"#;
10626        let mut parser = tree_sitter::Parser::new();
10627        parser
10628            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10629            .unwrap();
10630        let tree = parser.parse(source, None).unwrap();
10631        assert!(tree.root_node().has_error());
10632        let root = tree.root_node();
10633        let outer = root
10634            .named_children(&mut root.walk())
10635            .find(|child| child.kind() == "ERROR")
10636            .expect("fragmented Analyzer prefix");
10637        let (outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
10638            .expect("structured Analyzer fragment boundary");
10639        assert_eq!(outer_name, "Analyzer");
10640        let outer_tree = cpp_reparse_fragmented_class_body(
10641            source,
10642            outer_fragment.reparse_start,
10643            outer_fragment.reparse_end,
10644        )
10645        .expect("reparse Analyzer body");
10646        let outer_root = outer_tree.root_node();
10647        let action_prefix = outer_root
10648            .named_children(&mut outer_root.walk())
10649            .find(|child| child.kind() == "ERROR")
10650            .expect("fragmented Action prefix");
10651        let (action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
10652            .expect("structured Action fragment boundary");
10653        assert_eq!(action_name, "Action");
10654        let action_tree = cpp_reparse_fragmented_class_body(
10655            source,
10656            action_fragment.reparse_start,
10657            action_fragment.reparse_end,
10658        )
10659        .expect("reparse Action body");
10660        let action_root = action_tree.root_node();
10661        let macro_prefix = action_root
10662            .named_children(&mut action_root.walk())
10663            .find(|child| child.kind() == "ERROR")
10664            .expect("constraint macro prefix");
10665        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
10666            .expect("structured template macro prefix");
10667        let macro_companion =
10668            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
10669        assert!(
10670            cpp_reparsed_template_macro_constructor_companion_is_indexable(
10671                macro_companion,
10672                macro_parameter,
10673                source,
10674            ),
10675            "split constrained constructor must be admitted: {}",
10676            macro_companion.to_sexp()
10677        );
10678        assert!(
10679            cpp_reparsed_members_are_indexable(action_root, source),
10680            "complete Action body must pass the recovery gate: {}",
10681            action_tree.root_node().to_sexp()
10682        );
10683        assert!(
10684            cpp_reparsed_members_are_indexable(outer_root, source),
10685            "complete Analyzer body must pass the recovery gate: {}",
10686            outer_tree.root_node().to_sexp()
10687        );
10688        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
10689        let parsed = parse_cpp_file(&file, source, &tree);
10690        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
10691            assert!(
10692                parsed
10693                    .declarations()
10694                    .iter()
10695                    .any(|unit| unit.fq_name() == expected),
10696                "missing recovered declaration {expected}: {:#?}",
10697                parsed.declarations()
10698            );
10699        }
10700        assert!(
10701            parsed
10702                .declarations()
10703                .iter()
10704                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
10705            "nested members must not remain flattened: {:#?}",
10706            parsed.declarations()
10707        );
10708    }
10709
10710    #[test]
10711    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
10712        let source = r#"
10713raw_hash_set& operator=(raw_hash_set&& that) {
10714  return move_assign(
10715      std::move(that),
10716      typename AllocTraits::propagate_on_container_move_assignment());
10717}
10718
10719iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
10720  return {};
10721}
10722
10723void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
10724
10725iterator insert(const_iterator hint, value_type&& value)
10726    ABSL_ATTRIBUTE_LIFETIME_BOUND {
10727  return {};
10728}
10729
10730friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
10731  return left.size() == right.size();
10732}
10733
10734static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
10735  return static_cast<slot_type*>(buffer);
10736}
10737
10738protected:
10739// Included-range recovery can attach this comment to the template prefix.
10740template <class K>
10741void AssertOnFind([[maybe_unused]] const K& key) {
10742  Check(key);
10743}
10744"#;
10745        let mut parser = tree_sitter::Parser::new();
10746        parser
10747            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10748            .unwrap();
10749        let tree = parser.parse(source, None).unwrap();
10750        assert!(
10751            tree.root_node().has_error(),
10752            "the fixture must exercise tree-sitter's errorful member shapes"
10753        );
10754        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
10755
10756        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
10757        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
10758        assert!(!cpp_reparsed_members_are_indexable(
10759            incomplete_tree.root_node(),
10760            incomplete_source
10761        ));
10762
10763        let outside_error_source = "int foo() stray_attribute {}\n";
10764        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
10765        assert!(outside_error_tree.root_node().has_error());
10766        assert!(!cpp_reparsed_members_are_indexable(
10767            outside_error_tree.root_node(),
10768            outside_error_source
10769        ));
10770
10771        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
10772        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
10773        assert!(!cpp_reparsed_members_are_indexable(
10774            variable_initializer_tree.root_node(),
10775            variable_initializer_source
10776        ));
10777    }
10778
10779    #[test]
10780    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
10781        let positive_source = r#"
10782std::pair<iterator, bool> insert(init_type&& value)
10783    ABSL_ATTRIBUTE_LIFETIME_BOUND
10784#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
10785  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
10786#endif
10787{
10788  return emplace(std::move(value));
10789}
10790"#;
10791        let mut parser = tree_sitter::Parser::new();
10792        parser
10793            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10794            .unwrap();
10795        let positive_tree = parser.parse(positive_source, None).unwrap();
10796        assert!(
10797            positive_tree.root_node().has_error(),
10798            "the fixture must exercise the split attribute/requires shape"
10799        );
10800        assert!(cpp_reparsed_members_are_indexable(
10801            positive_tree.root_node(),
10802            positive_source
10803        ));
10804
10805        let template_return_source = r#"
10806pair<int> insert(init_type&& value)
10807    ABSL_ATTRIBUTE_LIFETIME_BOUND
10808#if LANGUAGE_LEVEL >= 202002L
10809  requires(!Predicate<init_type>::value)
10810#endif
10811// Attributes and the function body may be separated by comments.
10812{
10813  return {};
10814}
10815"#;
10816        let template_return_tree = parser.parse(template_return_source, None).unwrap();
10817        assert!(
10818            cpp_reparsed_members_are_indexable(
10819                template_return_tree.root_node(),
10820                template_return_source
10821            ),
10822            "template-return attribute/requires tree: {}",
10823            template_return_tree.root_node().to_sexp()
10824        );
10825
10826        let no_body_source = r#"
10827std::pair<iterator, bool> insert(init_type&& value)
10828    ABSL_ATTRIBUTE_LIFETIME_BOUND
10829#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
10830  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
10831#endif
10832+ 0;
10833"#;
10834        let no_body_tree = parser.parse(no_body_source, None).unwrap();
10835        assert!(!cpp_reparsed_members_are_indexable(
10836            no_body_tree.root_node(),
10837            no_body_source
10838        ));
10839
10840        let extra_payload_source = r#"
10841pair<int> insert(init_type&& value)
10842    ABSL_ATTRIBUTE_LIFETIME_BOUND
10843#if LANGUAGE_LEVEL >= 202002L
10844  int unrelated;
10845  requires(Predicate<init_type>::value)
10846#endif
10847{
10848  return {};
10849}
10850"#;
10851        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
10852        assert!(!cpp_reparsed_members_are_indexable(
10853            extra_payload_tree.root_node(),
10854            extra_payload_source
10855        ));
10856
10857        let variable_initializer_source = r#"
10858int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
10859#if LANGUAGE_LEVEL >= 202002L
10860  requires(true)
10861#endif
10862{
10863  bad;
10864}
10865"#;
10866        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
10867        assert!(!cpp_reparsed_members_are_indexable(
10868            variable_initializer_tree.root_node(),
10869            variable_initializer_source
10870        ));
10871    }
10872
10873    #[test]
10874    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
10875        let source = include_str!(concat!(
10876            env!("CARGO_MANIFEST_DIR"),
10877            "/../../tests/fixtures/cpp_macro_sentinel_raw_hash_set.h"
10878        ));
10879        let mut parser = tree_sitter::Parser::new();
10880        parser
10881            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10882            .unwrap();
10883        let tree = parser.parse(source, None).unwrap();
10884        let field = "    raw_hash_set& s;";
10885        let start = source.find(field).expect("InsertSlot field") + 4;
10886        let node = tree
10887            .root_node()
10888            .descendant_for_byte_range(start, start + "raw_hash_set".len())
10889            .expect("raw_hash_set type node");
10890        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
10891
10892        assert_eq!(
10893            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
10894            Some(vec![
10895                "absl".to_string(),
10896                "container_internal".to_string(),
10897                "raw_hash_set".to_string(),
10898                "InsertSlot".to_string(),
10899            ])
10900        );
10901    }
10902
10903    #[test]
10904    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
10905        const DISTINCT_PER_KIND: usize = 64;
10906        let mut source = String::new();
10907        for index in 0..DISTINCT_PER_KIND {
10908            writeln!(source, "typedef int Alias{index};").unwrap();
10909        }
10910        writeln!(source, "typedef long Alias0;").unwrap();
10911        for index in 0..DISTINCT_PER_KIND {
10912            writeln!(source, "#define MACRO_{index} {index}").unwrap();
10913        }
10914        writeln!(source, "#define MACRO_0 duplicate").unwrap();
10915        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
10916
10917        let mut parser = tree_sitter::Parser::new();
10918        parser
10919            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10920            .unwrap();
10921        let tree = parser.parse(&source, None).unwrap();
10922        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
10923
10924        start_declaration_identity_comparison_probe();
10925        let parsed = parse_cpp_file(&file, &source, &tree);
10926        let comparisons = finish_declaration_identity_comparison_probe();
10927
10928        assert_eq!(
10929            DISTINCT_PER_KIND + 1,
10930            parsed
10931                .declarations()
10932                .iter()
10933                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
10934                .count(),
10935            "every physical typedef alias declaration must be retained so \
10936             conditional branch guards stay available to the resolver"
10937        );
10938        assert_eq!(
10939            DISTINCT_PER_KIND,
10940            parsed
10941                .declarations()
10942                .iter()
10943                .filter(|unit| {
10944                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
10945                })
10946                .count(),
10947            "macros should retain semantic-identity deduplication"
10948        );
10949        assert_eq!(
10950            2,
10951            parsed
10952                .declarations()
10953                .iter()
10954                .filter(|unit| {
10955                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
10956                })
10957                .count(),
10958            "function overloads must remain distinct"
10959        );
10960
10961        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
10962        assert!(
10963            comparisons <= dedup_inputs * 4,
10964            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
10965        );
10966    }
10967
10968    #[test]
10969    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
10970        let source = r#"namespace absl {
10971ABSL_NAMESPACE_BEGIN namespace container_internal {
10972template <typename T>
10973class broken {
10974 public:
10975  using value_type = T;
10976  T operator->() const { return &operator*(); }
10977  using alias = value_type;
10978};
10979}
10980}
10981"#;
10982        let mut parser = tree_sitter::Parser::new();
10983        parser
10984            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10985            .unwrap();
10986        let tree = parser.parse(source, None).unwrap();
10987        let broken = find_class_named(tree.root_node(), source, "broken")
10988            .expect("the positive fixture must expose the broken class node");
10989        assert!(
10990            broken.has_error(),
10991            "the positive fixture must retain an internal parser error"
10992        );
10993        assert!(
10994            cpp_complete_class_body_close(broken).is_some(),
10995            "the positive fixture must expose a real class body close"
10996        );
10997        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
10998        assert!(
10999            recovered.iter().any(|class| {
11000                class.scope_components == ["absl", "container_internal", "broken"]
11001            }),
11002            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
11003        );
11004    }
11005
11006    #[test]
11007    fn sentinel_recovery_keeps_members_after_nested_body_close() {
11008        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
11009NLOHMANN_BASIC_JSON_TPL_DECLARATION
11010class basic_json {
11011 private:
11012  union storage {
11013    int value;
11014  } data;
11015 public:
11016  using late_alias = int;
11017  late_alias value() const;
11018};
11019NLOHMANN_JSON_NAMESPACE_END
11020"#;
11021        let mut parser = tree_sitter::Parser::new();
11022        parser
11023            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11024            .unwrap();
11025        let tree = parser.parse(source, None).unwrap();
11026        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11027        let basic_json = recovered
11028            .iter()
11029            .find(|class| {
11030                class
11031                    .scope_components
11032                    .last()
11033                    .is_some_and(|name| name == "basic_json")
11034            })
11035            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
11036        let late_alias = source
11037            .find("late_alias value")
11038            .expect("late alias reference");
11039        assert!(
11040            basic_json.class_range.start_byte < late_alias
11041                && late_alias < basic_json.class_range.end_byte,
11042            "the recovered class range must include members after a nested close: {basic_json:#?}"
11043        );
11044    }
11045
11046    #[test]
11047    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
11048        let source = r#"namespace absl {
11049ABSL_NAMESPACE_BEGIN namespace container_internal {
11050template <typename T>
11051class broken {
11052 public:
11053  using value_type = T;
11054  T operator->() const { return &operator*(); }
11055}
11056}
11057"#;
11058        let mut parser = tree_sitter::Parser::new();
11059        parser
11060            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11061            .unwrap();
11062        let tree = parser.parse(source, None).unwrap();
11063        let broken = find_class_named(tree.root_node(), source, "broken")
11064            .expect("the negative fixture must expose the malformed class node");
11065        assert!(
11066            broken.has_error(),
11067            "the negative fixture must retain a parser error"
11068        );
11069        assert!(
11070            cpp_complete_class_body_close(broken).is_none(),
11071            "the malformed class must not expose a real body close"
11072        );
11073        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11074        assert!(
11075            recovered
11076                .iter()
11077                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
11078            "an incomplete class must not borrow the namespace close: {recovered:#?}"
11079        );
11080    }
11081
11082    #[test]
11083    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
11084        let source = r#"namespace absl {
11085ABSL_NAMESPACE_BEGIN namespace container_internal {
11086template <typename T>
11087struct broken {
11088  using value_type = T;
11089};
11090}
11091
11092#ifdef OWNER_DEF
11093template <typename T>
11094typename broken<T>::value_type broken<T>::method() {
11095  value_type value{};
11096  return value;
11097}
11098#endif
11099
11100namespace sibling {
11101template <typename T>
11102typename broken<T>::value_type broken<T>::other() {
11103  value_type value{};
11104  return value;
11105}
11106}
11107
11108ABSL_NAMESPACE_END
11109}
11110"#;
11111        let mut parser = tree_sitter::Parser::new();
11112        parser
11113            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11114            .unwrap();
11115        let tree = parser.parse(source, None).unwrap();
11116        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11117        let broken = recovered
11118            .iter()
11119            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
11120            .expect("the sentinel class must be recovered");
11121        let method_start = source
11122            .find("typename broken<T>::value_type broken<T>::method()")
11123            .expect("guarded sibling owner");
11124        let method_end = source[method_start..]
11125            .find("\n}")
11126            .map(|offset| method_start + offset + 2)
11127            .expect("guarded sibling owner close");
11128        assert!(
11129            broken
11130                .owner_ranges
11131                .iter()
11132                .any(|owner| owner.range.start_byte <= method_start
11133                    && method_end <= owner.range.end_byte),
11134            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
11135        );
11136        let sibling_start = source
11137            .find("typename broken<T>::value_type broken<T>::other()")
11138            .expect("nested namespace sibling owner");
11139        assert!(
11140            broken
11141                .owner_ranges
11142                .iter()
11143                .all(|owner| owner.range.start_byte > sibling_start
11144                    || owner.range.end_byte <= sibling_start),
11145            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
11146        );
11147    }
11148
11149    #[test]
11150    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
11151        let source = r#"#ifdef OUTER
11152namespace absl {
11153ABSL_NAMESPACE_BEGIN namespace container_internal {
11154template <typename T>
11155struct broken {
11156  using value_type = T;
11157};
11158}
11159}
11160
11161#ifdef OWNER_DEF
11162template <typename T>
11163typename broken<T>::value_type broken<T>::method() {
11164  value_type value{};
11165  return value;
11166}
11167#endif
11168#endif
11169"#;
11170        let mut parser = tree_sitter::Parser::new();
11171        parser
11172            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11173            .unwrap();
11174        let tree = parser.parse(source, None).unwrap();
11175        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11176        let broken = recovered
11177            .iter()
11178            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
11179            .expect("the sentinel class must be recovered");
11180        let method_start = source
11181            .find("typename broken<T>::value_type broken<T>::method()")
11182            .expect("outer sibling owner");
11183        assert!(
11184            broken
11185                .owner_ranges
11186                .iter()
11187                .all(|owner| owner.range.start_byte > method_start
11188                    || owner.range.end_byte <= method_start),
11189            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
11190        );
11191    }
11192
11193    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
11194    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
11195        let mut signatures = parsed
11196            .declarations()
11197            .iter()
11198            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
11199            .filter_map(|unit| unit.signature().map(str::to_string))
11200            .collect::<Vec<_>>();
11201        signatures.sort();
11202        signatures.dedup();
11203        signatures
11204    }
11205
11206    #[test]
11207    fn trailing_qualifiers_survive_parameter_list_whitespace() {
11208        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
11209        // declarator's structure, so an out-of-line definition that spells its
11210        // parameter list with different whitespace than the declaration must
11211        // still carry it.
11212        let source = r#"
11213struct Widget {
11214  bool multiline(int settings, int supprs) const;
11215  bool doublespace(int settings, int supprs) const;
11216  bool noexcept_multiline(int settings, int supprs) noexcept;
11217  bool ref_multiline(int settings, int supprs) &&;
11218};
11219bool
11220Widget::multiline (int settings,
11221                   int supprs) const
11222{ return settings + supprs > 0; }
11223bool Widget::doublespace(int settings,  int supprs) const { return true; }
11224bool Widget::noexcept_multiline(int settings,
11225                                int supprs) noexcept { return true; }
11226bool Widget::ref_multiline(int settings,
11227                           int supprs) && { return true; }
11228"#;
11229        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
11230        assert_eq!(
11231            vec!["(int, int) const".to_string()],
11232            identity_signatures(&parsed, "Widget.multiline")
11233        );
11234        assert_eq!(
11235            vec!["(int, int) const".to_string()],
11236            identity_signatures(&parsed, "Widget.doublespace")
11237        );
11238        assert_eq!(
11239            vec!["(int, int) noexcept".to_string()],
11240            identity_signatures(&parsed, "Widget.noexcept_multiline")
11241        );
11242        assert_eq!(
11243            vec!["(int, int) &&".to_string()],
11244            identity_signatures(&parsed, "Widget.ref_multiline")
11245        );
11246    }
11247
11248    #[test]
11249    fn trailing_qualifiers_still_separate_genuine_overloads() {
11250        // The qualifier must keep distinguishing the real C++ overload sets it
11251        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
11252        let source = r#"
11253struct Widget {
11254  int* slot(int index);
11255  const int* slot(int index) const;
11256  int log(int severity) &;
11257  int log(int severity) &&;
11258};
11259"#;
11260        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
11261        assert_eq!(
11262            vec!["(int)".to_string(), "(int) const".to_string()],
11263            identity_signatures(&parsed, "Widget.slot")
11264        );
11265        assert_eq!(
11266            vec!["(int) &".to_string(), "(int) &&".to_string()],
11267            identity_signatures(&parsed, "Widget.log")
11268        );
11269    }
11270
11271    #[test]
11272    fn virtual_specifier_is_not_part_of_the_identity_signature() {
11273        // `override` never appears on the out-of-line definition, and C++ does
11274        // not make it part of the signature, so it must not split the identity.
11275        let source = r#"
11276struct Base {
11277  virtual void run(int value) const;
11278};
11279struct Widget : Base {
11280  void run(int value) const override;
11281};
11282void Widget::run(int value) const {}
11283"#;
11284        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
11285        assert_eq!(
11286            vec!["(int) const".to_string()],
11287            identity_signatures(&parsed, "Widget.run")
11288        );
11289    }
11290
11291    #[test]
11292    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
11293        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
11294        // the function type, so a declaration that spells `const int` and a
11295        // definition that spells `int` are one entity.
11296        let source = r#"
11297struct Widget {
11298  bool value_params(const int settings, const int supprs);
11299  void pointee_const(const int* p);
11300  void pointer_const(int* const p);
11301  void both_const(const int* const p);
11302  void reference_const(const int& p);
11303  void array_const(const int values[4]);
11304};
11305bool Widget::value_params(int settings, int supprs) { return true; }
11306void Widget::pointer_const(int* p) {}
11307void Widget::both_const(const int* p) {}
11308"#;
11309        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
11310        assert_eq!(
11311            vec!["(int, int)".to_string()],
11312            identity_signatures(&parsed, "Widget.value_params")
11313        );
11314        assert_eq!(
11315            vec!["(int *)".to_string()],
11316            identity_signatures(&parsed, "Widget.pointer_const")
11317        );
11318        assert_eq!(
11319            vec!["(const int *)".to_string()],
11320            identity_signatures(&parsed, "Widget.both_const")
11321        );
11322        // The const that is not top-level still distinguishes the type.
11323        assert_eq!(
11324            vec!["(const int *)".to_string()],
11325            identity_signatures(&parsed, "Widget.pointee_const")
11326        );
11327        assert_eq!(
11328            vec!["(const int &)".to_string()],
11329            identity_signatures(&parsed, "Widget.reference_const")
11330        );
11331        assert_eq!(
11332            vec!["(const int [4])".to_string()],
11333            identity_signatures(&parsed, "Widget.array_const")
11334        );
11335    }
11336
11337    #[test]
11338    fn top_level_parameter_const_still_separates_pointee_overloads() {
11339        let source = r#"
11340struct Widget {
11341  void take(const int* p);
11342  void take(int* p);
11343};
11344"#;
11345        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
11346        assert_eq!(
11347            vec!["(const int *)".to_string(), "(int *)".to_string()],
11348            identity_signatures(&parsed, "Widget.take")
11349        );
11350    }
11351}